feat: 初始化项目结构和基本功能
- 创建项目根目录和主要子模块 - 添加基本的 Activity 和布局文件 - 实现简单的导航和电话拨打功能 - 添加相机和图像处理相关代码 - 创建网络请求和数据加密工具类 - 设置 AndroidManifest 文件和权限
This commit is contained in:
76
servicing/src/main/java/com/za/signature/BaseActivity.java
Normal file
76
servicing/src/main/java/com/za/signature/BaseActivity.java
Normal file
@ -0,0 +1,76 @@
|
||||
package com.za.signature;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.util.StatusBarCompat;
|
||||
|
||||
|
||||
/***
|
||||
* Activity基类
|
||||
*
|
||||
* @since 2018-06-25
|
||||
* @author king
|
||||
*/
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
|
||||
protected View actionbar;
|
||||
protected TextView tvCancel;
|
||||
protected TextView tvSave;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(getLayout());
|
||||
initTitleBar();
|
||||
initView();
|
||||
initData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化标题栏
|
||||
*/
|
||||
protected void initTitleBar() {
|
||||
actionbar = findViewById(R.id.actionbar);
|
||||
tvCancel = findViewById(R.id.tv_cancel);
|
||||
tvSave = findViewById(R.id.tv_ok);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取布局
|
||||
*/
|
||||
protected abstract int getLayout();
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
*/
|
||||
protected abstract void initView();
|
||||
|
||||
/**
|
||||
* 初始化数据
|
||||
*/
|
||||
protected abstract void initData();
|
||||
|
||||
/**
|
||||
* 设置主题颜色
|
||||
*
|
||||
* @param color 主题颜色
|
||||
*/
|
||||
protected void setThemeColor(int color) {
|
||||
try {
|
||||
if (actionbar != null) {
|
||||
actionbar.setBackgroundColor(color);
|
||||
}
|
||||
StatusBarCompat.compat(this, color);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
441
servicing/src/main/java/com/za/signature/GridPaintActivity.java
Normal file
441
servicing/src/main/java/com/za/signature/GridPaintActivity.java
Normal file
@ -0,0 +1,441 @@
|
||||
package com.za.signature;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.text.Editable;
|
||||
import android.util.TypedValue;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.config.PenConfig;
|
||||
import com.za.signature.util.BitmapUtil;
|
||||
import com.za.signature.util.DisplayUtil;
|
||||
import com.za.signature.util.SystemUtil;
|
||||
import com.za.signature.view.CircleImageView;
|
||||
import com.za.signature.view.CircleView;
|
||||
import com.za.signature.view.GridDrawable;
|
||||
import com.za.signature.view.GridPaintView;
|
||||
import com.za.signature.view.HVScrollView;
|
||||
import com.za.signature.view.HandWriteEditView;
|
||||
import com.za.signature.view.PaintSettingWindow;
|
||||
|
||||
|
||||
/***
|
||||
* 名称:GridWriteActivity<br>
|
||||
* 描述:米格输入界面
|
||||
* 最近修改时间:
|
||||
* @since 2017/11/14
|
||||
* @author king
|
||||
*/
|
||||
public class GridPaintActivity extends BaseActivity implements View.OnClickListener, Handler.Callback {
|
||||
|
||||
private View mCircleContainer;
|
||||
private HVScrollView mTextContainer;
|
||||
private HandWriteEditView mEditView;
|
||||
private CircleImageView mDeleteView;
|
||||
private CircleImageView mSpaceView;
|
||||
private CircleImageView mClearView;
|
||||
private CircleImageView mEnterView;
|
||||
private CircleView mPenCircleView;
|
||||
private GridPaintView mPaintView;
|
||||
private ProgressDialog mSaveProgressDlg;
|
||||
private static final int MSG_SAVE_SUCCESS = 1;
|
||||
private static final int MSG_SAVE_FAILED = 2;
|
||||
private static final int MSG_WRITE_OK = 100;
|
||||
private Handler mHandler;
|
||||
|
||||
private String mSavePath;
|
||||
private Editable cacheEditable;
|
||||
|
||||
private int bgColor;
|
||||
private boolean isCrop;
|
||||
private String format;
|
||||
private int lineSize;
|
||||
private int fontSize;
|
||||
|
||||
|
||||
private PaintSettingWindow settingWindow;
|
||||
|
||||
@Override
|
||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
bgColor = getIntent().getIntExtra("background", Color.TRANSPARENT);
|
||||
lineSize = getIntent().getIntExtra("lineLength", 15);
|
||||
fontSize = getIntent().getIntExtra("fontSize", 50);
|
||||
isCrop = getIntent().getBooleanExtra("crop", false);
|
||||
format = getIntent().getStringExtra("format");
|
||||
|
||||
PenConfig.PAINT_COLOR = PenConfig.getPaintColor(this);
|
||||
PenConfig.PAINT_SIZE_LEVEL = PenConfig.getPaintTextLevel(this);
|
||||
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
|
||||
super.onCreate(savedInstanceState);
|
||||
SystemUtil.disableShowInput(getApplicationContext(), mEditView);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 横竖屏切换
|
||||
*/
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
setContentView(R.layout.sign_activity_grid_paint);
|
||||
initTitleBar();
|
||||
initView();
|
||||
initData();
|
||||
SystemUtil.disableShowInput(getApplicationContext(), mEditView);
|
||||
|
||||
if (mEditView != null && cacheEditable != null) {
|
||||
mEditView.setText(cacheEditable);
|
||||
mEditView.setSelection(cacheEditable.length());
|
||||
mEditView.requestFocus();
|
||||
}
|
||||
mHandler = new Handler(this);
|
||||
if (settingWindow != null) {
|
||||
settingWindow.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayout() {
|
||||
return R.layout.sign_activity_grid_paint;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
setThemeColor(PenConfig.THEME_COLOR);
|
||||
mPenCircleView.setOutBorderColor(PenConfig.THEME_COLOR);
|
||||
|
||||
mClearView.setEnabled(false);
|
||||
mClearView.setImage(R.mipmap.sign_ic_clear, Color.LTGRAY);
|
||||
mEnterView.setImage(R.mipmap.sign_ic_enter, PenConfig.THEME_COLOR);
|
||||
mSpaceView.setImage(R.mipmap.sign_ic_space, PenConfig.THEME_COLOR);
|
||||
mDeleteView.setImage(R.mipmap.sign_ic_delete, PenConfig.THEME_COLOR);
|
||||
|
||||
mHandler = new Handler(this);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化视图
|
||||
*/
|
||||
@Override
|
||||
protected void initView() {
|
||||
mPaintView = findViewById(R.id.paint_view);
|
||||
mDeleteView = findViewById(R.id.delete);
|
||||
mSpaceView = findViewById(R.id.space);
|
||||
mPenCircleView = findViewById(R.id.pen_color);
|
||||
mClearView = findViewById(R.id.clear);
|
||||
mEnterView = findViewById(R.id.enter);
|
||||
mEditView = findViewById(R.id.et_view);
|
||||
mTextContainer = findViewById(R.id.sv_container);
|
||||
mCircleContainer = findViewById(R.id.circle_container);
|
||||
mEnterView.setOnClickListener(this);
|
||||
mDeleteView.setOnClickListener(this);
|
||||
mSpaceView.setOnClickListener(this);
|
||||
mPenCircleView.setOnClickListener(this);
|
||||
tvCancel.setOnClickListener(this);
|
||||
mClearView.setOnClickListener(this);
|
||||
tvSave.setOnClickListener(this);
|
||||
mPenCircleView.setPaintColor(PenConfig.PAINT_COLOR);
|
||||
mPenCircleView.setRadiusLevel(PenConfig.PAINT_SIZE_LEVEL);
|
||||
|
||||
int size = getResources().getDimensionPixelSize(R.dimen.sign_grid_size);
|
||||
GridDrawable gridDrawable = new GridDrawable(size, size, Color.WHITE);
|
||||
mPaintView.setBackground(gridDrawable);
|
||||
|
||||
mPaintView.setGetTimeListener(new GridPaintView.WriteListener() {
|
||||
@Override
|
||||
public void onWriteStart() {
|
||||
mHandler.removeMessages(MSG_WRITE_OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWriteCompleted(long time) {
|
||||
mHandler.sendEmptyMessageDelayed(MSG_WRITE_OK, 1000);
|
||||
}
|
||||
});
|
||||
|
||||
int maxWidth = lineSize * DisplayUtil.dip2px(this, fontSize);
|
||||
if (!isCrop) {
|
||||
mEditView.setWidth(maxWidth + 2);
|
||||
mEditView.setMaxWidth(maxWidth);
|
||||
} else {
|
||||
mEditView.setWidth(maxWidth * 2 / 3);
|
||||
mEditView.setMaxWidth(maxWidth * 2 / 3);
|
||||
}
|
||||
mEditView.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize);
|
||||
mEditView.setLineHeight(DisplayUtil.dip2px(this, fontSize));
|
||||
mEditView.setHorizontallyScrolling(false);
|
||||
mEditView.requestFocus();
|
||||
if (bgColor != Color.TRANSPARENT) {
|
||||
mTextContainer.setBackgroundColor(bgColor);
|
||||
}
|
||||
mEditView.addTextWatcher(s -> {
|
||||
if (s != null && s.length() > 0) {
|
||||
mClearView.setEnabled(true);
|
||||
mClearView.setImage(R.mipmap.sign_ic_clear, PenConfig.THEME_COLOR);
|
||||
} else {
|
||||
mClearView.setEnabled(false);
|
||||
mClearView.setImage(R.mipmap.sign_ic_clear, Color.LTGRAY);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
mHandler.removeMessages(MSG_WRITE_OK);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
mPaintView.release();
|
||||
super.onDestroy();
|
||||
mHandler.removeMessages(MSG_SAVE_FAILED);
|
||||
mHandler.removeMessages(MSG_SAVE_SUCCESS);
|
||||
}
|
||||
|
||||
|
||||
private void initSaveProgressDlg() {
|
||||
mSaveProgressDlg = new ProgressDialog(this);
|
||||
mSaveProgressDlg.setMessage("正在保存,请稍候...");
|
||||
mSaveProgressDlg.setCancelable(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出清空提示
|
||||
*/
|
||||
private void showClearTips() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("提示")
|
||||
.setMessage("清空文本框内手写内容?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("确定", (dialog, which) -> {
|
||||
mEditView.setText("");
|
||||
mEditView.setSelection(0);
|
||||
cacheEditable = null;
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(@NonNull Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SAVE_FAILED:
|
||||
if (mSaveProgressDlg != null) {
|
||||
mSaveProgressDlg.dismiss();
|
||||
}
|
||||
Toast.makeText(getApplicationContext(), "保存失败", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case MSG_SAVE_SUCCESS:
|
||||
if (mSaveProgressDlg != null) {
|
||||
mSaveProgressDlg.dismiss();
|
||||
}
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(PenConfig.SAVE_PATH, mSavePath);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
break;
|
||||
case MSG_WRITE_OK:
|
||||
if (!mPaintView.isEmpty()) {
|
||||
Bitmap bitmap = mPaintView.buildBitmap(isCrop, DisplayUtil.dip2px(GridPaintActivity.this, fontSize));
|
||||
this.cacheEditable = mEditView.addBitmapToText(bitmap);
|
||||
mPaintView.reset();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
private void save() {
|
||||
if (mEditView.getText() == null || mEditView.getText().length() == 0) {
|
||||
Toast.makeText(getApplicationContext(), "没有写入任何文字", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
//先检查是否有存储权限
|
||||
// if (ContextCompat.checkSelfPermission(this,
|
||||
// Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Toast.makeText(getApplicationContext(), "没有读写存储的权限", Toast.LENGTH_SHORT).show();
|
||||
// return;
|
||||
// }
|
||||
if (mSaveProgressDlg == null) {
|
||||
initSaveProgressDlg();
|
||||
}
|
||||
mEditView.clearFocus();
|
||||
mEditView.setCursorVisible(false);
|
||||
|
||||
mSaveProgressDlg.show();
|
||||
new Thread(() -> {
|
||||
if (PenConfig.FORMAT_JPG.equals(format) && bgColor == Color.TRANSPARENT) {
|
||||
bgColor = Color.WHITE;
|
||||
}
|
||||
Bitmap bm = getWriteBitmap(bgColor);
|
||||
bm = BitmapUtil.clearBlank(bm, 20, bgColor);
|
||||
|
||||
if (bm == null) {
|
||||
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
|
||||
return;
|
||||
}
|
||||
mSavePath = BitmapUtil.saveImage(GridPaintActivity.this, bm, 100, format);
|
||||
if (mSavePath != null) {
|
||||
mHandler.obtainMessage(MSG_SAVE_SUCCESS).sendToTarget();
|
||||
} else {
|
||||
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
|
||||
}
|
||||
bm.recycle();
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取EdiText截图
|
||||
*
|
||||
* @param bgColor 背景颜色
|
||||
* @return EdiText截图
|
||||
*/
|
||||
private Bitmap getWriteBitmap(int bgColor) {
|
||||
int w = 0;
|
||||
int h = 0;
|
||||
for (int i = 0; i < mTextContainer.getChildCount(); i++) {
|
||||
h += mTextContainer.getChildAt(i).getHeight();
|
||||
w += mTextContainer.getChildAt(i).getWidth();
|
||||
}
|
||||
Bitmap bitmap = null;
|
||||
try {
|
||||
bitmap = Bitmap.createBitmap(w, h,
|
||||
Bitmap.Config.ARGB_8888);
|
||||
} catch (OutOfMemoryError e) {
|
||||
bitmap = Bitmap.createBitmap(w, h,
|
||||
Bitmap.Config.ARGB_4444);
|
||||
}
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawColor(bgColor);
|
||||
mTextContainer.draw(canvas);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int i = v.getId();
|
||||
if (i == R.id.delete) {
|
||||
this.cacheEditable = mEditView.deleteBitmapFromText();
|
||||
} else if (i == R.id.tv_cancel) {
|
||||
if (mEditView.getText() != null && mEditView.getText().length() > 0) {
|
||||
showQuitTip();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
} else if (i == R.id.tv_ok) {
|
||||
save();
|
||||
} else if (i == R.id.enter) {
|
||||
Editable editable = mEditView.getText();
|
||||
editable.insert(mEditView.getSelectionStart(), "\n");
|
||||
} else if (i == R.id.space) {
|
||||
mEditView.addSpace(fontSize);
|
||||
} else if (i == R.id.clear) {
|
||||
showClearTips();
|
||||
} else if (i == R.id.pen_color) {
|
||||
showSettingWindow();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 弹出画笔设置
|
||||
*/
|
||||
private void showSettingWindow() {
|
||||
settingWindow = new PaintSettingWindow(this);
|
||||
|
||||
settingWindow.setSettingListener(new PaintSettingWindow.OnSettingListener() {
|
||||
@Override
|
||||
public void onColorSetting(int color) {
|
||||
mPaintView.setPaintColor(color);
|
||||
mPenCircleView.setPaintColor(PenConfig.PAINT_COLOR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeSetting(int level) {
|
||||
mPaintView.setPaintWidth(PaintSettingWindow.PEN_SIZES[level]);
|
||||
mPenCircleView.setRadiusLevel(level);
|
||||
}
|
||||
});
|
||||
|
||||
int[] location = new int[2];
|
||||
mCircleContainer.getLocationOnScreen(location);
|
||||
View contentView = settingWindow.getContentView();
|
||||
//需要先测量,PopupWindow还未弹出时,宽高为0
|
||||
contentView.measure(SystemUtil.makeDropDownMeasureSpec(settingWindow.getWidth()),
|
||||
SystemUtil.makeDropDownMeasureSpec(settingWindow.getHeight()));
|
||||
|
||||
int padding = DisplayUtil.dip2px(this, 10);
|
||||
int offsetX, offsetY;
|
||||
|
||||
Configuration config = getResources().getConfiguration();
|
||||
int smallestScreenWidth = config.smallestScreenWidthDp;
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && smallestScreenWidth >= 720) {
|
||||
//平板上横屏显示
|
||||
settingWindow.popAtBottomRight();
|
||||
settingWindow.showAsDropDown(mCircleContainer, mCircleContainer.getWidth() - settingWindow.getContentView().getMeasuredWidth() - padding, 10);
|
||||
} else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
|
||||
//横屏显示
|
||||
offsetX = -settingWindow.getContentView().getMeasuredWidth() - padding;
|
||||
offsetY = -settingWindow.getContentView().getMeasuredHeight() - mCircleContainer.getHeight() / 2 + padding;
|
||||
settingWindow.popAtLeft();
|
||||
settingWindow.showAsDropDown(mCircleContainer, offsetX, offsetY);
|
||||
} else {
|
||||
//竖屏显示
|
||||
offsetX = 0;
|
||||
offsetY = -(settingWindow.getContentView().getMeasuredHeight() + mPenCircleView.getHeight() + 4 * padding);
|
||||
settingWindow.popAtTopLeft();
|
||||
settingWindow.showAsDropDown(mPenCircleView, offsetX, offsetY);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (mEditView.getText() != null && mEditView.getText().length() > 0) {
|
||||
showQuitTip();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出退出提示
|
||||
*/
|
||||
private void showQuitTip() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("提示")
|
||||
.setMessage("当前文字未保存,是否退出?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("确定", (dialog, which) -> {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
|
||||
|
||||
}
|
430
servicing/src/main/java/com/za/signature/PaintActivity.java
Normal file
430
servicing/src/main/java/com/za/signature/PaintActivity.java
Normal file
@ -0,0 +1,430 @@
|
||||
package com.za.signature;
|
||||
|
||||
import android.Manifest;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.config.PenConfig;
|
||||
import com.za.signature.util.BitmapUtil;
|
||||
import com.za.signature.util.DisplayUtil;
|
||||
import com.za.signature.util.StatusBarCompat;
|
||||
import com.za.signature.util.SystemUtil;
|
||||
import com.za.signature.view.CircleView;
|
||||
import com.za.signature.view.PaintSettingWindow;
|
||||
import com.za.signature.view.PaintView;
|
||||
|
||||
|
||||
/**
|
||||
* 空白手写画板
|
||||
*
|
||||
* @author king
|
||||
* @since 2018/07/10 14:20
|
||||
*/
|
||||
public class PaintActivity extends BaseActivity implements View.OnClickListener, PaintView.StepCallback {
|
||||
|
||||
/**
|
||||
* 画布最大宽度
|
||||
*/
|
||||
public static final int CANVAS_MAX_WIDTH = 3000;
|
||||
/**
|
||||
* 画布最大高度
|
||||
*/
|
||||
public static final int CANVAS_MAX_HEIGHT = 3000;
|
||||
|
||||
private ImageView mHandView; //切换 滚动/手写
|
||||
private ImageView mUndoView;
|
||||
private ImageView mRedoView;
|
||||
private ImageView mPenView;
|
||||
private ImageView mClearView;
|
||||
private CircleView mSettingView;
|
||||
|
||||
private PaintView mPaintView;
|
||||
|
||||
private ProgressDialog mSaveProgressDlg;
|
||||
private static final int MSG_SAVE_SUCCESS = 1;
|
||||
private static final int MSG_SAVE_FAILED = 2;
|
||||
|
||||
private String mSavePath;
|
||||
private boolean hasSize = false;
|
||||
|
||||
private float mWidth;
|
||||
private float mHeight;
|
||||
private float widthRate = 1.0f;
|
||||
private float heightRate = 1.0f;
|
||||
private int bgColor;
|
||||
private boolean isCrop;
|
||||
private String format;
|
||||
|
||||
|
||||
private PaintSettingWindow settingWindow;
|
||||
|
||||
@Override
|
||||
protected int getLayout() {
|
||||
return R.layout.sign_activity_paint;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initView() {
|
||||
|
||||
View mCancelView = findViewById(R.id.tv_cancel);
|
||||
View mOkView = findViewById(R.id.tv_ok);
|
||||
|
||||
mPaintView = findViewById(R.id.paint_view);
|
||||
mHandView = findViewById(R.id.btn_hand);
|
||||
mUndoView = findViewById(R.id.btn_undo);
|
||||
mRedoView = findViewById(R.id.btn_redo);
|
||||
mPenView = findViewById(R.id.btn_pen);
|
||||
mClearView = findViewById(R.id.btn_clear);
|
||||
mSettingView = findViewById(R.id.btn_setting);
|
||||
mUndoView.setOnClickListener(this);
|
||||
mRedoView.setOnClickListener(this);
|
||||
mPenView.setOnClickListener(this);
|
||||
mClearView.setOnClickListener(this);
|
||||
mSettingView.setOnClickListener(this);
|
||||
mHandView.setOnClickListener(this);
|
||||
mCancelView.setOnClickListener(this);
|
||||
mOkView.setOnClickListener(this);
|
||||
|
||||
mPenView.setSelected(true);
|
||||
mUndoView.setEnabled(false);
|
||||
mRedoView.setEnabled(false);
|
||||
mClearView.setEnabled(!mPaintView.isEmpty());
|
||||
|
||||
mPaintView.setBackgroundColor(Color.WHITE);
|
||||
mPaintView.setStepCallback(this);
|
||||
|
||||
PenConfig.PAINT_SIZE_LEVEL = PenConfig.getPaintTextLevel(this);
|
||||
PenConfig.PAINT_COLOR = PenConfig.getPaintColor(this);
|
||||
|
||||
mSettingView.setPaintColor(PenConfig.PAINT_COLOR);
|
||||
mSettingView.setRadiusLevel(PenConfig.PAINT_SIZE_LEVEL);
|
||||
|
||||
setThemeColor(PenConfig.THEME_COLOR);
|
||||
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, PenConfig.THEME_COLOR);
|
||||
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_pen, PenConfig.THEME_COLOR);
|
||||
BitmapUtil.setImage(mRedoView, R.mipmap.sign_ic_redo, mPaintView.canRedo() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
BitmapUtil.setImage(mUndoView, R.mipmap.sign_ic_undo, mPaintView.canUndo() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, !mPaintView.isEmpty() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
// mSettingView.setOutBorderColor(PenConfig.THEME_COLOR);
|
||||
BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_hand, PenConfig.THEME_COLOR);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取画布默认宽度
|
||||
*
|
||||
*/
|
||||
private int getResizeWidth() {
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && dm.widthPixels < dm.heightPixels) {
|
||||
return (int) (dm.heightPixels * widthRate);
|
||||
}
|
||||
return (int) (dm.widthPixels * widthRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取画布默认高度
|
||||
*
|
||||
*/
|
||||
private int getResizeHeight() {
|
||||
int toolBarHeight = getResources().getDimensionPixelSize(R.dimen.sign_grid_toolbar_height);
|
||||
int actionbarHeight = getResources().getDimensionPixelSize(R.dimen.sign_actionbar_height);
|
||||
int statusBarHeight = StatusBarCompat.getStatusBarHeight(this);
|
||||
int otherHeight = toolBarHeight + actionbarHeight + statusBarHeight;
|
||||
DisplayMetrics dm = new DisplayMetrics();
|
||||
getWindowManager().getDefaultDisplay().getMetrics(dm);
|
||||
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE && dm.widthPixels < dm.heightPixels) {
|
||||
return (int) ((dm.widthPixels - otherHeight) * heightRate);
|
||||
}
|
||||
return (int) ((dm.heightPixels - otherHeight) * heightRate);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initData() {
|
||||
isCrop = getIntent().getBooleanExtra("crop", false);
|
||||
format = getIntent().getStringExtra("format");
|
||||
bgColor = getIntent().getIntExtra("background", Color.TRANSPARENT);
|
||||
String mInitPath = getIntent().getStringExtra("image");
|
||||
float bitmapWidth = getIntent().getFloatExtra("width", 1.0f);
|
||||
float bitmapHeight = getIntent().getFloatExtra("height", 1.0f);
|
||||
|
||||
if (bitmapWidth > 0 && bitmapWidth <= 1.0f) {
|
||||
widthRate = bitmapWidth;
|
||||
mWidth = getResizeWidth();
|
||||
} else {
|
||||
hasSize = true;
|
||||
mWidth = bitmapWidth;
|
||||
}
|
||||
if (bitmapHeight > 0 && bitmapHeight <= 1.0f) {
|
||||
heightRate = bitmapHeight;
|
||||
mHeight = getResizeHeight();
|
||||
} else {
|
||||
hasSize = true;
|
||||
mHeight = bitmapHeight;
|
||||
}
|
||||
if (mWidth > CANVAS_MAX_WIDTH) {
|
||||
Toast.makeText(getApplicationContext(), "画板宽度已超过" + CANVAS_MAX_WIDTH, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
if (mHeight > CANVAS_MAX_WIDTH) {
|
||||
Toast.makeText(getApplicationContext(), "画板高度已超过" + CANVAS_MAX_WIDTH, Toast.LENGTH_LONG).show();
|
||||
finish();
|
||||
return;
|
||||
}
|
||||
//初始画板设置
|
||||
if (!hasSize && !TextUtils.isEmpty(mInitPath)) {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(mInitPath);
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
hasSize = true;
|
||||
if (mWidth > CANVAS_MAX_WIDTH || mHeight > CANVAS_MAX_HEIGHT) {
|
||||
bitmap = BitmapUtil.zoomImg(bitmap, CANVAS_MAX_WIDTH, CANVAS_MAX_WIDTH);
|
||||
mWidth = bitmap.getWidth();
|
||||
mHeight = bitmap.getHeight();
|
||||
}
|
||||
}
|
||||
mPaintView.init((int) mWidth, (int) mHeight, mInitPath);
|
||||
if (bgColor != Color.TRANSPARENT) {
|
||||
mPaintView.setBackgroundColor(bgColor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 横竖屏切换
|
||||
*/
|
||||
@Override
|
||||
public void onConfigurationChanged(@NonNull Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
if (settingWindow != null) {
|
||||
settingWindow.dismiss();
|
||||
}
|
||||
|
||||
int resizeWidth = getResizeWidth();
|
||||
int resizeHeight = getResizeHeight();
|
||||
if (mPaintView != null && !hasSize) {
|
||||
mPaintView.resize(mPaintView.getLastBitmap(), resizeWidth, resizeHeight);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
int i = v.getId();
|
||||
if (i == R.id.btn_setting) {
|
||||
showPaintSettingWindow();
|
||||
|
||||
} else if (i == R.id.btn_hand) {
|
||||
//切换是否允许写字
|
||||
mPaintView.setFingerEnable(!mPaintView.isFingerEnable());
|
||||
if (mPaintView.isFingerEnable()) {
|
||||
BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_hand, PenConfig.THEME_COLOR);
|
||||
} else {
|
||||
BitmapUtil.setImage(mHandView, R.mipmap.sign_ic_drag, PenConfig.THEME_COLOR);
|
||||
}
|
||||
|
||||
} else if (i == R.id.btn_clear) {
|
||||
mPaintView.reset();
|
||||
|
||||
} else if (i == R.id.btn_undo) {
|
||||
mPaintView.undo();
|
||||
|
||||
} else if (i == R.id.btn_redo) {
|
||||
mPaintView.redo();
|
||||
|
||||
} else if (i == R.id.btn_pen) {
|
||||
if (!mPaintView.isEraser()) {
|
||||
mPaintView.setPenType(PaintView.TYPE_ERASER);
|
||||
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_eraser, PenConfig.THEME_COLOR);
|
||||
} else {
|
||||
mPaintView.setPenType(PaintView.TYPE_PEN);
|
||||
BitmapUtil.setImage(mPenView, R.mipmap.sign_ic_pen, PenConfig.THEME_COLOR);
|
||||
}
|
||||
} else if (i == R.id.tv_ok) {
|
||||
save();
|
||||
|
||||
} else if (i == R.id.tv_cancel) {
|
||||
if (!mPaintView.isEmpty()) {
|
||||
showQuitTip();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
if (mPaintView != null) {
|
||||
mPaintView.release();
|
||||
}
|
||||
if (mHandler != null) {
|
||||
mHandler.removeCallbacksAndMessages(null);
|
||||
}
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 弹出画笔设置
|
||||
*/
|
||||
private void showPaintSettingWindow() {
|
||||
settingWindow = new PaintSettingWindow(this);
|
||||
settingWindow.setSettingListener(new PaintSettingWindow.OnSettingListener() {
|
||||
@Override
|
||||
public void onColorSetting(int color) {
|
||||
mPaintView.setPaintColor(color);
|
||||
mSettingView.setPaintColor(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSizeSetting(int index) {
|
||||
mSettingView.setRadiusLevel(index);
|
||||
mPaintView.setPaintWidth(PaintSettingWindow.PEN_SIZES[index]);
|
||||
}
|
||||
});
|
||||
|
||||
View contentView = settingWindow.getContentView();
|
||||
//需要先测量,PopupWindow还未弹出时,宽高为0
|
||||
contentView.measure(SystemUtil.makeDropDownMeasureSpec(settingWindow.getWidth()),
|
||||
SystemUtil.makeDropDownMeasureSpec(settingWindow.getHeight()));
|
||||
|
||||
int padding = DisplayUtil.dip2px(this, 45);
|
||||
settingWindow.popAtTopRight();
|
||||
settingWindow.showAsDropDown(mSettingView, -250, -2 * padding - settingWindow.getContentView().getMeasuredHeight());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private void initSaveProgressDlg() {
|
||||
mSaveProgressDlg = new ProgressDialog(this);
|
||||
mSaveProgressDlg.setMessage("正在保存,请稍候...");
|
||||
mSaveProgressDlg.setCancelable(false);
|
||||
}
|
||||
|
||||
@SuppressLint("HandlerLeak")
|
||||
private Handler mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_SAVE_FAILED:
|
||||
mSaveProgressDlg.dismiss();
|
||||
Toast.makeText(getApplicationContext(), "保存失败", Toast.LENGTH_SHORT).show();
|
||||
break;
|
||||
case MSG_SAVE_SUCCESS:
|
||||
mSaveProgressDlg.dismiss();
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra(PenConfig.SAVE_PATH, mSavePath);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 保存
|
||||
*/
|
||||
private void save() {
|
||||
if (mPaintView.isEmpty()) {
|
||||
Toast.makeText(getApplicationContext(), "没有写入任何文字", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
//先检查是否有存储权限
|
||||
// if (ContextCompat.checkSelfPermission(this,
|
||||
// Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
// Toast.makeText(getApplicationContext(), "没有读写存储的权限", Toast.LENGTH_SHORT).show();
|
||||
// return;
|
||||
// }
|
||||
if (mSaveProgressDlg == null) {
|
||||
initSaveProgressDlg();
|
||||
}
|
||||
mSaveProgressDlg.show();
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Bitmap result = mPaintView.buildAreaBitmap(isCrop);
|
||||
if (PenConfig.FORMAT_JPG.equals(format) && bgColor == Color.TRANSPARENT) {
|
||||
bgColor = Color.WHITE;
|
||||
}
|
||||
if (bgColor != Color.TRANSPARENT) {
|
||||
result = BitmapUtil.drawBgToBitmap(result, bgColor);
|
||||
}
|
||||
if (result == null) {
|
||||
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
|
||||
return;
|
||||
}
|
||||
mSavePath = BitmapUtil.saveImage(PaintActivity.this, result, 100, format);
|
||||
if (mSavePath != null) {
|
||||
mHandler.obtainMessage(MSG_SAVE_SUCCESS).sendToTarget();
|
||||
} else {
|
||||
mHandler.obtainMessage(MSG_SAVE_FAILED).sendToTarget();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
}).start();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 画布有操作
|
||||
*/
|
||||
@Override
|
||||
public void onOperateStatusChanged() {
|
||||
mUndoView.setEnabled(mPaintView.canUndo());
|
||||
mRedoView.setEnabled(mPaintView.canRedo());
|
||||
mClearView.setEnabled(!mPaintView.isEmpty());
|
||||
|
||||
BitmapUtil.setImage(mRedoView, R.mipmap.sign_ic_redo, mPaintView.canRedo() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
BitmapUtil.setImage(mUndoView, R.mipmap.sign_ic_undo, mPaintView.canUndo() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
BitmapUtil.setImage(mClearView, R.mipmap.sign_ic_clear, !mPaintView.isEmpty() ? PenConfig.THEME_COLOR : Color.LTGRAY);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!mPaintView.isEmpty()) {
|
||||
showQuitTip();
|
||||
} else {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 弹出退出提示
|
||||
*/
|
||||
private void showQuitTip() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle("提示")
|
||||
.setMessage("当前文字未保存,是否退出?")
|
||||
.setNegativeButton("取消", null)
|
||||
.setPositiveButton("确定", (dialog, which) -> {
|
||||
setResult(RESULT_CANCELED);
|
||||
finish();
|
||||
});
|
||||
builder.show();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package com.za.signature.config;
|
||||
|
||||
|
||||
/***
|
||||
* 每个点的控制,关心三个因素:笔的宽度,坐标,透明数值
|
||||
*
|
||||
* @since 2018/06/15
|
||||
* @author king
|
||||
*/
|
||||
public class ControllerPoint {
|
||||
public float x;
|
||||
public float y;
|
||||
|
||||
public float width;
|
||||
public int alpha = 255;
|
||||
|
||||
public ControllerPoint() {
|
||||
}
|
||||
|
||||
public ControllerPoint(float x, float y) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
|
||||
public void set(float x, float y, float w) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.width = w;
|
||||
}
|
||||
|
||||
|
||||
public void set(ControllerPoint point) {
|
||||
this.x = point.x;
|
||||
this.y = point.y;
|
||||
this.width = point.width;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package com.za.signature.config;
|
||||
|
||||
/***
|
||||
* 触摸点信息
|
||||
*
|
||||
* @since 2018/06/15
|
||||
* @author king
|
||||
*/
|
||||
public class MotionElement {
|
||||
|
||||
public float x;
|
||||
public float y;
|
||||
/**
|
||||
* 压力值 物理设备决定的,和设计的设备有关系
|
||||
*/
|
||||
public float pressure;
|
||||
/**
|
||||
* 绘制的工具是否是手指或者是笔(触摸笔)
|
||||
*/
|
||||
public int toolType;
|
||||
|
||||
public MotionElement(float mx, float my, float mp, int ttype) {
|
||||
x = mx;
|
||||
y = my;
|
||||
pressure = mp;
|
||||
toolType = ttype;
|
||||
}
|
||||
|
||||
|
||||
}
|
103
servicing/src/main/java/com/za/signature/config/PenConfig.java
Normal file
103
servicing/src/main/java/com/za/signature/config/PenConfig.java
Normal file
@ -0,0 +1,103 @@
|
||||
package com.za.signature.config;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Color;
|
||||
|
||||
import com.za.signature.view.PaintSettingWindow;
|
||||
|
||||
/**
|
||||
* 画笔配置
|
||||
*
|
||||
* @author king
|
||||
* @since 2018/06/15
|
||||
*/
|
||||
public class PenConfig {
|
||||
|
||||
/**
|
||||
* 画笔大小等级
|
||||
*/
|
||||
public static int PAINT_SIZE_LEVEL = 2;
|
||||
|
||||
/**
|
||||
* 画笔颜色
|
||||
*/
|
||||
public static int PAINT_COLOR = Color.parseColor(PaintSettingWindow.PEN_COLORS[0]);
|
||||
|
||||
/**
|
||||
* 笔锋控制值,越小笔锋越粗,越不明显
|
||||
*/
|
||||
public static final float DIS_VEL_CAL_FACTOR = 0.008f;
|
||||
|
||||
|
||||
/**
|
||||
* 主题颜色
|
||||
*/
|
||||
public static int THEME_COLOR = Color.parseColor("#0c53ab");
|
||||
|
||||
public static final String SAVE_PATH = "path";
|
||||
private static final String SP_SETTING = "sp_sign_setting";
|
||||
|
||||
/**
|
||||
* jpg格式
|
||||
*/
|
||||
public static final String FORMAT_JPG = "JPG";
|
||||
/**
|
||||
* png格式
|
||||
*/
|
||||
public static final String FORMAT_PNG = "PNG";
|
||||
|
||||
/**
|
||||
* 保存画笔颜色设置
|
||||
*/
|
||||
public static void setPaintColor(Context context, int color) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SP_SETTING, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putInt("color", color);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取画笔颜色
|
||||
*/
|
||||
public static int getPaintColor(Context context) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SP_SETTING, Context.MODE_PRIVATE);
|
||||
return sp.getInt("color", PAINT_COLOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存画笔大小level
|
||||
*/
|
||||
public static void savePaintTextLevel(Context context, int size) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SP_SETTING, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putInt("sizeLevel", size);
|
||||
editor.apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* 从sp文件中获取选中画笔大小level
|
||||
*/
|
||||
public static int getPaintTextLevel(Context context) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SP_SETTING, Context.MODE_PRIVATE);
|
||||
return sp.getInt("sizeLevel", PAINT_SIZE_LEVEL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取是否第一次打开
|
||||
*/
|
||||
public static boolean getFirst(Context context) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SP_SETTING, Context.MODE_PRIVATE);
|
||||
return sp.getBoolean("isFirst", true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否第一次打开
|
||||
*/
|
||||
public static void setFirst(Context context, boolean isFirst) {
|
||||
SharedPreferences sp = context.getSharedPreferences(SP_SETTING, Context.MODE_PRIVATE);
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean("isFirst", isFirst);
|
||||
editor.apply();
|
||||
}
|
||||
}
|
240
servicing/src/main/java/com/za/signature/pen/BasePen.java
Normal file
240
servicing/src/main/java/com/za/signature/pen/BasePen.java
Normal file
@ -0,0 +1,240 @@
|
||||
package com.za.signature.pen;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.za.signature.config.ControllerPoint;
|
||||
import com.za.signature.config.MotionElement;
|
||||
import com.za.signature.config.PenConfig;
|
||||
import com.za.signature.util.Bezier;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* 画笔基类
|
||||
*
|
||||
* @author king
|
||||
* @since 2018/06/15
|
||||
*/
|
||||
public abstract class BasePen {
|
||||
/**
|
||||
* 绘制计算的次数,数值越小计算的次数越多
|
||||
*/
|
||||
public static final int STEP_FACTOR = 20;
|
||||
|
||||
protected ArrayList<ControllerPoint> mHWPointList = new ArrayList<>();
|
||||
protected ControllerPoint mLastPoint = new ControllerPoint(0, 0);
|
||||
protected Paint mPaint;
|
||||
|
||||
/**
|
||||
* 笔的宽度信息
|
||||
*/
|
||||
private double mBaseWidth;
|
||||
|
||||
private double mLastVel;
|
||||
private double mLastWidth;
|
||||
|
||||
protected Bezier mBezier = new Bezier();
|
||||
|
||||
protected ControllerPoint mCurPoint;
|
||||
|
||||
public void setPaint(Paint paint) {
|
||||
mPaint = paint;
|
||||
mBaseWidth = paint.getStrokeWidth();
|
||||
}
|
||||
|
||||
public void draw(Canvas canvas) {
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
//点的集合少 不去绘制
|
||||
if (mHWPointList == null || mHWPointList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
mCurPoint = mHWPointList.get(0);
|
||||
doPreDraw(canvas);
|
||||
}
|
||||
|
||||
private int lastId = 0;//记录最先/最后的手指id
|
||||
|
||||
public void onTouchEvent(MotionEvent event, Canvas canvas) {
|
||||
// event会被下一次事件重用,这里必须生成新的,否则会有问题
|
||||
int action = event.getAction() & event.getActionMasked();
|
||||
MotionEvent event2 = MotionEvent.obtain(event);
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
lastId = event2.getPointerId(0);
|
||||
onDown(createMotionElement(event2), canvas);
|
||||
return;
|
||||
case MotionEvent.ACTION_POINTER_DOWN:
|
||||
lastId = 0;
|
||||
mLastVel = 0;
|
||||
mLastPoint = new ControllerPoint(event2.getX(event2.getActionIndex()), event2.getY(event2.getActionIndex()));
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
if (lastId != event2.getPointerId(event2.getActionIndex())) {
|
||||
return;
|
||||
}
|
||||
onMove(createMotionElement(event2), canvas);
|
||||
return;
|
||||
case MotionEvent.ACTION_POINTER_UP:
|
||||
onUp(createMotionElement(event2), canvas);
|
||||
return;
|
||||
case MotionEvent.ACTION_UP:
|
||||
lastId = event2.getPointerId(0);
|
||||
onUp(createMotionElement(event2), canvas);
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 按下的事件
|
||||
*/
|
||||
public void onDown(MotionElement mElement, Canvas canvas) {
|
||||
if (mPaint == null) {
|
||||
throw new NullPointerException("paint不能为null");
|
||||
}
|
||||
if (getNewPaint(mPaint) != null) {
|
||||
Paint paint = getNewPaint(mPaint);
|
||||
mPaint = paint;
|
||||
paint = null;
|
||||
}
|
||||
mHWPointList.clear();
|
||||
//记录down的控制点的信息
|
||||
ControllerPoint curPoint = new ControllerPoint(mElement.x, mElement.y);
|
||||
mLastWidth = 0.7 * mBaseWidth;
|
||||
//down下的点的宽度
|
||||
curPoint.width = (float) mLastWidth;
|
||||
mLastVel = 0;
|
||||
//记录当前的点
|
||||
mLastPoint = curPoint;
|
||||
}
|
||||
|
||||
protected Paint getNewPaint(Paint paint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手指移动的事件
|
||||
*/
|
||||
public void onMove(MotionElement mElement, Canvas canvas) {
|
||||
ControllerPoint curPoint = new ControllerPoint(mElement.x, mElement.y);
|
||||
double deltaX = curPoint.x - mLastPoint.x;
|
||||
double deltaY = curPoint.y - mLastPoint.y;
|
||||
//deltaX和deltay平方和的二次方根 想象一个例子 1+1的平方根为1.4 (x²+y²)开根号
|
||||
//同理,当滑动的越快的话,deltaX+deltaY的值越大,这个越大的话,curDis也越大
|
||||
double curDis = Math.hypot(deltaX, deltaY);
|
||||
//我们求出的这个值越小,画的点或者是绘制椭圆形越多,这个值越大的话,绘制的越少,笔就越细,宽度越小
|
||||
double curVel = curDis * PenConfig.DIS_VEL_CAL_FACTOR;
|
||||
double curWidth;
|
||||
//点的集合少,我们得必须改变宽度,每次点击的down的时候,这个事件
|
||||
if (mHWPointList.size() < 2) {
|
||||
|
||||
curWidth = calcNewWidth(curVel, mLastVel, curDis, 1.7,
|
||||
mLastWidth);
|
||||
curPoint.width = (float) curWidth;
|
||||
mBezier.init(mLastPoint, curPoint);
|
||||
} else {
|
||||
mLastVel = curVel;
|
||||
curWidth = calcNewWidth(curVel, mLastVel, curDis, 1.7,
|
||||
mLastWidth);
|
||||
curPoint.width = (float) curWidth;
|
||||
mBezier.addNode(curPoint);
|
||||
}
|
||||
//每次移动的话,这里赋值新的值
|
||||
mLastWidth = curWidth;
|
||||
doMove(curDis);
|
||||
mLastPoint = curPoint;
|
||||
}
|
||||
|
||||
/**
|
||||
* 手指抬起来的事件
|
||||
*/
|
||||
public void onUp(MotionElement mElement, Canvas canvas) {
|
||||
if (mHWPointList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
mCurPoint = new ControllerPoint(mElement.x, mElement.y);
|
||||
double deltaX = mCurPoint.x - mLastPoint.x;
|
||||
double deltaY = mCurPoint.y - mLastPoint.y;
|
||||
double curDis = Math.hypot(deltaX, deltaY);
|
||||
mCurPoint.width = 0;
|
||||
|
||||
mBezier.addNode(mCurPoint);
|
||||
|
||||
int steps = 1 + (int) curDis / STEP_FACTOR;
|
||||
double step = 1.0 / steps;
|
||||
for (double t = 0; t < 1.0; t += step) {
|
||||
ControllerPoint point = mBezier.getPoint(t);
|
||||
mHWPointList.add(point);
|
||||
}
|
||||
mBezier.end();
|
||||
for (double t = 0; t < 1.0; t += step) {
|
||||
ControllerPoint point = mBezier.getPoint(t);
|
||||
mHWPointList.add(point);
|
||||
}
|
||||
draw(canvas);
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算新的宽度信息
|
||||
*/
|
||||
public double calcNewWidth(double curVel, double lastVel, double curDis,
|
||||
double factor, double lastWidth) {
|
||||
double calVel = curVel * 0.6 + lastVel * (1 - 0.6);
|
||||
double vfac = Math.log(factor * 2.0f) * (-calVel);
|
||||
return mBaseWidth * Math.exp(vfac);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建触摸点信息
|
||||
*/
|
||||
public MotionElement createMotionElement(MotionEvent motionEvent) {
|
||||
return new MotionElement(motionEvent.getX(0), motionEvent.getY(0),
|
||||
motionEvent.getPressure(), motionEvent.getToolType(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除缓存的触摸点
|
||||
*/
|
||||
public void clear() {
|
||||
mHWPointList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制
|
||||
* 当现在的点和触摸点的位置在一起的时候不用去绘制
|
||||
*/
|
||||
protected void drawToPoint(Canvas canvas, ControllerPoint point, Paint paint) {
|
||||
if ((mCurPoint.x == point.x) && (mCurPoint.y == point.y)) {
|
||||
return;
|
||||
}
|
||||
doDraw(canvas, point, paint);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断笔是否为空
|
||||
*/
|
||||
public boolean isNullPaint() {
|
||||
return mPaint == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 移动的时候的处理方法
|
||||
*/
|
||||
protected abstract void doMove(double f);
|
||||
|
||||
/**
|
||||
* 绘制方法
|
||||
*/
|
||||
protected abstract void doDraw(Canvas canvas, ControllerPoint point, Paint paint);
|
||||
|
||||
/**
|
||||
* onDraw之前的操作
|
||||
*/
|
||||
protected abstract void doPreDraw(Canvas canvas);
|
||||
}
|
77
servicing/src/main/java/com/za/signature/pen/Eraser.java
Normal file
77
servicing/src/main/java/com/za/signature/pen/Eraser.java
Normal file
@ -0,0 +1,77 @@
|
||||
package com.za.signature.pen;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffXfermode;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
/***
|
||||
* 名称:Eraser<br>
|
||||
* 描述:橡皮擦
|
||||
* 最近修改时间:
|
||||
* @since 2018/5/7
|
||||
* @author king
|
||||
*/
|
||||
public class Eraser {
|
||||
|
||||
private final Paint eraserPaint;
|
||||
|
||||
/**
|
||||
* 画笔路径
|
||||
*/
|
||||
private Path mPath;
|
||||
/**
|
||||
* 记录上一个点的坐标
|
||||
*/
|
||||
private float mLastX;
|
||||
private float mLastY;
|
||||
|
||||
public Eraser(int paintWidth) {
|
||||
eraserPaint = new Paint();
|
||||
eraserPaint.setStyle(Paint.Style.STROKE);
|
||||
eraserPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||
eraserPaint.setStrokeCap(Paint.Cap.ROUND);
|
||||
eraserPaint.setStrokeWidth(paintWidth);
|
||||
eraserPaint.setFilterBitmap(true);
|
||||
|
||||
eraserPaint.setColor(Color.WHITE);
|
||||
eraserPaint.setDither(true);
|
||||
eraserPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
|
||||
eraserPaint.setAntiAlias(true);
|
||||
|
||||
mPath = new Path();
|
||||
}
|
||||
|
||||
// public void setPaintWidth(int width) {
|
||||
// eraserPaint.setStrokeWidth(width);
|
||||
// }
|
||||
|
||||
public void handleEraserEvent(MotionEvent event, Canvas canvas) {
|
||||
final float x = event.getX();
|
||||
final float y = event.getY();
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
|
||||
mLastX = x;
|
||||
mLastY = y;
|
||||
mPath.reset();
|
||||
mPath.moveTo(x, y);
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
mPath.quadTo(mLastX, mLastY, (x + mLastX) / 2, (y + mLastY) / 2);
|
||||
canvas.drawPath(mPath, eraserPaint);
|
||||
mLastX = x;
|
||||
mLastY = y;
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
mPath.lineTo(mLastX, mLastY);
|
||||
canvas.drawPath(mPath, eraserPaint);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
79
servicing/src/main/java/com/za/signature/pen/SteelPen.java
Normal file
79
servicing/src/main/java/com/za/signature/pen/SteelPen.java
Normal file
@ -0,0 +1,79 @@
|
||||
package com.za.signature.pen;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
|
||||
import com.za.signature.config.ControllerPoint;
|
||||
|
||||
|
||||
/**
|
||||
* 钢笔
|
||||
*
|
||||
* @since 2018/06/15
|
||||
* @author king
|
||||
*/
|
||||
public class SteelPen extends BasePen {
|
||||
|
||||
@Override
|
||||
protected void doPreDraw(Canvas canvas) {
|
||||
for (int i = 1; i < mHWPointList.size(); i++) {
|
||||
ControllerPoint point = mHWPointList.get(i);
|
||||
drawToPoint(canvas, point, mPaint);
|
||||
mCurPoint = point;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doMove(double curDis) {
|
||||
int steps = 1 + (int) curDis / STEP_FACTOR;
|
||||
double step = 1.0 / steps;
|
||||
for (double t = 0; t < 1.0; t += step) {
|
||||
ControllerPoint point = mBezier.getPoint(t);
|
||||
mHWPointList.add(point);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doDraw(Canvas canvas, ControllerPoint point, Paint paint) {
|
||||
drawLine(canvas, mCurPoint.x, mCurPoint.y, mCurPoint.width, point.x,
|
||||
point.y, point.width, paint);
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制方法,实现笔锋效果
|
||||
*/
|
||||
private void drawLine(Canvas canvas, double x0, double y0, double w0, double x1, double y1, double w1, Paint paint) {
|
||||
//求两个数字的平方根 x的平方+y的平方在开方记得X的平方+y的平方=1,这就是一个园
|
||||
double curDis = Math.hypot(x0 - x1, y0 - y1);
|
||||
int steps;
|
||||
//绘制的笔的宽度是多少,绘制多少个椭圆
|
||||
if (paint.getStrokeWidth() < 6) {
|
||||
steps = 1 + (int) (curDis / 2);
|
||||
} else if (paint.getStrokeWidth() > 60) {
|
||||
steps = 1 + (int) (curDis / 4);
|
||||
} else {
|
||||
steps = 1 + (int) (curDis / 3);
|
||||
}
|
||||
double deltaX = (x1 - x0) / steps;
|
||||
double deltaY = (y1 - y0) / steps;
|
||||
double deltaW = (w1 - w0) / steps;
|
||||
double x = x0;
|
||||
double y = y0;
|
||||
double w = w0;
|
||||
|
||||
for (int i = 0; i < steps; i++) {
|
||||
RectF oval = new RectF();
|
||||
float top = (float) (y - w / 2.0f);
|
||||
float left = (float) (x - w / 4.0f);
|
||||
float right = (float) (x + w / 4.0f);
|
||||
float bottom = (float) (y + w / 2.0f);
|
||||
oval.set(left, top, right, bottom);
|
||||
//最基本的实现,通过点控制线,绘制椭圆
|
||||
canvas.drawOval(oval, paint);
|
||||
x += deltaX;
|
||||
y += deltaY;
|
||||
w += deltaW;
|
||||
}
|
||||
}
|
||||
}
|
135
servicing/src/main/java/com/za/signature/util/Bezier.java
Normal file
135
servicing/src/main/java/com/za/signature/util/Bezier.java
Normal file
@ -0,0 +1,135 @@
|
||||
package com.za.signature.util;
|
||||
|
||||
|
||||
import com.za.signature.config.ControllerPoint;
|
||||
|
||||
/**
|
||||
* 贝塞尔操作工具类,对点的位置和宽度控制的bezier曲线,主要是两个点,都包含了宽度和点的坐标
|
||||
*
|
||||
* @since 2018/06/15
|
||||
* @author king
|
||||
*/
|
||||
public class Bezier {
|
||||
/**
|
||||
* 控制点
|
||||
*/
|
||||
private final ControllerPoint mControl = new ControllerPoint();
|
||||
/**
|
||||
* 距离
|
||||
*/
|
||||
private final ControllerPoint mDestination = new ControllerPoint();
|
||||
/**
|
||||
* 下一个需要控制点
|
||||
*/
|
||||
private final ControllerPoint mNextControl = new ControllerPoint();
|
||||
/**
|
||||
* 资源的点
|
||||
*/
|
||||
private final ControllerPoint mSource = new ControllerPoint();
|
||||
|
||||
/**
|
||||
* 初始化两个点,
|
||||
*
|
||||
* @param last 最后的点的信息
|
||||
* @param cur 当前点的信息,当前点的信息,当前点的是根据事件获得,同时这个当前点的宽度是经过计算的得出的
|
||||
*/
|
||||
public void init(ControllerPoint last, ControllerPoint cur) {
|
||||
init(last.x, last.y, last.width, cur.x, cur.y, cur.width);
|
||||
}
|
||||
|
||||
public void init(float lastx, float lasty, float lastWidth, float x, float y, float width) {
|
||||
//资源点设置,最后的点的为资源点
|
||||
mSource.set(lastx, lasty, lastWidth);
|
||||
float xmid = getMid(lastx, x);
|
||||
float ymid = getMid(lasty, y);
|
||||
float wmid = getMid(lastWidth, width);
|
||||
//距离点为平均点
|
||||
mDestination.set(xmid, ymid, wmid);
|
||||
//控制点为当前的距离点
|
||||
mControl.set(getMid(lastx, xmid), getMid(lasty, ymid), getMid(lastWidth, wmid));
|
||||
//下个控制点为当前点
|
||||
mNextControl.set(x, y, width);
|
||||
}
|
||||
|
||||
public void addNode(ControllerPoint cur) {
|
||||
addNode(cur.x, cur.y, cur.width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换就的点,原来的距离点变换为资源点,控制点变为原来的下一个控制点,距离点取原来控制点的和新的的一半
|
||||
* 下个控制点为新的点
|
||||
*
|
||||
* @param x 新的点的坐标
|
||||
* @param y 新的点的坐标
|
||||
* @param width
|
||||
*/
|
||||
public void addNode(float x, float y, float width) {
|
||||
mSource.set(mDestination);
|
||||
mControl.set(mNextControl);
|
||||
mDestination.set(getMid(mNextControl.x, x), getMid(mNextControl.y, y), getMid(mNextControl.width, width));
|
||||
mNextControl.set(x, y, width);
|
||||
}
|
||||
|
||||
/**
|
||||
* 结合手指抬起来的动作,告诉现在的曲线控制点也必须变化,其实在这里也不需要结合着up事件使用
|
||||
* 因为在down的事件中,所有点都会被重置,然后设置这个没有多少意义,但是可以改变下个事件的朝向改变
|
||||
* 先留着,因为后面如果需要控制整个颜色的改变的话,我的依靠这个方法,还有按压的时间的变化
|
||||
*/
|
||||
public void end() {
|
||||
mSource.set(mDestination);
|
||||
float x = getMid(mNextControl.x, mSource.x);
|
||||
float y = getMid(mNextControl.y, mSource.y);
|
||||
float w = getMid(mNextControl.width, mSource.width);
|
||||
mControl.set(x, y, w);
|
||||
mDestination.set(mNextControl);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取点信息
|
||||
*/
|
||||
public ControllerPoint getPoint(double t) {
|
||||
float x = (float) getX(t);
|
||||
float y = (float) getY(t);
|
||||
float w = (float) getW(t);
|
||||
ControllerPoint point = new ControllerPoint();
|
||||
point.set(x, y, w);
|
||||
return point;
|
||||
}
|
||||
|
||||
/**
|
||||
* 三阶曲线的控制点
|
||||
*/
|
||||
private double getValue(double p0, double p1, double p2, double t) {
|
||||
double a = p2 - 2 * p1 + p0;
|
||||
double b = 2 * (p1 - p0);
|
||||
double c = p0;
|
||||
return a * t * t + b * t + c;
|
||||
}
|
||||
|
||||
private double getX(double t) {
|
||||
return getValue(mSource.x, mControl.x, mDestination.x, t);
|
||||
}
|
||||
|
||||
private double getY(double t) {
|
||||
return getValue(mSource.y, mControl.y, mDestination.y, t);
|
||||
}
|
||||
|
||||
private double getW(double t) {
|
||||
return getWidth(mSource.width, mDestination.width, t);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取两个数平均值
|
||||
*
|
||||
* @param x1 一个点的x
|
||||
* @param x2 一个点的x
|
||||
*/
|
||||
private float getMid(float x1, float x2) {
|
||||
return (float) ((x1 + x2) / 2.0);
|
||||
}
|
||||
|
||||
private double getWidth(double w0, double w1, double t) {
|
||||
return w0 + (w1 - w0) * t;
|
||||
}
|
||||
|
||||
}
|
394
servicing/src/main/java/com/za/signature/util/BitmapUtil.java
Normal file
394
servicing/src/main/java/com/za/signature/util/BitmapUtil.java
Normal file
@ -0,0 +1,394 @@
|
||||
package com.za.signature.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.za.signature.config.PenConfig;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 图像操作工具类
|
||||
*
|
||||
* @author king
|
||||
* @since 2018/07/05
|
||||
*/
|
||||
public class BitmapUtil {
|
||||
|
||||
/**
|
||||
* 逐行扫描 清除边界空白
|
||||
*
|
||||
* @param blank 边距留多少个像素
|
||||
* @param color 背景色限定
|
||||
* @return 清除边界后的Bitmap
|
||||
*/
|
||||
public static Bitmap clearBlank(Bitmap mBitmap, int blank, int color) {
|
||||
if (mBitmap != null) {
|
||||
int height = mBitmap.getHeight();
|
||||
int width = mBitmap.getWidth();
|
||||
int top = 0, left = 0, right = 0, bottom = 0;
|
||||
int[] widthPixels = new int[width];
|
||||
boolean isStop;
|
||||
for (int y = 0; y < height; y++) {
|
||||
mBitmap.getPixels(widthPixels, 0, width, 0, y, width, 1);
|
||||
isStop = false;
|
||||
for (int pix : widthPixels) {
|
||||
if (pix != color) {
|
||||
|
||||
top = y;
|
||||
isStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int y = height - 1; y >= 0; y--) {
|
||||
mBitmap.getPixels(widthPixels, 0, width, 0, y, width, 1);
|
||||
isStop = false;
|
||||
for (int pix : widthPixels) {
|
||||
if (pix != color) {
|
||||
bottom = y;
|
||||
isStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
widthPixels = new int[height];
|
||||
for (int x = 0; x < width; x++) {
|
||||
mBitmap.getPixels(widthPixels, 0, 1, x, 0, 1, height);
|
||||
isStop = false;
|
||||
for (int pix : widthPixels) {
|
||||
if (pix != color) {
|
||||
left = x;
|
||||
isStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int x = width - 1; x > 0; x--) {
|
||||
mBitmap.getPixels(widthPixels, 0, 1, x, 0, 1, height);
|
||||
isStop = false;
|
||||
for (int pix : widthPixels) {
|
||||
if (pix != color) {
|
||||
right = x;
|
||||
isStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (blank < 0) {
|
||||
blank = 0;
|
||||
}
|
||||
left = Math.max(left - blank, 0);
|
||||
top = Math.max(top - blank, 0);
|
||||
right = Math.min(right + blank, width - 1);
|
||||
bottom = Math.min(bottom + blank, height - 1);
|
||||
return Bitmap.createBitmap(mBitmap, left, top, right - left, bottom - top);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除bitmap左右边界空白
|
||||
*
|
||||
* @param mBitmap 源图
|
||||
* @param blank 边距留多少个像素
|
||||
* @param color 背景色限定
|
||||
* @return 清除后的bitmap
|
||||
*/
|
||||
public static Bitmap clearLRBlank(Bitmap mBitmap, int blank, int color) {
|
||||
if (mBitmap != null) {
|
||||
int height = mBitmap.getHeight();
|
||||
int width = mBitmap.getWidth();
|
||||
int left = 0, right = 0;
|
||||
int[] pixs = new int[height];
|
||||
boolean isStop;
|
||||
for (int x = 0; x < width; x++) {
|
||||
mBitmap.getPixels(pixs, 0, 1, x, 0, 1, height);
|
||||
isStop = false;
|
||||
for (int pix : pixs) {
|
||||
if (pix != color) {
|
||||
left = x;
|
||||
isStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
for (int x = width - 1; x > 0; x--) {
|
||||
mBitmap.getPixels(pixs, 0, 1, x, 0, 1, height);
|
||||
isStop = false;
|
||||
for (int pix : pixs) {
|
||||
if (pix != color) {
|
||||
right = x;
|
||||
isStop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isStop) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (blank < 0) {
|
||||
blank = 0;
|
||||
}
|
||||
left = Math.max(left - blank, 0);
|
||||
right = Math.min(right + blank, width - 1);
|
||||
return Bitmap.createBitmap(mBitmap, left, 0, right - left, height);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 给Bitmap添加背景色
|
||||
*
|
||||
* @param srcBitmap 源图
|
||||
* @param color 背景颜色
|
||||
* @return 修改背景后的bitmap
|
||||
*/
|
||||
public static Bitmap drawBgToBitmap(Bitmap srcBitmap, int color) {
|
||||
Paint paint = new Paint();
|
||||
paint.setColor(color);
|
||||
Bitmap bitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());
|
||||
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
canvas.drawRect(0, 0, srcBitmap.getWidth(), srcBitmap.getHeight(), paint);
|
||||
canvas.drawBitmap(srcBitmap, 0, 0, paint);
|
||||
return bitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存图像到本地
|
||||
*
|
||||
* @param bmp 源图
|
||||
* @param quality 压缩质量
|
||||
* @param format 图片格式
|
||||
* @return 保存后的图片地址
|
||||
*/
|
||||
public static String saveImage(Context context, Bitmap bmp, int quality, String format) {
|
||||
if (bmp == null) {
|
||||
return null;
|
||||
}
|
||||
FileOutputStream fos = null;
|
||||
try {
|
||||
String appDir = context.getExternalCacheDir().getAbsolutePath();
|
||||
File saveDir = new File(appDir, "signImg");
|
||||
if (!saveDir.exists()) {
|
||||
saveDir.mkdirs();
|
||||
}
|
||||
String fileName;
|
||||
Bitmap.CompressFormat compressFormat;
|
||||
if (PenConfig.FORMAT_JPG.equals(format)) {
|
||||
compressFormat = Bitmap.CompressFormat.JPEG;
|
||||
fileName = System.currentTimeMillis() + ".jpg";
|
||||
} else {
|
||||
compressFormat = Bitmap.CompressFormat.PNG;
|
||||
fileName = System.currentTimeMillis() + ".png";
|
||||
}
|
||||
File file = new File(saveDir, fileName);
|
||||
fos = new FileOutputStream(file);
|
||||
bmp.compress(compressFormat, quality, fos);
|
||||
fos.flush();
|
||||
return file.getAbsolutePath();
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (fos != null) {
|
||||
try {
|
||||
fos.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据宽度缩放图片,高度等比例
|
||||
*
|
||||
* @param bm 源图
|
||||
* @param newWidth 新宽度
|
||||
* @return 缩放后的bitmap
|
||||
*/
|
||||
public static Bitmap zoomImg(Bitmap bm, int newWidth) {
|
||||
// 获得图片的宽高
|
||||
int width = bm.getWidth();
|
||||
int height = bm.getHeight();
|
||||
// 计算缩放比例
|
||||
float ratio = ((float) newWidth) / width;
|
||||
// 取得想要缩放的matrix参数
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postScale(ratio, ratio);
|
||||
// 得到新的图片
|
||||
return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 缩放图片至指定宽高
|
||||
*
|
||||
* @param bm 源图
|
||||
* @param newWidth 新宽度
|
||||
* @param newHeight 新高度
|
||||
* @return 缩放后的bitmap
|
||||
*/
|
||||
public static Bitmap zoomImage(Bitmap bm, int newWidth, int newHeight) {
|
||||
// 获得图片的宽高
|
||||
int width = bm.getWidth();
|
||||
int height = bm.getHeight();
|
||||
// 计算缩放比例
|
||||
float scaleWidth = ((float) newWidth) / width;
|
||||
float scaleHeight = ((float) newHeight) / height;
|
||||
// 取得想要缩放的matrix参数
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.postScale(scaleWidth, scaleHeight);
|
||||
// 得到新的图片
|
||||
return Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据宽高之中最大缩放比缩放图片
|
||||
*
|
||||
* @param bitmap 源图
|
||||
* @param newWidth 新宽度
|
||||
* @param newHeight 新高度
|
||||
* @return 缩放后的bitmap
|
||||
*/
|
||||
public static Bitmap zoomImg(Bitmap bitmap, int newWidth,
|
||||
int newHeight) {
|
||||
// 获取这个图片的宽和高
|
||||
int width = bitmap.getWidth();
|
||||
int height = bitmap.getHeight();
|
||||
// 创建操作图片用的matrix对象
|
||||
Matrix matrix = new Matrix();
|
||||
// 计算宽高缩放率
|
||||
float scaleWidth = ((float) newWidth) / width;
|
||||
float scaleHeight = ((float) newHeight) / height;
|
||||
float ratio = Math.max(scaleWidth, scaleHeight);
|
||||
// 缩放图片动作
|
||||
matrix.postScale(ratio, ratio);
|
||||
return Bitmap.createBitmap(bitmap, 0, 0, width,
|
||||
height, matrix, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 给图片右下角添加水印
|
||||
*
|
||||
* @param src 源图
|
||||
* @param watermark 水印图
|
||||
* @param bgColor 背景色
|
||||
* @param fixed 源图是否固定大小,固定则在源图上绘制印章,不固定则动态改变图片大小
|
||||
* @return 添加水印后的图片
|
||||
*/
|
||||
public static Bitmap addWaterMask(Bitmap src, Bitmap watermark, int bgColor, boolean fixed) {
|
||||
int w = src.getWidth();
|
||||
int h = src.getHeight();
|
||||
//获取原始水印图片的宽、高
|
||||
int w2 = watermark.getWidth();
|
||||
int h2 = watermark.getHeight();
|
||||
|
||||
//合理控制水印大小
|
||||
Matrix matrix1 = new Matrix();
|
||||
float ratio;
|
||||
|
||||
ratio = (float) w2 / w;
|
||||
if (ratio > 1.0f && ratio <= 2.0f) {
|
||||
ratio = 0.7f;
|
||||
} else if (ratio > 2.0f) {
|
||||
ratio = 0.5f;
|
||||
} else if (ratio <= 0.2f) {
|
||||
ratio = 2.0f;
|
||||
} else if (ratio < 0.3f) {
|
||||
ratio = 1.5f;
|
||||
} else if (ratio <= 0.4f) {
|
||||
ratio = 1.2f;
|
||||
} else if (ratio < 1.0f) {
|
||||
ratio = 1.0f;
|
||||
}
|
||||
matrix1.postScale(ratio, ratio);
|
||||
watermark = Bitmap.createBitmap(watermark, 0, 0, w2, h2, matrix1, true);
|
||||
|
||||
//获取新的水印图片的宽、高
|
||||
w2 = watermark.getWidth();
|
||||
h2 = watermark.getHeight();
|
||||
if (!fixed) {
|
||||
if (w < 1.5 * w2) {
|
||||
w = w + w2;
|
||||
}
|
||||
if (h < 2 * h2) {
|
||||
h = h + h2;
|
||||
}
|
||||
}
|
||||
// 创建一个新的和SRC长度宽度一样的位图
|
||||
Bitmap result = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
|
||||
Canvas cv = new Canvas(result);
|
||||
cv.drawColor(bgColor);
|
||||
//在canvas上绘制原图和新的水印图
|
||||
cv.drawBitmap(src, 0, 0, null);
|
||||
//水印图绘制在画布的右下角,距离右边和底部都为20
|
||||
cv.drawBitmap(watermark, w - w2 - 20, h - h2 - 20, null);
|
||||
cv.save();
|
||||
cv.restore();
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改图片颜色
|
||||
*
|
||||
* @param inBitmap 源图
|
||||
* @param color 颜色
|
||||
* @return 修改颜色后的图片
|
||||
*/
|
||||
public static Bitmap changeBitmapColor(Bitmap inBitmap, int color) {
|
||||
if (inBitmap == null) {
|
||||
return null;
|
||||
}
|
||||
Bitmap outBitmap = Bitmap.createBitmap(inBitmap.getWidth(), inBitmap.getHeight(), inBitmap.getConfig());
|
||||
Canvas canvas = new Canvas(outBitmap);
|
||||
Paint paint = new Paint();
|
||||
paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
|
||||
canvas.drawBitmap(inBitmap, 0, 0, paint);
|
||||
return outBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置ImageView的图片,支持改变图片颜色
|
||||
*
|
||||
* @param iv
|
||||
* @param id
|
||||
* @param color
|
||||
*/
|
||||
public static void setImage(ImageView iv, int id, int color) {
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(iv.getResources(), id);
|
||||
iv.setImageBitmap(BitmapUtil.changeBitmapColor(bitmap, color));
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
package com.za.signature.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.view.Display;
|
||||
import android.view.WindowManager;
|
||||
|
||||
/***
|
||||
* 名称:DisplayUtil<br>
|
||||
* 描述:显示相关操作工具类
|
||||
* 最近修改时间:
|
||||
* @since 2017/11/16
|
||||
* @author king
|
||||
*/
|
||||
public class DisplayUtil {
|
||||
|
||||
|
||||
/**
|
||||
* dp 转成为 px(像素)
|
||||
*/
|
||||
public static int dip2px(Context context, float dpValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (dpValue * scale + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* px(像素) 转成为 dp
|
||||
*/
|
||||
public static int px2dip(Context context, float pxValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().density;
|
||||
return (int) (pxValue / scale + 0.5f);
|
||||
}
|
||||
|
||||
/**
|
||||
* px(像素) 转成为 dp
|
||||
*/
|
||||
public static int px2sp(Context context, float pxValue) {
|
||||
final float scale = context.getResources().getDisplayMetrics().scaledDensity;
|
||||
return (int) (pxValue / scale + 0.5f);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* px(像素) 转成为 dp
|
||||
*/
|
||||
public static int sp2px(Context context, float spValue) {
|
||||
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
|
||||
return (int) (spValue * fontScale + 0.5f);
|
||||
}
|
||||
/**
|
||||
* 获取屏幕宽度
|
||||
*/
|
||||
public static int getScreenWidth(Context context) {
|
||||
WindowManager manager = (WindowManager) context
|
||||
.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = manager.getDefaultDisplay();
|
||||
return display.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取屏幕高度
|
||||
* @param context
|
||||
* @return
|
||||
*/
|
||||
public static int getScreenHeight(Context context) {
|
||||
WindowManager manager = (WindowManager) context
|
||||
.getSystemService(Context.WINDOW_SERVICE);
|
||||
Display display = manager.getDefaultDisplay();
|
||||
return display.getHeight();
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package com.za.signature.util;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
|
||||
/***
|
||||
* 沉浸式状态栏
|
||||
*
|
||||
* @since 2018-11-24
|
||||
* @author king
|
||||
*/
|
||||
public class StatusBarCompat {
|
||||
private static final int INVALID_VAL = -1;
|
||||
private static final int COLOR_DEFAULT = Color.parseColor("#20000000");
|
||||
|
||||
public static void compat(Activity activity, int statusColor) {
|
||||
if (statusColor != INVALID_VAL) {
|
||||
activity.getWindow().setStatusBarColor(statusColor);
|
||||
}
|
||||
}
|
||||
|
||||
public static void compat(Activity activity) {
|
||||
compat(activity, INVALID_VAL);
|
||||
}
|
||||
|
||||
|
||||
public static int getStatusBarHeight(Context context) {
|
||||
int result = 0;
|
||||
int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = context.getResources().getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
211
servicing/src/main/java/com/za/signature/util/StepOperator.java
Normal file
211
servicing/src/main/java/com/za/signature/util/StepOperator.java
Normal file
@ -0,0 +1,211 @@
|
||||
package com.za.signature.util;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 撤销/恢复 操作工具类
|
||||
*
|
||||
* @author king
|
||||
* @since 2018/06/27
|
||||
*/
|
||||
public class StepOperator {
|
||||
|
||||
/**
|
||||
* 缓存步骤数
|
||||
*/
|
||||
private static final int CAPACITY = 12;
|
||||
/**
|
||||
* 保存每一步绘制的bitmap
|
||||
*/
|
||||
private List<Bitmap> mBitmaps = null;
|
||||
|
||||
/**
|
||||
* 允许缓存Bitmap的最大宽度限制,过大容易内存溢出
|
||||
*/
|
||||
public static int MAX_CACHE_BITMAP_WIDTH = 1024;
|
||||
/**
|
||||
* 当前位置
|
||||
*/
|
||||
private int currentIndex;
|
||||
|
||||
private boolean isUndo = false;
|
||||
|
||||
/**
|
||||
* 最大缓存内存大小
|
||||
*/
|
||||
private final int cacheMemory;
|
||||
|
||||
public StepOperator() {
|
||||
if (mBitmaps == null) {
|
||||
mBitmaps = new ArrayList<>();
|
||||
}
|
||||
currentIndex = -1;
|
||||
int maxMemory = (int) Runtime.getRuntime().maxMemory();
|
||||
cacheMemory = maxMemory / 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置
|
||||
*/
|
||||
public void reset() {
|
||||
if (mBitmaps != null) {
|
||||
for (Bitmap bitmap : mBitmaps) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
mBitmaps.clear();
|
||||
}
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 内存是否足够
|
||||
*
|
||||
*/
|
||||
private boolean isMemoryEnable() {
|
||||
int bitmapCache = 0;
|
||||
for (Bitmap bitmap : mBitmaps) {
|
||||
bitmapCache += bitmap.getRowBytes() * bitmap.getHeight();
|
||||
}
|
||||
return bitmapCache <= cacheMemory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 缓存绘制的Bitmap
|
||||
*
|
||||
* @param bitmap
|
||||
*/
|
||||
public void addBitmap(Bitmap bitmap) {
|
||||
if (mBitmaps == null) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (!isMemoryEnable() && mBitmaps.size() > 1) {
|
||||
mBitmaps.get(1).recycle();
|
||||
//删除第一笔(0的位置有空的占位图)
|
||||
mBitmaps.remove(1);
|
||||
}
|
||||
if (currentIndex != -1 && isUndo) {
|
||||
for (int i = currentIndex + 1; i < mBitmaps.size(); i++) {
|
||||
mBitmaps.get(i).recycle();
|
||||
}
|
||||
mBitmaps = mBitmaps.subList(0, currentIndex + 1);
|
||||
isUndo = false;
|
||||
}
|
||||
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), null, true);
|
||||
mBitmaps.add(bitmap);
|
||||
currentIndex = mBitmaps.size() - 1;
|
||||
|
||||
if (mBitmaps.size() > CAPACITY) {
|
||||
mBitmaps.get(1).recycle();
|
||||
//删除第一笔(0的位置有空的占位图)
|
||||
mBitmaps.remove(1);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (OutOfMemoryError e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前是否第一步
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public boolean currentIsFirst() {
|
||||
if (mBitmaps != null && currentIndex == 0) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断当前是否最后一步
|
||||
*
|
||||
*/
|
||||
public boolean currentIsLast() {
|
||||
return mBitmaps != null && currentIndex == mBitmaps.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 撤销
|
||||
*/
|
||||
public void undo(Bitmap srcBitmap) {
|
||||
if (mBitmaps == null) {
|
||||
return;
|
||||
}
|
||||
isUndo = true;
|
||||
currentIndex--;
|
||||
if (currentIndex < 0) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
try {
|
||||
Bitmap bitmap = mBitmaps.get(currentIndex);
|
||||
if (bitmap.isRecycled()) {
|
||||
return;
|
||||
}
|
||||
bitmap = BitmapUtil.zoomImg(bitmap, srcBitmap.getWidth());
|
||||
if (bitmap.getWidth() > srcBitmap.getWidth() || bitmap.getHeight() > srcBitmap.getHeight()) {
|
||||
bitmap = BitmapUtil.zoomImage(bitmap, srcBitmap.getWidth(), srcBitmap.getHeight());
|
||||
}
|
||||
//保存所有的像素的数组,图片宽×高
|
||||
int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
|
||||
|
||||
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
srcBitmap.setPixels(pixels, 0, bitmap.getWidth(), 0, 0,
|
||||
bitmap.getWidth(), bitmap.getHeight());
|
||||
} catch (OutOfMemoryError e) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复
|
||||
*/
|
||||
public void redo(Bitmap srcBitmap) {
|
||||
if (mBitmaps == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
currentIndex++;
|
||||
int lastIndex = mBitmaps.size() - 1;
|
||||
if (currentIndex >= lastIndex) {
|
||||
currentIndex = lastIndex;
|
||||
}
|
||||
try {
|
||||
Bitmap bitmap = mBitmaps.get(currentIndex);
|
||||
if (bitmap.isRecycled()) {
|
||||
return;
|
||||
}
|
||||
bitmap = BitmapUtil.zoomImg(bitmap, srcBitmap.getWidth());
|
||||
if (bitmap.getWidth() > srcBitmap.getWidth() || bitmap.getHeight() > srcBitmap.getHeight()) {
|
||||
bitmap = BitmapUtil.zoomImage(bitmap, srcBitmap.getWidth(), srcBitmap.getHeight());
|
||||
}
|
||||
//保存所有的像素的数组,图片宽×高
|
||||
int[] pixels = new int[bitmap.getWidth() * bitmap.getHeight()];
|
||||
bitmap.getPixels(pixels, 0, bitmap.getWidth(), 0, 0, bitmap.getWidth(), bitmap.getHeight());
|
||||
srcBitmap.setPixels(pixels, 0, bitmap.getWidth(), 0, 0,
|
||||
bitmap.getWidth(), bitmap.getHeight());
|
||||
} catch (OutOfMemoryError e) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 清空
|
||||
*/
|
||||
public void freeBitmaps() {
|
||||
if (mBitmaps == null) {
|
||||
return;
|
||||
}
|
||||
for (Bitmap bitmap : mBitmaps) {
|
||||
bitmap.recycle();
|
||||
}
|
||||
mBitmaps.clear();
|
||||
mBitmaps = null;
|
||||
currentIndex = -1;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,527 @@
|
||||
package com.za.signature.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.FrameLayout.LayoutParams;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/***
|
||||
* 沉浸式状态栏
|
||||
*
|
||||
* 最近修改时间:2018年09月17日 17:48分
|
||||
* @since 2018-09-17
|
||||
* @author king
|
||||
*/
|
||||
public class SystemBarTintManager {
|
||||
static {
|
||||
// Android allows a system property to override the presence of the navigation bar.
|
||||
// Used by the emulator.
|
||||
// See https://github.com/android/platform_frameworks_base/blob/master/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java#L1076
|
||||
try {
|
||||
Class c = Class.forName("android.os.SystemProperties");
|
||||
Method m = c.getDeclaredMethod("get", String.class);
|
||||
m.setAccessible(true);
|
||||
sNavBarOverride = (String) m.invoke(null, "qemu.hw.mainkeys");
|
||||
} catch (Throwable e) {
|
||||
sNavBarOverride = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The default system bar tint color value.
|
||||
*/
|
||||
public static final int DEFAULT_TINT_COLOR = 0x99000000;
|
||||
|
||||
private static String sNavBarOverride;
|
||||
|
||||
private final SystemBarConfig mConfig;
|
||||
private boolean mStatusBarAvailable;
|
||||
private boolean mNavBarAvailable;
|
||||
private boolean mStatusBarTintEnabled;
|
||||
private boolean mNavBarTintEnabled;
|
||||
private View mStatusBarTintView;
|
||||
private View mNavBarTintView;
|
||||
|
||||
/**
|
||||
* Constructor. Call this in the host activity onCreate method after its
|
||||
* content view has been set. You should always create new instances when
|
||||
* the host activity is recreated.
|
||||
*
|
||||
* @param activity The host activity.
|
||||
*/
|
||||
public SystemBarTintManager(Activity activity) {
|
||||
|
||||
Window win = activity.getWindow();
|
||||
ViewGroup decorViewGroup = (ViewGroup) win.getDecorView();
|
||||
|
||||
// check theme attrs
|
||||
int[] attrs = {android.R.attr.windowTranslucentStatus,
|
||||
android.R.attr.windowTranslucentNavigation};
|
||||
TypedArray a = activity.obtainStyledAttributes(attrs);
|
||||
try {
|
||||
mStatusBarAvailable = a.getBoolean(0, false);
|
||||
mNavBarAvailable = a.getBoolean(1, false);
|
||||
} finally {
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
// check window flags
|
||||
WindowManager.LayoutParams winParams = win.getAttributes();
|
||||
int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
|
||||
if ((winParams.flags & bits) != 0) {
|
||||
mStatusBarAvailable = true;
|
||||
}
|
||||
bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
|
||||
if ((winParams.flags & bits) != 0) {
|
||||
mNavBarAvailable = true;
|
||||
}
|
||||
|
||||
mConfig = new SystemBarConfig(activity, mStatusBarAvailable, mNavBarAvailable);
|
||||
// device might not have virtual navigation keys
|
||||
if (!mConfig.hasNavigtionBar()) {
|
||||
mNavBarAvailable = false;
|
||||
}
|
||||
|
||||
if (mStatusBarAvailable) {
|
||||
setupStatusBarView(activity, decorViewGroup);
|
||||
}
|
||||
if (mNavBarAvailable) {
|
||||
setupNavBarView(activity, decorViewGroup);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable tinting of the system status bar.
|
||||
*
|
||||
* If the platform is running Jelly Bean or earlier, or translucent system
|
||||
* UI modes have not been enabled in either the theme or via window flags,
|
||||
* then this method does nothing.
|
||||
*
|
||||
* @param enabled True to enable tinting, false to disable it (default).
|
||||
*/
|
||||
public void setStatusBarTintEnabled(boolean enabled) {
|
||||
mStatusBarTintEnabled = enabled;
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable tinting of the system navigation bar.
|
||||
*
|
||||
* If the platform does not have soft navigation keys, is running Jelly Bean
|
||||
* or earlier, or translucent system UI modes have not been enabled in either
|
||||
* the theme or via window flags, then this method does nothing.
|
||||
*
|
||||
* @param enabled True to enable tinting, false to disable it (default).
|
||||
*/
|
||||
public void setNavigationBarTintEnabled(boolean enabled) {
|
||||
mNavBarTintEnabled = enabled;
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setVisibility(enabled ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified color tint to all system UI bars.
|
||||
*
|
||||
* @param color The color of the background tint.
|
||||
*/
|
||||
public void setTintColor(int color) {
|
||||
setStatusBarTintColor(color);
|
||||
setNavigationBarTintColor(color);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable or color resource to all system UI bars.
|
||||
*
|
||||
* @param res The identifier of the resource.
|
||||
*/
|
||||
public void setTintResource(int res) {
|
||||
setStatusBarTintResource(res);
|
||||
setNavigationBarTintResource(res);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable to all system UI bars.
|
||||
*
|
||||
* @param drawable The drawable to use as the background, or null to remove it.
|
||||
*/
|
||||
public void setTintDrawable(Drawable drawable) {
|
||||
setStatusBarTintDrawable(drawable);
|
||||
setNavigationBarTintDrawable(drawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified alpha to all system UI bars.
|
||||
*
|
||||
* @param alpha The alpha to use
|
||||
*/
|
||||
public void setTintAlpha(float alpha) {
|
||||
setStatusBarAlpha(alpha);
|
||||
setNavigationBarAlpha(alpha);
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified color tint to the system status bar.
|
||||
*
|
||||
* @param color The color of the background tint.
|
||||
*/
|
||||
public void setStatusBarTintColor(int color) {
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable or color resource to the system status bar.
|
||||
*
|
||||
* @param res The identifier of the resource.
|
||||
*/
|
||||
public void setStatusBarTintResource(int res) {
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setBackgroundResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable to the system status bar.
|
||||
*
|
||||
* @param drawable The drawable to use as the background, or null to remove it.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setStatusBarTintDrawable(Drawable drawable) {
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setBackgroundDrawable(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified alpha to the system status bar.
|
||||
*
|
||||
* @param alpha The alpha to use
|
||||
*/
|
||||
public void setStatusBarAlpha(float alpha) {
|
||||
if (mStatusBarAvailable) {
|
||||
mStatusBarTintView.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified color tint to the system navigation bar.
|
||||
*
|
||||
* @param color The color of the background tint.
|
||||
*/
|
||||
public void setNavigationBarTintColor(int color) {
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setBackgroundColor(color);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable or color resource to the system navigation bar.
|
||||
*
|
||||
* @param res The identifier of the resource.
|
||||
*/
|
||||
public void setNavigationBarTintResource(int res) {
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setBackgroundResource(res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified drawable to the system navigation bar.
|
||||
*
|
||||
* @param drawable The drawable to use as the background, or null to remove it.
|
||||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
public void setNavigationBarTintDrawable(Drawable drawable) {
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setBackgroundDrawable(drawable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the specified alpha to the system navigation bar.
|
||||
*
|
||||
* @param alpha The alpha to use
|
||||
*/
|
||||
public void setNavigationBarAlpha(float alpha) {
|
||||
if (mNavBarAvailable) {
|
||||
mNavBarTintView.setAlpha(alpha);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the system bar configuration.
|
||||
*
|
||||
* @return The system bar configuration for the current device configuration.
|
||||
*/
|
||||
public SystemBarConfig getConfig() {
|
||||
return mConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is tinting enabled for the system status bar?
|
||||
*
|
||||
* @return True if enabled, False otherwise.
|
||||
*/
|
||||
public boolean isStatusBarTintEnabled() {
|
||||
return mStatusBarTintEnabled;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is tinting enabled for the system navigation bar?
|
||||
*
|
||||
* @return True if enabled, False otherwise.
|
||||
*/
|
||||
public boolean isNavBarTintEnabled() {
|
||||
return mNavBarTintEnabled;
|
||||
}
|
||||
|
||||
private void setupStatusBarView(Context context, ViewGroup decorViewGroup) {
|
||||
mStatusBarTintView = new View(context);
|
||||
LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getStatusBarHeight());
|
||||
params.gravity = Gravity.TOP;
|
||||
if (mNavBarAvailable && !mConfig.isNavigationAtBottom()) {
|
||||
params.rightMargin = mConfig.getNavigationBarWidth();
|
||||
}
|
||||
mStatusBarTintView.setLayoutParams(params);
|
||||
mStatusBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);
|
||||
mStatusBarTintView.setVisibility(View.GONE);
|
||||
decorViewGroup.addView(mStatusBarTintView);
|
||||
}
|
||||
|
||||
private void setupNavBarView(Context context, ViewGroup decorViewGroup) {
|
||||
mNavBarTintView = new View(context);
|
||||
LayoutParams params;
|
||||
if (mConfig.isNavigationAtBottom()) {
|
||||
params = new LayoutParams(LayoutParams.MATCH_PARENT, mConfig.getNavigationBarHeight());
|
||||
params.gravity = Gravity.BOTTOM;
|
||||
} else {
|
||||
params = new LayoutParams(mConfig.getNavigationBarWidth(), LayoutParams.MATCH_PARENT);
|
||||
params.gravity = Gravity.RIGHT;
|
||||
}
|
||||
mNavBarTintView.setLayoutParams(params);
|
||||
mNavBarTintView.setBackgroundColor(DEFAULT_TINT_COLOR);
|
||||
mNavBarTintView.setVisibility(View.GONE);
|
||||
decorViewGroup.addView(mNavBarTintView);
|
||||
}
|
||||
|
||||
/**
|
||||
* Class which describes system bar sizing and other characteristics for the current
|
||||
* device configuration.
|
||||
*
|
||||
*/
|
||||
public static class SystemBarConfig {
|
||||
|
||||
private static final String STATUS_BAR_HEIGHT_RES_NAME = "status_bar_height";
|
||||
private static final String NAV_BAR_HEIGHT_RES_NAME = "navigation_bar_height";
|
||||
private static final String NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME = "navigation_bar_height_landscape";
|
||||
private static final String NAV_BAR_WIDTH_RES_NAME = "navigation_bar_width";
|
||||
private static final String SHOW_NAV_BAR_RES_NAME = "config_showNavigationBar";
|
||||
|
||||
private final boolean mTranslucentStatusBar;
|
||||
private final boolean mTranslucentNavBar;
|
||||
private final int mStatusBarHeight;
|
||||
private final int mActionBarHeight;
|
||||
private final boolean mHasNavigationBar;
|
||||
private final int mNavigationBarHeight;
|
||||
private final int mNavigationBarWidth;
|
||||
private final boolean mInPortrait;
|
||||
private final float mSmallestWidthDp;
|
||||
|
||||
private SystemBarConfig(Activity activity, boolean translucentStatusBar, boolean traslucentNavBar) {
|
||||
Resources res = activity.getResources();
|
||||
mInPortrait = (res.getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT);
|
||||
mSmallestWidthDp = getSmallestWidthDp(activity);
|
||||
mStatusBarHeight = getInternalDimensionSize(res, STATUS_BAR_HEIGHT_RES_NAME);
|
||||
mActionBarHeight = getActionBarHeight(activity);
|
||||
mNavigationBarHeight = getNavigationBarHeight(activity);
|
||||
mNavigationBarWidth = getNavigationBarWidth(activity);
|
||||
mHasNavigationBar = (mNavigationBarHeight > 0);
|
||||
mTranslucentStatusBar = translucentStatusBar;
|
||||
mTranslucentNavBar = traslucentNavBar;
|
||||
}
|
||||
|
||||
private int getActionBarHeight(Context context) {
|
||||
int result = 0;
|
||||
TypedValue tv = new TypedValue();
|
||||
context.getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true);
|
||||
result = TypedValue.complexToDimensionPixelSize(tv.data, context.getResources().getDisplayMetrics());
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getNavigationBarHeight(Context context) {
|
||||
Resources res = context.getResources();
|
||||
int result = 0;
|
||||
if (hasNavBar(context)) {
|
||||
String key;
|
||||
if (mInPortrait) {
|
||||
key = NAV_BAR_HEIGHT_RES_NAME;
|
||||
} else {
|
||||
key = NAV_BAR_HEIGHT_LANDSCAPE_RES_NAME;
|
||||
}
|
||||
return getInternalDimensionSize(res, key);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private int getNavigationBarWidth(Context context) {
|
||||
Resources res = context.getResources();
|
||||
int result = 0;
|
||||
if (hasNavBar(context)) {
|
||||
return getInternalDimensionSize(res, NAV_BAR_WIDTH_RES_NAME);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean hasNavBar(Context context) {
|
||||
Resources res = context.getResources();
|
||||
int resourceId = res.getIdentifier(SHOW_NAV_BAR_RES_NAME, "bool", "android");
|
||||
if (resourceId != 0) {
|
||||
boolean hasNav = res.getBoolean(resourceId);
|
||||
// check override flag (see static block)
|
||||
if ("1".equals(sNavBarOverride)) {
|
||||
hasNav = false;
|
||||
} else if ("0".equals(sNavBarOverride)) {
|
||||
hasNav = true;
|
||||
}
|
||||
return hasNav;
|
||||
} else { // fallback
|
||||
return !ViewConfiguration.get(context).hasPermanentMenuKey();
|
||||
}
|
||||
}
|
||||
|
||||
private int getInternalDimensionSize(Resources res, String key) {
|
||||
int result = 0;
|
||||
int resourceId = res.getIdentifier(key, "dimen", "android");
|
||||
if (resourceId > 0) {
|
||||
result = res.getDimensionPixelSize(resourceId);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private float getSmallestWidthDp(Activity activity) {
|
||||
DisplayMetrics metrics = new DisplayMetrics();
|
||||
activity.getWindowManager().getDefaultDisplay().getRealMetrics(metrics);
|
||||
float widthDp = metrics.widthPixels / metrics.density;
|
||||
float heightDp = metrics.heightPixels / metrics.density;
|
||||
return Math.min(widthDp, heightDp);
|
||||
}
|
||||
|
||||
/**
|
||||
* Should a navigation bar appear at the bottom of the screen in the current
|
||||
* device configuration? A navigation bar may appear on the right side of
|
||||
* the screen in certain configurations.
|
||||
*
|
||||
* @return True if navigation should appear at the bottom of the screen, False otherwise.
|
||||
*/
|
||||
public boolean isNavigationAtBottom() {
|
||||
return (mSmallestWidthDp >= 600 || mInPortrait);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the system status bar.
|
||||
*
|
||||
* @return The height of the status bar (in pixels).
|
||||
*/
|
||||
public int getStatusBarHeight() {
|
||||
return mStatusBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the action bar.
|
||||
*
|
||||
* @return The height of the action bar (in pixels).
|
||||
*/
|
||||
public int getActionBarHeight() {
|
||||
return mActionBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does this device have a system navigation bar?
|
||||
*
|
||||
* @return True if this device uses soft key navigation, False otherwise.
|
||||
*/
|
||||
public boolean hasNavigtionBar() {
|
||||
return mHasNavigationBar;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the system navigation bar.
|
||||
*
|
||||
* @return The height of the navigation bar (in pixels). If the device does not have
|
||||
* soft navigation keys, this will always return 0.
|
||||
*/
|
||||
public int getNavigationBarHeight() {
|
||||
return mNavigationBarHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the system navigation bar when it is placed vertically on the screen.
|
||||
*
|
||||
* @return The width of the navigation bar (in pixels). If the device does not have
|
||||
* soft navigation keys, this will always return 0.
|
||||
*/
|
||||
public int getNavigationBarWidth() {
|
||||
return mNavigationBarWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout inset for any system UI that appears at the top of the screen.
|
||||
*
|
||||
* @param withActionBar True to include the height of the action bar, False otherwise.
|
||||
* @return The layout inset (in pixels).
|
||||
*/
|
||||
public int getPixelInsetTop(boolean withActionBar) {
|
||||
return (mTranslucentStatusBar ? mStatusBarHeight : 0) + (withActionBar ? mActionBarHeight : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout inset for any system UI that appears at the bottom of the screen.
|
||||
*
|
||||
* @return The layout inset (in pixels).
|
||||
*/
|
||||
public int getPixelInsetBottom() {
|
||||
if (mTranslucentNavBar && isNavigationAtBottom()) {
|
||||
return mNavigationBarHeight;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the layout inset for any system UI that appears at the right of the screen.
|
||||
*
|
||||
* @return The layout inset (in pixels).
|
||||
*/
|
||||
public int getPixelInsetRight() {
|
||||
if (mTranslucentNavBar && !isNavigationAtBottom()) {
|
||||
return mNavigationBarWidth;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
129
servicing/src/main/java/com/za/signature/util/SystemUtil.java
Normal file
129
servicing/src/main/java/com/za/signature/util/SystemUtil.java
Normal file
@ -0,0 +1,129 @@
|
||||
package com.za.signature.util;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.view.ActionMode;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
/***
|
||||
* 显示相关操作工具类
|
||||
*
|
||||
* @since 2017/11/16
|
||||
* @author king
|
||||
*/
|
||||
public class SystemUtil {
|
||||
|
||||
/**
|
||||
* 判断是否平板设备
|
||||
* @param context
|
||||
* @return true:平板,false:手机
|
||||
*/
|
||||
public static boolean isTablet(Context context) {
|
||||
return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >=
|
||||
Configuration.SCREENLAYOUT_SIZE_LARGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* 测量View的高度
|
||||
*/
|
||||
@SuppressWarnings("ResourceType")
|
||||
public static int makeDropDownMeasureSpec(int measureSpec) {
|
||||
int mode;
|
||||
if (measureSpec == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
||||
mode = View.MeasureSpec.UNSPECIFIED;
|
||||
} else {
|
||||
mode = View.MeasureSpec.EXACTLY;
|
||||
}
|
||||
return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec), mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止软键盘弹出
|
||||
*/
|
||||
public static void disableShowInput(Context context, EditText editText) {
|
||||
Class<EditText> cls = EditText.class;
|
||||
Method method;
|
||||
try {
|
||||
method = cls.getMethod("setShowSoftInputOnFocus", boolean.class);
|
||||
method.setAccessible(true);
|
||||
method.invoke(editText, false);
|
||||
} catch (Exception e) {
|
||||
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm != null) {
|
||||
imm.hideSoftInputFromWindow(editText.getWindowToken(), 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 禁止输入框复制粘贴菜单
|
||||
*/
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
public static void disableCopyAndPaste(final EditText editText) {
|
||||
try {
|
||||
if (editText == null) {
|
||||
return;
|
||||
}
|
||||
editText.setOnLongClickListener(v -> true);
|
||||
editText.setLongClickable(false);
|
||||
editText.setOnTouchListener((v, event) -> {
|
||||
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
||||
setInsertionDisabled(editText);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
editText.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
|
||||
@Override
|
||||
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyActionMode(ActionMode mode) {
|
||||
}
|
||||
});
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void setInsertionDisabled(EditText editText) {
|
||||
try {
|
||||
Field editorField = TextView.class.getDeclaredField("mEditor");
|
||||
editorField.setAccessible(true);
|
||||
Object editorObject = editorField.get(editText);
|
||||
Class editorClass = Class.forName("android.widget.Editor");
|
||||
Field mInsertionControllerEnabledField = editorClass.getDeclaredField("mInsertionControllerEnabled");
|
||||
mInsertionControllerEnabledField.setAccessible(true);
|
||||
mInsertionControllerEnabledField.set(editorObject, false);
|
||||
Field mSelectionControllerEnabledField = editorClass.getDeclaredField("mSelectionControllerEnabled");
|
||||
mSelectionControllerEnabledField.setAccessible(true);
|
||||
mSelectionControllerEnabledField.set(editorObject, false);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,481 @@
|
||||
package com.za.signature.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.BitmapShader;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Outline;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewOutlineProvider;
|
||||
|
||||
import androidx.annotation.ColorInt;
|
||||
import androidx.annotation.ColorRes;
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.appcompat.widget.AppCompatImageView;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.util.BitmapUtil;
|
||||
|
||||
public class CircleImageView extends AppCompatImageView {
|
||||
|
||||
private static final ScaleType SCALE_TYPE = ScaleType.CENTER_CROP;
|
||||
|
||||
private static final Bitmap.Config BITMAP_CONFIG = Bitmap.Config.ARGB_4444;
|
||||
private static final int COLORDRAWABLE_DIMENSION = 2;
|
||||
|
||||
private static final int DEFAULT_BORDER_WIDTH = 0;
|
||||
private static final int DEFAULT_BORDER_COLOR = Color.parseColor("#0c53ab");
|
||||
private static final int DEFAULT_CIRCLE_BACKGROUND_COLOR = Color.WHITE;
|
||||
private static final boolean DEFAULT_BORDER_OVERLAY = false;
|
||||
|
||||
private final RectF mDrawableRect = new RectF();
|
||||
private final RectF mBorderRect = new RectF();
|
||||
|
||||
private final Matrix mShaderMatrix = new Matrix();
|
||||
private final Paint mBitmapPaint = new Paint();
|
||||
private final Paint mBorderPaint = new Paint();
|
||||
private final Paint mCircleBackgroundPaint = new Paint();
|
||||
|
||||
private int mBorderColor = DEFAULT_BORDER_COLOR;
|
||||
private int mBorderWidth = DEFAULT_BORDER_WIDTH;
|
||||
private int mCircleBackgroundColor = DEFAULT_CIRCLE_BACKGROUND_COLOR;
|
||||
|
||||
private Bitmap mBitmap;
|
||||
private BitmapShader mBitmapShader;
|
||||
private int mBitmapWidth;
|
||||
private int mBitmapHeight;
|
||||
|
||||
private float mDrawableRadius;
|
||||
private float mBorderRadius;
|
||||
|
||||
private ColorFilter mColorFilter;
|
||||
|
||||
private boolean mReady;
|
||||
private boolean mSetupPending;
|
||||
private boolean mBorderOverlay;
|
||||
private boolean mDisableCircularTransformation;
|
||||
|
||||
public CircleImageView(Context context) {
|
||||
super(context);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
public CircleImageView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CircleImageView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);
|
||||
|
||||
mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);
|
||||
mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);
|
||||
mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);
|
||||
|
||||
// Look for deprecated civ_fill_color if civ_circle_background_color is not set
|
||||
if (a.hasValue(R.styleable.CircleImageView_civ_circle_background_color)) {
|
||||
mCircleBackgroundColor = a.getColor(R.styleable.CircleImageView_civ_circle_background_color,
|
||||
DEFAULT_CIRCLE_BACKGROUND_COLOR);
|
||||
} else if (a.hasValue(R.styleable.CircleImageView_civ_fill_color)) {
|
||||
mCircleBackgroundColor = a.getColor(R.styleable.CircleImageView_civ_fill_color,
|
||||
DEFAULT_CIRCLE_BACKGROUND_COLOR);
|
||||
}
|
||||
|
||||
a.recycle();
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
super.setScaleType(SCALE_TYPE);
|
||||
mReady = true;
|
||||
|
||||
setOutlineProvider(new OutlineProvider());
|
||||
|
||||
if (mSetupPending) {
|
||||
setup();
|
||||
mSetupPending = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScaleType getScaleType() {
|
||||
return SCALE_TYPE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setScaleType(ScaleType scaleType) {
|
||||
if (scaleType != SCALE_TYPE) {
|
||||
throw new IllegalArgumentException(String.format("ScaleType %s not supported.", scaleType));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdjustViewBounds(boolean adjustViewBounds) {
|
||||
if (adjustViewBounds) {
|
||||
throw new IllegalArgumentException("adjustViewBounds not supported.");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mDisableCircularTransformation) {
|
||||
super.onDraw(canvas);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBitmap == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mCircleBackgroundColor != Color.TRANSPARENT) {
|
||||
canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mCircleBackgroundPaint);
|
||||
}
|
||||
canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);
|
||||
if (mBorderWidth > 0) {
|
||||
canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
setup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPadding(int left, int top, int right, int bottom) {
|
||||
super.setPadding(left, top, right, bottom);
|
||||
setup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPaddingRelative(int start, int top, int end, int bottom) {
|
||||
super.setPaddingRelative(start, top, end, bottom);
|
||||
setup();
|
||||
}
|
||||
|
||||
public int getBorderColor() {
|
||||
return mBorderColor;
|
||||
}
|
||||
|
||||
public void setBorderColor(@ColorInt int borderColor) {
|
||||
if (borderColor == mBorderColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
mBorderColor = borderColor;
|
||||
mBorderPaint.setColor(mBorderColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link #setBorderColor(int)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public void setBorderColorResource(@ColorRes int borderColorRes) {
|
||||
setBorderColor(getContext().getResources().getColor(borderColorRes));
|
||||
}
|
||||
|
||||
public int getCircleBackgroundColor() {
|
||||
return mCircleBackgroundColor;
|
||||
}
|
||||
|
||||
public void setCircleBackgroundColor(@ColorInt int circleBackgroundColor) {
|
||||
if (circleBackgroundColor == mCircleBackgroundColor) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCircleBackgroundColor = circleBackgroundColor;
|
||||
mCircleBackgroundPaint.setColor(circleBackgroundColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setCircleBackgroundColorResource(@ColorRes int circleBackgroundRes) {
|
||||
setCircleBackgroundColor(getContext().getResources().getColor(circleBackgroundRes));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the color drawn behind the circle-shaped drawable.
|
||||
*
|
||||
* @return The color drawn behind the drawable
|
||||
* @deprecated Use {@link #getCircleBackgroundColor()} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public int getFillColor() {
|
||||
return getCircleBackgroundColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mergeBitmapRecycle color to be drawn behind the circle-shaped drawable. Note that
|
||||
* this has no effect if the drawable is opaque or no drawable is set.
|
||||
*
|
||||
* @param fillColor The color to be drawn behind the drawable
|
||||
* @deprecated Use {@link #setCircleBackgroundColor(int)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setFillColor(@ColorInt int fillColor) {
|
||||
setCircleBackgroundColor(fillColor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set mergeBitmapRecycle color to be drawn behind the circle-shaped drawable. Note that
|
||||
* this has no effect if the drawable is opaque or no drawable is set.
|
||||
*
|
||||
* @param fillColorRes The color resource to be resolved to mergeBitmapRecycle color and
|
||||
* drawn behind the drawable
|
||||
* @deprecated Use {@link #setCircleBackgroundColorResource(int)} instead.
|
||||
*/
|
||||
@Deprecated
|
||||
public void setFillColorResource(@ColorRes int fillColorRes) {
|
||||
setCircleBackgroundColorResource(fillColorRes);
|
||||
}
|
||||
|
||||
public int getBorderWidth() {
|
||||
return mBorderWidth;
|
||||
}
|
||||
|
||||
public void setBorderWidth(int borderWidth) {
|
||||
if (borderWidth == mBorderWidth) {
|
||||
return;
|
||||
}
|
||||
|
||||
mBorderWidth = borderWidth;
|
||||
setup();
|
||||
}
|
||||
|
||||
public boolean isBorderOverlay() {
|
||||
return mBorderOverlay;
|
||||
}
|
||||
|
||||
public void setBorderOverlay(boolean borderOverlay) {
|
||||
if (borderOverlay == mBorderOverlay) {
|
||||
return;
|
||||
}
|
||||
|
||||
mBorderOverlay = borderOverlay;
|
||||
setup();
|
||||
}
|
||||
|
||||
public boolean isDisableCircularTransformation() {
|
||||
return mDisableCircularTransformation;
|
||||
}
|
||||
|
||||
public void setDisableCircularTransformation(boolean disableCircularTransformation) {
|
||||
if (mDisableCircularTransformation == disableCircularTransformation) {
|
||||
return;
|
||||
}
|
||||
|
||||
mDisableCircularTransformation = disableCircularTransformation;
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageBitmap(Bitmap bm) {
|
||||
super.setImageBitmap(bm);
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageDrawable(Drawable drawable) {
|
||||
super.setImageDrawable(drawable);
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageResource(@DrawableRes int resId) {
|
||||
super.setImageResource(resId);
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageURI(Uri uri) {
|
||||
super.setImageURI(uri);
|
||||
initializeBitmap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(ColorFilter cf) {
|
||||
if (cf == mColorFilter) {
|
||||
return;
|
||||
}
|
||||
|
||||
mColorFilter = cf;
|
||||
applyColorFilter();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ColorFilter getColorFilter() {
|
||||
return mColorFilter;
|
||||
}
|
||||
|
||||
private void applyColorFilter() {
|
||||
if (mBitmapPaint != null) {
|
||||
mBitmapPaint.setColorFilter(mColorFilter);
|
||||
}
|
||||
}
|
||||
|
||||
private Bitmap getBitmapFromDrawable(Drawable drawable) {
|
||||
if (drawable == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (drawable instanceof BitmapDrawable) {
|
||||
return ((BitmapDrawable) drawable).getBitmap();
|
||||
}
|
||||
|
||||
try {
|
||||
Bitmap bitmap;
|
||||
|
||||
if (drawable instanceof ColorDrawable) {
|
||||
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, BITMAP_CONFIG);
|
||||
} else {
|
||||
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), BITMAP_CONFIG);
|
||||
}
|
||||
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||
drawable.draw(canvas);
|
||||
return bitmap;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeBitmap() {
|
||||
if (mDisableCircularTransformation) {
|
||||
mBitmap = null;
|
||||
} else {
|
||||
mBitmap = getBitmapFromDrawable(getDrawable());
|
||||
}
|
||||
setup();
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
if (!mReady) {
|
||||
mSetupPending = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (getWidth() == 0 && getHeight() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mBitmap == null) {
|
||||
invalidate();
|
||||
return;
|
||||
}
|
||||
|
||||
mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
|
||||
|
||||
mBitmapPaint.setAntiAlias(true);
|
||||
mBitmapPaint.setShader(mBitmapShader);
|
||||
|
||||
mBorderPaint.setStyle(Paint.Style.STROKE);
|
||||
mBorderPaint.setAntiAlias(true);
|
||||
mBorderPaint.setColor(mBorderColor);
|
||||
mBorderPaint.setStrokeWidth(mBorderWidth);
|
||||
|
||||
mCircleBackgroundPaint.setStyle(Paint.Style.FILL);
|
||||
mCircleBackgroundPaint.setAntiAlias(true);
|
||||
mCircleBackgroundPaint.setColor(mCircleBackgroundColor);
|
||||
|
||||
mBitmapHeight = mBitmap.getHeight();
|
||||
mBitmapWidth = mBitmap.getWidth();
|
||||
|
||||
mBorderRect.set(calculateBounds());
|
||||
mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);
|
||||
|
||||
mDrawableRect.set(mBorderRect);
|
||||
if (!mBorderOverlay && mBorderWidth > 0) {
|
||||
mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);
|
||||
}
|
||||
mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);
|
||||
|
||||
applyColorFilter();
|
||||
updateShaderMatrix();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
private RectF calculateBounds() {
|
||||
int availableWidth = getWidth() - getPaddingLeft() - getPaddingRight();
|
||||
int availableHeight = getHeight() - getPaddingTop() - getPaddingBottom();
|
||||
|
||||
int sideLength = Math.min(availableWidth, availableHeight);
|
||||
|
||||
float left = getPaddingLeft() + (availableWidth - sideLength) / 2f;
|
||||
float top = getPaddingTop() + (availableHeight - sideLength) / 2f;
|
||||
|
||||
return new RectF(left, top, left + sideLength, top + sideLength);
|
||||
}
|
||||
|
||||
private void updateShaderMatrix() {
|
||||
float scale;
|
||||
float dx = 0;
|
||||
float dy = 0;
|
||||
|
||||
mShaderMatrix.set(null);
|
||||
|
||||
if (mBitmapWidth * mDrawableRect.height() > mDrawableRect.width() * mBitmapHeight) {
|
||||
scale = mDrawableRect.height() / (float) mBitmapHeight;
|
||||
dx = (mDrawableRect.width() - mBitmapWidth * scale) * 0.5f;
|
||||
} else {
|
||||
scale = mDrawableRect.width() / (float) mBitmapWidth;
|
||||
dy = (mDrawableRect.height() - mBitmapHeight * scale) * 0.5f;
|
||||
}
|
||||
|
||||
mShaderMatrix.setScale(scale, scale);
|
||||
mShaderMatrix.postTranslate((int) (dx + 0.5f) + mDrawableRect.left, (int) (dy + 0.5f) + mDrawableRect.top);
|
||||
|
||||
mBitmapShader.setLocalMatrix(mShaderMatrix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return inTouchableArea(event.getX(), event.getY()) && super.onTouchEvent(event);
|
||||
}
|
||||
|
||||
private boolean inTouchableArea(float x, float y) {
|
||||
return Math.pow(x - mBorderRect.centerX(), 2) + Math.pow(y - mBorderRect.centerY(), 2) <= Math.pow(mBorderRadius, 2);
|
||||
}
|
||||
|
||||
|
||||
private class OutlineProvider extends ViewOutlineProvider {
|
||||
|
||||
@Override
|
||||
public void getOutline(View view, Outline outline) {
|
||||
Rect bounds = new Rect();
|
||||
mBorderRect.roundOut(bounds);
|
||||
outline.setRoundRect(bounds, bounds.width() / 2.0f);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
public void setImage(int id, int color) {
|
||||
this.mBorderColor = color;
|
||||
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), id);
|
||||
setImageBitmap(BitmapUtil.changeBitmapColor(bitmap, color));
|
||||
}
|
||||
}
|
186
servicing/src/main/java/com/za/signature/view/CircleView.java
Normal file
186
servicing/src/main/java/com/za/signature/view/CircleView.java
Normal file
@ -0,0 +1,186 @@
|
||||
package com.za.signature.view;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.config.PenConfig;
|
||||
import com.za.signature.util.DisplayUtil;
|
||||
|
||||
|
||||
/**
|
||||
* 自定义圆形View
|
||||
*
|
||||
* @author king
|
||||
* @since 2018-06-01
|
||||
*/
|
||||
public class CircleView extends View {
|
||||
|
||||
private Paint mPaint;
|
||||
private Paint backPaint;
|
||||
private Paint borderPaint;
|
||||
private Paint outBorderPaint;
|
||||
private int paintColor;
|
||||
private int outBorderColor = Color.parseColor("#0c53ab");
|
||||
private int circleRadius;
|
||||
private int radiusLevel;
|
||||
private boolean showBorder = false;
|
||||
private boolean showOutBorder = false;
|
||||
|
||||
public CircleView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public CircleView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CircleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleView);
|
||||
paintColor = ta.getColor(R.styleable.CircleView_penColor, PenConfig.PAINT_COLOR);
|
||||
outBorderColor = ta.getColor(R.styleable.CircleView_penColor, Color.parseColor("#0c53ab"));
|
||||
radiusLevel = ta.getInteger(R.styleable.CircleView_sizeLevel, 2);
|
||||
circleRadius = DisplayUtil.dip2px(context, PaintSettingWindow.PEN_SIZES[radiusLevel]);
|
||||
showBorder = ta.getBoolean(R.styleable.CircleView_showBorder, false);
|
||||
showOutBorder = ta.getBoolean(R.styleable.CircleView_showOutBorder, false);
|
||||
ta.recycle();
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
private void init() {
|
||||
|
||||
borderPaint = new Paint();
|
||||
borderPaint.setColor(paintColor);
|
||||
borderPaint.setStrokeWidth(5);
|
||||
borderPaint.setAntiAlias(true);
|
||||
borderPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||
borderPaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
outBorderPaint = new Paint();
|
||||
outBorderPaint.setColor(outBorderColor);
|
||||
outBorderPaint.setStrokeWidth(3.5f);
|
||||
outBorderPaint.setAntiAlias(true);
|
||||
outBorderPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||
outBorderPaint.setStyle(Paint.Style.STROKE);
|
||||
|
||||
backPaint = new Paint();
|
||||
backPaint.setColor(Color.WHITE);
|
||||
backPaint.setAntiAlias(true);
|
||||
backPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||
backPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
mPaint = new Paint();
|
||||
mPaint.setColor(paintColor);
|
||||
mPaint.setStrokeWidth(20);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStrokeJoin(Paint.Join.ROUND);
|
||||
mPaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.drawCircle((float) getWidth() / 2, (float) getHeight() / 2, (float) getWidth() / 2, backPaint);
|
||||
//绘制内边框
|
||||
if (showBorder) {
|
||||
canvas.drawCircle((float) getWidth() / 2, (float) getHeight() / 2, circleRadius / 2.5f + 10, borderPaint);
|
||||
}
|
||||
//绘制外边框
|
||||
if (showOutBorder) {
|
||||
canvas.drawCircle((float) getWidth() / 2, (float) getHeight() / 2, (float) getWidth() / 2 - 2f, outBorderPaint);
|
||||
}
|
||||
canvas.drawCircle((float) getWidth() / 2, (float) getHeight() / 2, circleRadius / 2.5f, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = onMeasureR(0, widthMeasureSpec);
|
||||
int height = onMeasureR(1, heightMeasureSpec);
|
||||
setMeasuredDimension(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算控件宽高
|
||||
*/
|
||||
public int onMeasureR(int attr, int oldMeasure) {
|
||||
|
||||
int newSize = 0;
|
||||
int mode = MeasureSpec.getMode(oldMeasure);
|
||||
int oldSize = MeasureSpec.getSize(oldMeasure);
|
||||
|
||||
switch (mode) {
|
||||
case MeasureSpec.EXACTLY:
|
||||
newSize = oldSize;
|
||||
break;
|
||||
case MeasureSpec.AT_MOST:
|
||||
case MeasureSpec.UNSPECIFIED:
|
||||
float value;
|
||||
if (attr == 0) {
|
||||
if (showOutBorder) {
|
||||
value = (circleRadius / 2.5f + 40) * 2;
|
||||
} else {
|
||||
value = (circleRadius / 2.5f + 25) * 2;
|
||||
}
|
||||
newSize = (int) (getPaddingLeft() + value + getPaddingRight());
|
||||
} else if (attr == 1) {
|
||||
if (showOutBorder) {
|
||||
value = (circleRadius / 2.5f + 40) * 2;
|
||||
} else {
|
||||
value = (circleRadius / 2.5f + 25) * 2;
|
||||
}
|
||||
// value = (circleRadius / 2.5f + 20) * 2;
|
||||
// 控件的高度 + getPaddingTop() + getPaddingBottom()
|
||||
newSize = (int) (getPaddingTop() + value + getPaddingBottom());
|
||||
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return newSize;
|
||||
}
|
||||
|
||||
public void setPaintColor(int paintColor) {
|
||||
this.paintColor = paintColor;
|
||||
mPaint.setColor(paintColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setRadiusLevel(int level) {
|
||||
this.radiusLevel = level;
|
||||
this.circleRadius = DisplayUtil.dip2px(getContext(), PaintSettingWindow.PEN_SIZES[level]);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void showBorder(boolean showBorder) {
|
||||
this.showBorder = showBorder;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setOutBorderColor(int outBorderColor) {
|
||||
this.outBorderColor = outBorderColor;
|
||||
outBorderPaint.setColor(outBorderColor);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public int getPaintColor() {
|
||||
return paintColor;
|
||||
}
|
||||
|
||||
public int getRadiusLevel() {
|
||||
return radiusLevel;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
package com.za.signature.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.za.signature.util.StatusBarCompat;
|
||||
|
||||
/***
|
||||
* 名称:EraserView
|
||||
* 描述:橡皮擦指示器
|
||||
* 最近修改时间:2018年09月13日 16:57分
|
||||
* @since 2018-09-13
|
||||
* @author king
|
||||
*/
|
||||
public class EraserView extends View {
|
||||
|
||||
private Paint paint;
|
||||
private int statusBarHeight;
|
||||
|
||||
public EraserView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public EraserView(Context context, @Nullable AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public EraserView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
setBackgroundDrawable(null);
|
||||
setBackground(null);
|
||||
setDrawingCacheEnabled(false);
|
||||
paint = new Paint();
|
||||
paint.setAntiAlias(true);
|
||||
paint.setStyle(Paint.Style.FILL);
|
||||
if (Build.MODEL.contains("EBEN")) {//E人E本顶部没有状态栏
|
||||
statusBarHeight = 0;
|
||||
} else {
|
||||
statusBarHeight = StatusBarCompat.getStatusBarHeight(context);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setX(float x) {
|
||||
super.setX(x - (float) getWidth() / 2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setY(float y) {
|
||||
super.setY(y - (float) getHeight() / 2 - statusBarHeight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.drawColor(Color.TRANSPARENT);
|
||||
paint.setColor(Color.LTGRAY);
|
||||
canvas.drawCircle((float) getWidth() / 2, (float) getHeight() / 2, (float) getWidth() / 2, paint);
|
||||
paint.setColor(Color.WHITE);
|
||||
canvas.drawCircle((float) getWidth() / 2, (float) getHeight() / 2, (float) getWidth() / 2 - 8, paint);
|
||||
}
|
||||
}
|
121
servicing/src/main/java/com/za/signature/view/GridDrawable.java
Normal file
121
servicing/src/main/java/com/za/signature/view/GridDrawable.java
Normal file
@ -0,0 +1,121 @@
|
||||
package com.za.signature.view;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.DashPathEffect;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/***
|
||||
* 名称:GridDrawable<br>
|
||||
* 描述:自定义米格背景视图
|
||||
* 最近修改时间:
|
||||
* @since 2017/11/16
|
||||
* @author king
|
||||
*/
|
||||
|
||||
public class GridDrawable extends Drawable {
|
||||
|
||||
private Paint mPaint;
|
||||
private Paint mDashPaint;
|
||||
private Paint mLinePaint;
|
||||
|
||||
private Bitmap mBitmap;
|
||||
private Canvas mCanvas;
|
||||
|
||||
private Path linePath;
|
||||
private Path dashPath;
|
||||
|
||||
private final int mWidth;
|
||||
private final int mHeight;
|
||||
private final int backgroundColor;
|
||||
|
||||
public GridDrawable(int width, int height, int backgroundColor) {
|
||||
this.mWidth = width;
|
||||
this.mHeight = height;
|
||||
this.backgroundColor = backgroundColor;
|
||||
init();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
//边框画笔
|
||||
mPaint = new Paint();
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStrokeWidth(10.0f);
|
||||
mPaint.setColor(Color.parseColor("#c4c4c4"));
|
||||
|
||||
//虚线画笔
|
||||
mDashPaint = new Paint();
|
||||
mDashPaint.setStyle(Paint.Style.STROKE);
|
||||
mDashPaint.setAntiAlias(true);
|
||||
mDashPaint.setStrokeWidth(5.0f);
|
||||
mDashPaint.setColor(Color.parseColor("#c4c4c4"));
|
||||
mDashPaint.setPathEffect(new DashPathEffect(new float[]{50, 40}, 0));
|
||||
|
||||
mLinePaint = new Paint();
|
||||
mLinePaint.setStyle(Paint.Style.STROKE);
|
||||
mLinePaint.setAntiAlias(true);
|
||||
mLinePaint.setStrokeWidth(5.0f);
|
||||
mLinePaint.setColor(Color.parseColor("#c4c4c4"));
|
||||
|
||||
linePath = new Path();
|
||||
dashPath = new Path();
|
||||
|
||||
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444);
|
||||
mCanvas = new Canvas(mBitmap);
|
||||
mCanvas.drawColor(backgroundColor);
|
||||
doDraw();
|
||||
}
|
||||
|
||||
private void doDraw() {
|
||||
Rect rect = new Rect(0, 0, mWidth, mHeight);
|
||||
mCanvas.drawRect(rect, mPaint);
|
||||
|
||||
linePath.moveTo(0, (float) mHeight / 2);
|
||||
linePath.lineTo(mWidth, (float) mHeight / 2);
|
||||
mCanvas.drawPath(linePath, mLinePaint);
|
||||
|
||||
linePath.moveTo((float) mWidth / 2, 0);
|
||||
linePath.lineTo((float) mWidth / 2, mHeight);
|
||||
mCanvas.drawPath(linePath, mLinePaint);
|
||||
|
||||
dashPath.moveTo(0, 0);
|
||||
dashPath.lineTo(mWidth, mHeight);
|
||||
mCanvas.drawPath(dashPath, mDashPaint);
|
||||
|
||||
dashPath.moveTo(mWidth, 0);
|
||||
dashPath.lineTo(0, mHeight);
|
||||
mCanvas.drawPath(dashPath, mDashPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int i) {
|
||||
mPaint.setAlpha(i);
|
||||
mDashPaint.setAlpha(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||
mPaint.setColorFilter(colorFilter);
|
||||
mDashPaint.setColorFilter(colorFilter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.TRANSLUCENT;
|
||||
}
|
||||
}
|
221
servicing/src/main/java/com/za/signature/view/GridPaintView.java
Normal file
221
servicing/src/main/java/com/za/signature/view/GridPaintView.java
Normal file
@ -0,0 +1,221 @@
|
||||
package com.za.signature.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.config.PenConfig;
|
||||
import com.za.signature.pen.BasePen;
|
||||
import com.za.signature.pen.SteelPen;
|
||||
import com.za.signature.util.BitmapUtil;
|
||||
import com.za.signature.util.DisplayUtil;
|
||||
|
||||
/**
|
||||
* 田字格手写画板
|
||||
*
|
||||
* @author king
|
||||
* @since 2018/5/4
|
||||
*/
|
||||
public class GridPaintView extends View {
|
||||
private Paint mPaint;
|
||||
private Canvas mCanvas;
|
||||
private Bitmap mBitmap;
|
||||
private BasePen mStokeBrushPen;
|
||||
|
||||
/**
|
||||
* 是否有绘制
|
||||
*/
|
||||
private boolean hasDraw;
|
||||
|
||||
/**
|
||||
* 画布宽度
|
||||
*/
|
||||
private final int mWidth;
|
||||
/**
|
||||
* 画布高度
|
||||
*/
|
||||
private final int mHeight;
|
||||
|
||||
public GridPaintView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public GridPaintView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public GridPaintView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
mWidth = (int) getResources().getDimension(R.dimen.sign_grid_size);
|
||||
mHeight = (int) getResources().getDimension(R.dimen.sign_grid_size);
|
||||
initParameter();
|
||||
}
|
||||
|
||||
private void initParameter() {
|
||||
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
|
||||
|
||||
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_8888);
|
||||
mStokeBrushPen = new SteelPen();
|
||||
|
||||
initPaint();
|
||||
initCanvas();
|
||||
}
|
||||
|
||||
|
||||
private void initPaint() {
|
||||
mPaint = new Paint();
|
||||
mPaint.setColor(PenConfig.PAINT_COLOR);
|
||||
mPaint.setStrokeWidth(DisplayUtil.dip2px(getContext(), PaintSettingWindow.PEN_SIZES[PenConfig.PAINT_SIZE_LEVEL]));
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setAlpha(0xFF);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStrokeMiter(1.0f);
|
||||
mStokeBrushPen.setPaint(mPaint);
|
||||
}
|
||||
|
||||
private void initCanvas() {
|
||||
mCanvas = new Canvas(mBitmap);
|
||||
//设置画布的颜色的问题
|
||||
mCanvas.drawColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
|
||||
mStokeBrushPen.draw(canvas);
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
|
||||
mStokeBrushPen.onTouchEvent(event, mCanvas);
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
if (mGetWriteListener != null) {
|
||||
mGetWriteListener.onWriteStart();
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
hasDraw = true;
|
||||
if (mGetWriteListener != null) {
|
||||
mGetWriteListener.onWriteStart();
|
||||
}
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mGetWriteListener != null) {
|
||||
mGetWriteListener.onWriteCompleted(System.currentTimeMillis());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 判断是否有绘制内容在画布上
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return !hasDraw;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 清除画布
|
||||
*/
|
||||
public void reset() {
|
||||
mBitmap.eraseColor(Color.TRANSPARENT);
|
||||
hasDraw = false;
|
||||
mStokeBrushPen.clear();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
public void release() {
|
||||
destroyDrawingCache();
|
||||
if (mBitmap != null) {
|
||||
mBitmap.recycle();
|
||||
mBitmap = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public WriteListener mGetWriteListener;
|
||||
|
||||
public void setGetTimeListener(WriteListener l) {
|
||||
mGetWriteListener = l;
|
||||
}
|
||||
|
||||
|
||||
public interface WriteListener {
|
||||
/**
|
||||
* 开始书写
|
||||
*/
|
||||
void onWriteStart();
|
||||
|
||||
/**
|
||||
* 书写完毕
|
||||
*
|
||||
* @param time 当前时间
|
||||
*/
|
||||
void onWriteCompleted(long time);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置画笔大小
|
||||
*
|
||||
* @param width 大小
|
||||
*/
|
||||
public void setPaintWidth(int width) {
|
||||
if (mPaint != null) {
|
||||
mPaint.setStrokeWidth(DisplayUtil.dip2px(getContext(), width));
|
||||
mStokeBrushPen.setPaint(mPaint);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置画笔颜色
|
||||
*
|
||||
* @param color 颜色
|
||||
*/
|
||||
public void setPaintColor(int color) {
|
||||
if (mPaint != null) {
|
||||
mPaint.setColor(color);
|
||||
mStokeBrushPen.setPaint(mPaint);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 构建Bitmap
|
||||
*
|
||||
* @return 所绘制的bitmap
|
||||
*/
|
||||
public Bitmap buildBitmap(boolean clearBlank, int zoomSize) {
|
||||
if (!hasDraw) {
|
||||
return null;
|
||||
}
|
||||
Bitmap result = BitmapUtil.zoomImg(mBitmap, zoomSize);
|
||||
if (clearBlank) {
|
||||
result = BitmapUtil.clearLRBlank(result, 10, Color.TRANSPARENT);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
2739
servicing/src/main/java/com/za/signature/view/HVScrollView.java
Normal file
2739
servicing/src/main/java/com/za/signature/view/HVScrollView.java
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,201 @@
|
||||
package com.za.signature.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.text.Editable;
|
||||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.style.ImageSpan;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
|
||||
import androidx.appcompat.widget.AppCompatEditText;
|
||||
|
||||
import com.za.signature.util.DisplayUtil;
|
||||
import com.za.signature.util.SystemUtil;
|
||||
|
||||
|
||||
/**
|
||||
* 显示手写字的View
|
||||
*
|
||||
* @author king
|
||||
* @since 2018-06-28
|
||||
*/
|
||||
public class HandWriteEditView extends AppCompatEditText {
|
||||
|
||||
private float lineHeight = 150f;
|
||||
boolean reLayout = false;
|
||||
private TextWatch textWatcher;
|
||||
|
||||
public HandWriteEditView(Context context) {
|
||||
super(context);
|
||||
init();
|
||||
}
|
||||
|
||||
public HandWriteEditView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public HandWriteEditView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
this.setTextIsSelectable(false);
|
||||
this.setImeOptions(EditorInfo.IME_FLAG_NO_EXTRACT_UI);
|
||||
this.setGravity(Gravity.START);
|
||||
|
||||
//禁止选择复制粘贴
|
||||
SystemUtil.disableCopyAndPaste(this);
|
||||
lineHeight = (float) DisplayUtil.dip2px(getContext(), 50f);
|
||||
addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
|
||||
float add = lineHeight;
|
||||
setLineSpacing(0f, 1f);
|
||||
setLineSpacing(add, 0);
|
||||
setIncludeFontPadding(false);
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
int top = (int) ((add - getTextSize()) * 0.5f);
|
||||
setPadding(getPaddingLeft(), top, getPaddingRight(), -top);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
if (textWatcher != null) {
|
||||
textWatcher.afterTextChanged(editable);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置行高
|
||||
*
|
||||
*/
|
||||
public void setLineHeight(float lineHeight) {
|
||||
this.lineHeight = lineHeight;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加手写文字
|
||||
*
|
||||
* @param srcBitmap 手写文字图片
|
||||
*/
|
||||
public Editable addBitmapToText(Bitmap srcBitmap) {
|
||||
if (srcBitmap == null) {
|
||||
return null;
|
||||
}
|
||||
SpannableString mSpan = new SpannableString("1");
|
||||
mSpan.setSpan(new ImageSpan(getContext(), srcBitmap), mSpan.length() - 1, mSpan.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
|
||||
|
||||
Editable editable = getText();
|
||||
//获取光标所在位置
|
||||
int index = getSelectionStart();
|
||||
editable.insert(index, mSpan);
|
||||
setText(editable);
|
||||
setSelection(index + mSpan.length());
|
||||
return editable;
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加空格
|
||||
*/
|
||||
public void addSpace(int fontSize) {
|
||||
int size = DisplayUtil.dip2px(getContext(), fontSize);
|
||||
ColorDrawable drawable = new ColorDrawable(Color.TRANSPARENT);
|
||||
Bitmap bitmap = Bitmap.createBitmap(fontSize, size, Bitmap.Config.ARGB_4444);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
drawable.draw(canvas);
|
||||
addBitmapToText(bitmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文字
|
||||
*/
|
||||
public Editable deleteBitmapFromText() {
|
||||
|
||||
Editable editable = getEditableText();
|
||||
int start = getSelectionStart();
|
||||
if (start == 0) {
|
||||
return null;
|
||||
}
|
||||
editable.delete(start - 1, start);
|
||||
return editable;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (!reLayout) {
|
||||
reLayout = true;
|
||||
setIncludeFontPadding(false);
|
||||
setGravity(Gravity.CENTER_VERTICAL);
|
||||
setLineSpacing(lineHeight, 0);
|
||||
int top = (int) ((lineHeight - getTextSize()) * 0.5f);
|
||||
setPadding(getPaddingLeft(), top, getPaddingRight(), -top);
|
||||
requestLayout();
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
|
||||
super.onMeasure(expandSpec, expandSpec);
|
||||
}
|
||||
|
||||
boolean canPaste() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean canCut() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean canCopy() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean canSelectAllText() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean canSelectText() {
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean textCanBeSelected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuggestionsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addTextWatcher(TextWatch textWatcher) {
|
||||
this.textWatcher = textWatcher;
|
||||
}
|
||||
|
||||
public interface TextWatch {
|
||||
void afterTextChanged(Editable var1);
|
||||
}
|
||||
}
|
@ -0,0 +1,172 @@
|
||||
package com.za.signature.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.PopupWindow;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.config.PenConfig;
|
||||
|
||||
/**
|
||||
* 画笔设置窗口
|
||||
*
|
||||
* @author king
|
||||
* @since 2018-06-04
|
||||
*/
|
||||
public class PaintSettingWindow extends PopupWindow {
|
||||
public static final String[] PEN_COLORS = new String[]{"#101010", "#027de9", "#0cba02", "#f9d403", "#ec041f"};
|
||||
public static final int[] PEN_SIZES = new int[]{5, 15, 20, 25, 30};
|
||||
|
||||
private final Context context;
|
||||
private CircleView lastSelectColorView;
|
||||
private CircleView lastSelectSizeView;
|
||||
private int selectColor;
|
||||
private View rootView;
|
||||
private OnSettingListener settingListener;
|
||||
|
||||
public PaintSettingWindow(Context context) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
init();
|
||||
}
|
||||
|
||||
private void init() {
|
||||
rootView = LayoutInflater.from(context).inflate(R.layout.sign_paint_setting, null);
|
||||
LinearLayout container = rootView.findViewById(R.id.color_container);
|
||||
for (int i = 0; i < container.getChildCount(); i++) {
|
||||
final int index = i;
|
||||
final CircleView circleView = (CircleView) container.getChildAt(i);
|
||||
|
||||
if (circleView.getPaintColor() == PenConfig.PAINT_COLOR) {
|
||||
circleView.showBorder(true);
|
||||
lastSelectColorView = circleView;
|
||||
}
|
||||
|
||||
circleView.setOnClickListener(v -> {
|
||||
if (lastSelectColorView != null) {
|
||||
lastSelectColorView.showBorder(false);
|
||||
}
|
||||
circleView.showBorder(true);
|
||||
selectColor = Color.parseColor(PEN_COLORS[index]);
|
||||
lastSelectColorView = circleView;
|
||||
PenConfig.PAINT_COLOR = selectColor;
|
||||
PenConfig.setPaintColor(context, selectColor);
|
||||
if (settingListener != null) {
|
||||
settingListener.onColorSetting(selectColor);
|
||||
}
|
||||
});
|
||||
}
|
||||
LinearLayout sizeContainer = rootView.findViewById(R.id.size_container);
|
||||
for (int i = 0; i < sizeContainer.getChildCount(); i++) {
|
||||
final int index = i;
|
||||
final CircleView circleView = (CircleView) sizeContainer.getChildAt(i);
|
||||
if (circleView.getRadiusLevel() == PenConfig.PAINT_SIZE_LEVEL) {
|
||||
circleView.showBorder(true);
|
||||
lastSelectSizeView = circleView;
|
||||
}
|
||||
circleView.setOnClickListener(v -> {
|
||||
if (lastSelectSizeView != null) {
|
||||
lastSelectSizeView.showBorder(false);
|
||||
}
|
||||
circleView.showBorder(true);
|
||||
lastSelectSizeView = circleView;
|
||||
PenConfig.PAINT_SIZE_LEVEL = index;
|
||||
PenConfig.savePaintTextLevel(context, index);
|
||||
if (settingListener != null) {
|
||||
settingListener.onSizeSetting(index);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
this.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
this.setContentView(rootView);
|
||||
this.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
|
||||
this.setFocusable(true);
|
||||
this.setOutsideTouchable(true);
|
||||
this.update();
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示在左上角
|
||||
*/
|
||||
public void popAtTopLeft() {
|
||||
View sv = rootView.findViewById(R.id.size_container);
|
||||
View cv = rootView.findViewById(R.id.color_container);
|
||||
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp.setMargins(20, 10, 20, 0);
|
||||
lp.gravity = Gravity.CENTER;
|
||||
sv.setLayoutParams(lp);
|
||||
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp1.setMargins(20, 0, 20, 40);
|
||||
lp1.gravity = Gravity.CENTER;
|
||||
cv.setLayoutParams(lp1);
|
||||
rootView.setBackgroundResource(R.mipmap.sign_top_left_pop_bg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示在右上角
|
||||
*/
|
||||
public void popAtTopRight() {
|
||||
View sv = rootView.findViewById(R.id.size_container);
|
||||
View cv = rootView.findViewById(R.id.color_container);
|
||||
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp.setMargins(20, 10, 20, 0);
|
||||
lp.gravity = Gravity.CENTER;
|
||||
sv.setLayoutParams(lp);
|
||||
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp1.setMargins(20, 0, 20, 40);
|
||||
lp1.gravity = Gravity.CENTER;
|
||||
cv.setLayoutParams(lp1);
|
||||
rootView.setBackgroundResource(R.mipmap.sign_top_right_pop_bg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示在右下角
|
||||
*/
|
||||
public void popAtBottomRight() {
|
||||
View sv = rootView.findViewById(R.id.size_container);
|
||||
View cv = rootView.findViewById(R.id.color_container);
|
||||
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp.setMargins(20, 40, 20, 0);
|
||||
lp.gravity = Gravity.CENTER;
|
||||
sv.setLayoutParams(lp);
|
||||
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp1.setMargins(20, 0, 20, 10);
|
||||
lp1.gravity = Gravity.CENTER;
|
||||
cv.setLayoutParams(lp1);
|
||||
rootView.setBackgroundResource(R.mipmap.sign_bottom_right_pop_bg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示在左边
|
||||
*/
|
||||
public void popAtLeft() {
|
||||
View sv = rootView.findViewById(R.id.size_container);
|
||||
View cv = rootView.findViewById(R.id.color_container);
|
||||
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp.setMargins(40, 20, 55, 0);
|
||||
lp.gravity = Gravity.CENTER;
|
||||
sv.setLayoutParams(lp);
|
||||
LinearLayout.LayoutParams lp1 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
||||
lp1.setMargins(40, 0, 55, 20);
|
||||
lp1.gravity = Gravity.CENTER;
|
||||
cv.setLayoutParams(lp1);
|
||||
rootView.setBackgroundResource(R.mipmap.sign_left_pop_bg);
|
||||
}
|
||||
|
||||
public interface OnSettingListener {
|
||||
void onColorSetting(int color);
|
||||
|
||||
void onSizeSetting(int index);
|
||||
}
|
||||
|
||||
public void setSettingListener(OnSettingListener settingListener) {
|
||||
this.settingListener = settingListener;
|
||||
}
|
||||
}
|
504
servicing/src/main/java/com/za/signature/view/PaintView.java
Normal file
504
servicing/src/main/java/com/za/signature/view/PaintView.java
Normal file
@ -0,0 +1,504 @@
|
||||
package com.za.signature.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.za.servicing.R;
|
||||
import com.za.signature.config.PenConfig;
|
||||
import com.za.signature.pen.BasePen;
|
||||
import com.za.signature.pen.Eraser;
|
||||
import com.za.signature.pen.SteelPen;
|
||||
import com.za.signature.util.BitmapUtil;
|
||||
import com.za.signature.util.DisplayUtil;
|
||||
import com.za.signature.util.StepOperator;
|
||||
|
||||
|
||||
/**
|
||||
* 手写画板
|
||||
*
|
||||
* @author king
|
||||
* @since 2018/5/4
|
||||
*/
|
||||
public class PaintView extends View {
|
||||
|
||||
public static final int TYPE_PEN = 0;
|
||||
public static final int TYPE_ERASER = 1;
|
||||
|
||||
private Paint mPaint;
|
||||
private Canvas mCanvas;
|
||||
private Bitmap mBitmap;
|
||||
private BasePen mStokeBrushPen;
|
||||
|
||||
/**
|
||||
* 是否允许写字
|
||||
*/
|
||||
private boolean isFingerEnable = true;
|
||||
/**
|
||||
* 是否橡皮擦模式
|
||||
*/
|
||||
private boolean isEraser = false;
|
||||
|
||||
/**
|
||||
* 是否有绘制
|
||||
*/
|
||||
private boolean hasDraw = false;
|
||||
|
||||
|
||||
/**
|
||||
* 画笔轨迹记录
|
||||
*/
|
||||
private StepOperator mStepOperation;
|
||||
|
||||
private StepCallback mCallback;
|
||||
|
||||
/**
|
||||
* 是否可以撤销
|
||||
*/
|
||||
private boolean mCanUndo;
|
||||
/**
|
||||
* 是否可以恢复
|
||||
*/
|
||||
private boolean mCanRedo;
|
||||
|
||||
private int mWidth;
|
||||
private int mHeight;
|
||||
|
||||
private boolean isDrawing = false;//是否正在绘制
|
||||
private int toolType = 0; //记录手写笔类型:触控笔/手指
|
||||
|
||||
private Eraser eraser;
|
||||
|
||||
public PaintView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public PaintView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public PaintView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化画板
|
||||
*
|
||||
* @param width 画板宽度
|
||||
* @param height 画板高度
|
||||
* @param path 初始图片路径
|
||||
*/
|
||||
public void init(int width, int height, String path) {
|
||||
this.mWidth = width;
|
||||
this.mHeight = height;
|
||||
|
||||
mBitmap = Bitmap.createBitmap(mWidth, mHeight, Bitmap.Config.ARGB_4444);
|
||||
mStokeBrushPen = new SteelPen();
|
||||
|
||||
initPaint();
|
||||
initCanvas();
|
||||
|
||||
mStepOperation = new StepOperator();
|
||||
if (!TextUtils.isEmpty(path)) {
|
||||
Bitmap bitmap = BitmapFactory.decodeFile(path);
|
||||
resize(bitmap, mWidth, mHeight);
|
||||
} else {
|
||||
mStepOperation.addBitmap(mBitmap);
|
||||
}
|
||||
//橡皮擦
|
||||
eraser = new Eraser(getResources().getDimensionPixelSize(R.dimen.sign_eraser_size));
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始画笔设置
|
||||
*/
|
||||
private void initPaint() {
|
||||
int strokeWidth = DisplayUtil.dip2px(getContext(), PaintSettingWindow.PEN_SIZES[PenConfig.PAINT_SIZE_LEVEL]);
|
||||
mPaint = new Paint();
|
||||
mPaint.setColor(PenConfig.PAINT_COLOR);
|
||||
mPaint.setStrokeWidth(strokeWidth);
|
||||
mPaint.setStyle(Paint.Style.STROKE);
|
||||
mPaint.setAlpha(0xFF);
|
||||
mPaint.setAntiAlias(true);
|
||||
mPaint.setStrokeMiter(1.0f);
|
||||
mStokeBrushPen.setPaint(mPaint);
|
||||
}
|
||||
|
||||
private void initCanvas() {
|
||||
mCanvas = new Canvas(mBitmap);
|
||||
//设置画布的背景色为透明
|
||||
mCanvas.drawColor(Color.TRANSPARENT);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onDraw(@NonNull Canvas canvas) {
|
||||
canvas.drawBitmap(mBitmap, 0, 0, mPaint);
|
||||
if (!isEraser) {
|
||||
mStokeBrushPen.draw(canvas);
|
||||
}
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean dispatchTouchEvent(MotionEvent ev) {
|
||||
getParent().requestDisallowInterceptTouchEvent(true);
|
||||
return super.dispatchTouchEvent(ev);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
|
||||
toolType = event.getToolType(event.getActionIndex());
|
||||
if (!isFingerEnable && toolType != MotionEvent.TOOL_TYPE_STYLUS) {
|
||||
return false;
|
||||
}
|
||||
if (isEraser) {
|
||||
eraser.handleEraserEvent(event, mCanvas);
|
||||
} else {
|
||||
mStokeBrushPen.onTouchEvent(event, mCanvas);
|
||||
}
|
||||
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
isDrawing = false;
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
hasDraw = true;
|
||||
mCanUndo = true;
|
||||
isDrawing = true;
|
||||
break;
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
isDrawing = false;
|
||||
break;
|
||||
case MotionEvent.ACTION_UP:
|
||||
if (mStepOperation != null && isDrawing) {
|
||||
mStepOperation.addBitmap(mBitmap);
|
||||
}
|
||||
mCanUndo = !mStepOperation.currentIsFirst();
|
||||
mCanRedo = !mStepOperation.currentIsLast();
|
||||
if (mCallback != null) {
|
||||
mCallback.onOperateStatusChanged();
|
||||
}
|
||||
isDrawing = false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 判断是否有绘制内容在画布上
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return !hasDraw;
|
||||
}
|
||||
|
||||
/**
|
||||
* 撤销
|
||||
*/
|
||||
public void undo() {
|
||||
|
||||
if (mStepOperation == null || !mCanUndo) {
|
||||
return;
|
||||
}
|
||||
if (!mStepOperation.currentIsFirst()) {
|
||||
mCanUndo = true;
|
||||
mStepOperation.undo(mBitmap);
|
||||
hasDraw = true;
|
||||
invalidate();
|
||||
|
||||
if (mStepOperation.currentIsFirst()) {
|
||||
mCanUndo = false;
|
||||
hasDraw = false;
|
||||
}
|
||||
} else {
|
||||
mCanUndo = false;
|
||||
hasDraw = false;
|
||||
}
|
||||
if (!mStepOperation.currentIsLast()) {
|
||||
mCanRedo = true;
|
||||
}
|
||||
if (mCallback != null) {
|
||||
mCallback.onOperateStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复
|
||||
*/
|
||||
public void redo() {
|
||||
if (mStepOperation == null || !mCanRedo) {
|
||||
return;
|
||||
}
|
||||
if (!mStepOperation.currentIsLast()) {
|
||||
mCanRedo = true;
|
||||
mStepOperation.redo(mBitmap);
|
||||
hasDraw = true;
|
||||
invalidate();
|
||||
if (mStepOperation.currentIsLast()) {
|
||||
mCanRedo = false;
|
||||
}
|
||||
} else {
|
||||
mCanRedo = false;
|
||||
}
|
||||
if (!mStepOperation.currentIsFirst()) {
|
||||
mCanUndo = true;
|
||||
}
|
||||
if (mCallback != null) {
|
||||
mCallback.onOperateStatusChanged();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除画布,记得清除点的集合
|
||||
*/
|
||||
public void reset() {
|
||||
mBitmap.eraseColor(Color.TRANSPARENT);
|
||||
hasDraw = false;
|
||||
mStokeBrushPen.clear();
|
||||
if (mStepOperation != null) {
|
||||
mStepOperation.reset();
|
||||
mStepOperation.addBitmap(mBitmap);
|
||||
}
|
||||
mCanRedo = false;
|
||||
mCanUndo = false;
|
||||
if (mCallback != null) {
|
||||
mCallback.onOperateStatusChanged();
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
public void release() {
|
||||
destroyDrawingCache();
|
||||
if (mBitmap != null) {
|
||||
mBitmap.recycle();
|
||||
mBitmap = null;
|
||||
}
|
||||
if (mStepOperation != null) {
|
||||
mStepOperation.freeBitmaps();
|
||||
mStepOperation = null;
|
||||
}
|
||||
}
|
||||
|
||||
public interface StepCallback {
|
||||
/**
|
||||
* 操作变更
|
||||
*/
|
||||
void onOperateStatusChanged();
|
||||
}
|
||||
|
||||
public void setStepCallback(StepCallback callback) {
|
||||
this.mCallback = callback;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置画笔样式
|
||||
*
|
||||
*/
|
||||
public void setPenType(int penType) {
|
||||
isEraser = false;
|
||||
switch (penType) {
|
||||
case TYPE_PEN:
|
||||
mStokeBrushPen = new SteelPen();
|
||||
break;
|
||||
case TYPE_ERASER:
|
||||
isEraser = true;
|
||||
break;
|
||||
}
|
||||
//设置
|
||||
if (mStokeBrushPen.isNullPaint()) {
|
||||
mStokeBrushPen.setPaint(mPaint);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置画笔大小
|
||||
*
|
||||
* @param width 大小
|
||||
*/
|
||||
public void setPaintWidth(int width) {
|
||||
if (mPaint != null) {
|
||||
mPaint.setStrokeWidth(DisplayUtil.dip2px(getContext(), width));
|
||||
// eraser.setPaintWidth(DisplayUtil.dip2px(getContext(), width));
|
||||
mStokeBrushPen.setPaint(mPaint);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置画笔颜色
|
||||
*
|
||||
* @param color 颜色
|
||||
*/
|
||||
public void setPaintColor(int color) {
|
||||
if (mPaint != null) {
|
||||
mPaint.setColor(color);
|
||||
mStokeBrushPen.setPaint(mPaint);
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建Bitmap
|
||||
*
|
||||
* @return 所绘制的bitmap
|
||||
*/
|
||||
public Bitmap buildAreaBitmap(boolean isCrop) {
|
||||
if (!hasDraw) {
|
||||
return null;
|
||||
}
|
||||
Bitmap result;
|
||||
if (isCrop) {
|
||||
result = BitmapUtil.clearBlank(mBitmap, 50, Color.TRANSPARENT);
|
||||
} else {
|
||||
result = mBitmap;
|
||||
}
|
||||
destroyDrawingCache();
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isFingerEnable() {
|
||||
return isFingerEnable;
|
||||
}
|
||||
|
||||
public void setFingerEnable(boolean fingerEnable) {
|
||||
isFingerEnable = fingerEnable;
|
||||
}
|
||||
|
||||
public boolean isEraser() {
|
||||
return isEraser;
|
||||
}
|
||||
|
||||
public boolean canUndo() {
|
||||
return mCanUndo;
|
||||
}
|
||||
|
||||
public boolean canRedo() {
|
||||
return mCanRedo;
|
||||
}
|
||||
|
||||
public Bitmap getBitmap() {
|
||||
return mBitmap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 图片大小调整适配画布宽高
|
||||
*
|
||||
* @param bitmap 源图
|
||||
* @param width 新宽度
|
||||
* @param height 新高度
|
||||
*/
|
||||
public void resize(Bitmap bitmap, int width, int height) {
|
||||
|
||||
if (mBitmap != null) {
|
||||
if (width >= this.mWidth) {
|
||||
height = width * mBitmap.getHeight() / mBitmap.getWidth();
|
||||
}
|
||||
this.mWidth = width;
|
||||
this.mHeight = height;
|
||||
|
||||
mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
|
||||
restoreLastBitmap(bitmap, mBitmap);
|
||||
initCanvas();
|
||||
if (mStepOperation != null) {
|
||||
mStepOperation.addBitmap(mBitmap);
|
||||
}
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 恢复最后画的bitmap
|
||||
*
|
||||
* @param srcBitmap 最后的bitmap
|
||||
* @param newBitmap 新bitmap
|
||||
*/
|
||||
private void restoreLastBitmap(Bitmap srcBitmap, Bitmap newBitmap) {
|
||||
try {
|
||||
if (srcBitmap == null || srcBitmap.isRecycled()) {
|
||||
return;
|
||||
}
|
||||
srcBitmap = BitmapUtil.zoomImg(srcBitmap, newBitmap.getWidth());
|
||||
//缩放后如果还是超出新图宽高,继续缩放
|
||||
if (srcBitmap.getWidth() > newBitmap.getWidth() || srcBitmap.getHeight() > newBitmap.getHeight()) {
|
||||
srcBitmap = BitmapUtil.zoomImage(srcBitmap, newBitmap.getWidth(), newBitmap.getHeight());
|
||||
}
|
||||
//保存所有的像素的数组,图片宽×高
|
||||
int[] pixels = new int[srcBitmap.getWidth() * srcBitmap.getHeight()];
|
||||
srcBitmap.getPixels(pixels, 0, srcBitmap.getWidth(), 0, 0, srcBitmap.getWidth(), srcBitmap.getHeight());
|
||||
newBitmap.setPixels(pixels, 0, srcBitmap.getWidth(), 0, 0,
|
||||
srcBitmap.getWidth(), srcBitmap.getHeight());
|
||||
} catch (OutOfMemoryError e) {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int width = onMeasureR(0, widthMeasureSpec);
|
||||
int height = onMeasureR(1, heightMeasureSpec);
|
||||
setMeasuredDimension(width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算控件宽高
|
||||
*/
|
||||
public int onMeasureR(int attr, int oldMeasure) {
|
||||
|
||||
int newSize = 0;
|
||||
int mode = MeasureSpec.getMode(oldMeasure);
|
||||
int oldSize = MeasureSpec.getSize(oldMeasure);
|
||||
|
||||
switch (mode) {
|
||||
case MeasureSpec.EXACTLY:
|
||||
newSize = oldSize;
|
||||
break;
|
||||
case MeasureSpec.AT_MOST:
|
||||
case MeasureSpec.UNSPECIFIED:
|
||||
|
||||
float value = mWidth;
|
||||
|
||||
if (attr == 0) {
|
||||
if (mBitmap != null) {
|
||||
value = mBitmap.getWidth();
|
||||
}
|
||||
// 控件的宽度
|
||||
newSize = (int) (getPaddingLeft() + value + getPaddingRight());
|
||||
|
||||
} else if (attr == 1) {
|
||||
value = mHeight;
|
||||
if (mBitmap != null) {
|
||||
value = mBitmap.getHeight();
|
||||
}
|
||||
// 控件的高度
|
||||
newSize = (int) (getPaddingTop() + value + getPaddingBottom());
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return newSize;
|
||||
}
|
||||
|
||||
public Bitmap getLastBitmap() {
|
||||
return mBitmap;
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user