因为项目中有个需求需要实现一个自定义键盘控件。起初通过layout使用组合控件的形式进行实现,后来看了感觉实在无法忍受于是就打算继承view然后通过绘制的方式进行实现。
git地址: https://gitee.com/zhengdd/MyWidget
具体思路如下
1 设置一些自定义属性如背景颜色,数字颜色,数字大小等,实现使用控件时候可自定义配置
2 通过获取控件宽高,计算控件中分割线,按钮等位置坐标。
3 通过背景颜色,线条,特殊单元格背景(如按下状态按钮背景),按键信息(数字或者图标)的顺序进行界面绘制。
4 重写onTouchEvent方法中,根据事件刷新界面并计算位置返回操作状态。
具体代码如下
public class NumKeyBoardView extends View {
private float scale = 0;
private int view_bg_color;
private int line_color;
private int btn_press_bg_color;
private int btn_text_color;
private ColorStateList csl_next_bg_color;
private ColorStateList csl_next_text_color;
private int next_bg_color;
private int next_text_color;
private static final int[] NEXT_STATE_ENABLE = {R.attr.next_enable};
private static final int[] NEXT_STATE_DISABLE = {-R.attr.next_enable};
private Drawable back_icon;
private int text_size= 30;
private boolean next_enable= true;
private String next_text= "完成";
private String[] numValues = new String[]{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
"delete", "next", "null", "null"};
private Rect[] rects = new Rect[14];
private float[] pts;
private Rect pressedRect;
private Bitmap mBpDelete;
private float mBpWidth;
private float mBpHeight;
//控件宽度
private int _witth;
//控件高度
private int _height;
//单元格宽度
private int _cell_width;
//单元格高度
private int _cell_height;
private int linewidth = 1;
private int textwidth = 1;
private boolean isinitDate = false;
public Paint mPaint;
private int clickX = 0;
private int clickY = 0;
private String number = null;
public boolean isPressed = false;
private OnKeyBoardClickListener mListener;
public NumKeyBoardView(Context context) {
this(context, null);
}
public NumKeyBoardView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public NumKeyBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.NumKeyBoardView);
next_enable = array.getBoolean(R.styleable.NumKeyBoardView_next_enable, true);
csl_next_bg_color = array.getColorStateList(R.styleable.NumKeyBoardView_next_background);
upNextEnable();
init(array);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
this._witth = MeasureSpec.getSize(widthMeasureSpec);
this._height = MeasureSpec.getSize(heightMeasureSpec);
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!isinitDate) {
initdata();
}
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
drawBackground(canvas);
drawLine(canvas);
drawCell(canvas);
drawContent(canvas);
}
private void init(TypedArray typedArray) {
if (typedArray != null) {
view_bg_color = typedArray.getColor(R.styleable.NumKeyBoardView_view_background,
getResources().getColor(R.color.dong_color_white));
line_color = typedArray.getColor(R.styleable.NumKeyBoardView_line_color,
getResources().getColor(R.color.dong_color_line_gray));
btn_press_bg_color = typedArray.getColor(R.styleable.NumKeyBoardView_background_btn_press,
getResources().getColor(R.color.dong_color_btn_press_gray));
btn_text_color = typedArray.getColor(R.styleable
.NumKeyBoardView_text_color_btn, getResources().getColor(R.color.dong_cokor_btn_text));
back_icon = typedArray.getDrawable(R.styleable.NumKeyBoardView_icon_back);
if (back_icon == null) {
back_icon = getResources().getDrawable(R.drawable.dong_widget_icon_num_back);
}
next_text = typedArray.getString(R.styleable.NumKeyBoardView_text_next);
if (TextUtils.isEmpty(next_text)) {
next_text = "完成";
}
next_enable = typedArray.getBoolean(R.styleable.NumKeyBoardView_next_enable, true);
text_size = typedArray.getDimensionPixelSize(R.styleable.NumKeyBoardView_text_size,
dip2px(20f));
csl_next_bg_color = typedArray.getColorStateList(R.styleable.NumKeyBoardView_next_background);
csl_next_text_color = typedArray.getColorStateList(R.styleable.NumKeyBoardView_text_next);
upNextEnable();
} else {
view_bg_color = getResources().getColor(R.color.dong_color_white);
line_color = getResources().getColor(R.color.dong_color_line_gray);
btn_press_bg_color = getResources().getColor(R.color.dong_color_btn_press_gray);
btn_text_color = getResources().getColor(R.color.dong_color_font_gray);
if (next_enable) {
next_bg_color = getResources().getColor(R.color.dong_color_next_bg_enable);
next_text_color = getResources().getColor(R.color.dong_color_next_text_enable);
} else {
next_bg_color = getResources().getColor(R.color.dong_color_next_bg_disable);
next_text_color = getResources().getColor(R.color.dong_color_next_text_disable);
}
back_icon = getResources().getDrawable(R.drawable.dong_widget_icon_num_back);
upNextEnable();
}
}
private void initdata() {
isinitDate = true;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeWidth(linewidth);
_cell_width = _witth / 4;
_cell_height = _height / 4;
mBpDelete = ((BitmapDrawable) back_icon).getBitmap();
mBpWidth = mBpDelete.getWidth();
mBpHeight = mBpDelete.getHeight();
//10条分割线数组
pts = new float[]{
0, 0, _witth, 0,
0, _cell_height, _cell_width * 3, _cell_height,
0, _cell_height * 2, _witth, _cell_height * 2,
0, _cell_height * 3, _cell_width * 3, _cell_height * 3,
0, _height, _witth, _height,
0, 0, 0, _height,
_cell_width, 0, _cell_width, _height,
_cell_width * 2, 0, _cell_width * 2, _height,
_cell_width * 3, 0, _cell_width * 3, _height,
_witth, 0, _witth, _height
};
rects[0] = new Rect(_cell_width, _cell_height * 3, _cell_width * 2, _height);
rects[1] = new Rect(0, 0, _cell_width, _cell_height);
rects[2] = new Rect(_cell_width, 0, _cell_width * 2, _cell_height);
rects[3] = new Rect(_cell_width * 2, 0, _cell_width * 3, _cell_height);
rects[4] = new Rect(0, _cell_height, _cell_width, _cell_height * 2);
rects[5] = new Rect(_cell_width, _cell_height, _cell_width * 2, _cell_height * 2);
rects[6] = new Rect(_cell_width * 2, _cell_height, _cell_width * 3, _cell_height * 2);
rects[7] = new Rect(0, _cell_height * 2, _cell_width, _cell_height * 3);
rects[8] = new Rect(_cell_width, _cell_height * 2, _cell_width * 2, _cell_height * 3);
rects[9] = new Rect(_cell_width * 2, _cell_height * 2, _cell_width * 3, _cell_height * 3);
rects[10] = new Rect(_cell_width * 3, 0, _witth, _cell_height * 2);
rects[11] = new Rect(_cell_width * 3, _cell_height * 2, _witth, _height);
//默认空按钮位置
rects[12] = new Rect(0, _cell_height * 3, _cell_width, _height);
rects[13] = new Rect(_cell_width * 2, _cell_height * 3, _cell_width * 3, _height);
}
/**
* 绘制底色
*
* @param canvas
*/
private void drawBackground(Canvas canvas) {
canvas.drawColor(view_bg_color);
}
/**
* 绘制线条
*
* @param canvas
*/
private void drawLine(Canvas canvas) {
mPaint.setColor(line_color);
mPaint.setStrokeWidth(linewidth);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawLines(pts, mPaint);
}
/**
* 绘制点击的按钮颜色 和 下一步按钮背景色
*
* @param canvas
*/
private void drawCell(Canvas canvas) {
if (isPressed && pressedRect != null) {
mPaint.setColor(btn_press_bg_color);
mPaint.setStrokeWidth(linewidth);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawRect(pressedRect, mPaint);
}
mPaint.setColor(next_bg_color);
mPaint.setStrokeWidth(linewidth);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawRect(rects[11], mPaint);
}
/**
* 绘制文字及icon颜色
*
* @param canvas
*/
private void drawContent(Canvas canvas) {
mPaint.setColor(btn_text_color);
mPaint.setStrokeWidth(textwidth);
mPaint.setTextSize(text_size);
mPaint.setTextAlign(Paint.Align.CENTER);
for (int i = 0; i < 10; i++) {
drawTextCenter(rects[i], mPaint, numValues[i], canvas);
}
mPaint.setColor(next_text_color);
mPaint.setStrokeWidth(textwidth);
mPaint.setTextSize(text_size);
mPaint.setTextAlign(Paint.Align.CENTER);
drawTextCenter(rects[11], mPaint, next_text, canvas);
drawBitmapCenter(rects[10], mPaint, mBpDelete, canvas);
}
private void drawBitmapCenter(Rect rect, Paint paint, Bitmap bitmap, Canvas canvas) {
float x = rect.centerX() - (mBpWidth / 2);
float y = rect.centerY() - (mBpHeight / 2);
canvas.drawBitmap(bitmap, x, y, paint);
}
private void drawTextCenter(Rect rect, Paint textPaint, String text, Canvas canvas) {
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
float top = fontMetrics.top;//为基线到字体上边框的距离,即上图中的top
float bottom = fontMetrics.bottom;//为基线到字体下边框的距离,即上图中的bottom
int baseLineY = (int) (rect.centerY() - top / 2 - bottom / 2);//基线中间点的y轴计算公式
canvas.drawText(text, rect.centerX(), baseLineY, textPaint);
}
/**
* 设置下一步操作控制名称
*
* @param nexttext
*/
public void setNextText(String nexttext) {
if (!TextUtils.isEmpty(nexttext)) {
this.next_text = nexttext;
invalidate();
}
}
public void setNextEnable(boolean enable) {
if (next_enable != enable) {
next_enable = enable;
refreshDrawableState();
upNextEnable();
invalidate();
}
}
private void upNextEnable() {
if (csl_next_bg_color != null) {
next_bg_color = csl_next_bg_color.getColorForState(getDrawableState(), 0);
} else {
if (next_enable) {
next_bg_color = getResources().getColor(R.color.dong_color_next_bg_enable);
} else {
next_bg_color = getResources().getColor(R.color.dong_color_next_bg_disable);
}
}
if (csl_next_text_color != null) {
next_text_color = csl_next_text_color.getColorForState(getDrawableState(), 0);
} else {
if (next_enable) {
next_text_color = getResources().getColor(R.color.dong_color_next_text_enable);
} else {
next_text_color = getResources().getColor(R.color.dong_color_next_text_disable);
}
}
}
public void setOnKeyBoardClickListener(@Nullable OnKeyBoardClickListener l) {
if (l != null) {
mListener = l;
}
}
private int dip2px(float dpValue) {
/*当前view的缩放比例*/
if (0 == scale) {
scale = this.getContext().getResources().getDisplayMetrics().density;
}
return (int) (dpValue * scale + 0.5f);
}
public interface OnKeyBoardClickListener {
void onOutPut(String num);
void onDetele();
void onNextClick();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
clickX = (int) event.getX();
clickY = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: //按下
isPressed = true;
handleDown();
invalidate();
return true;
case MotionEvent.ACTION_UP: //弹起
if (mListener != null) {
switch (number) {
case "null":
break;
case "delete":
mListener.onDetele();
break;
case "next":
if (next_enable) {
mListener.onNextClick();
}
break;
default:
mListener.onOutPut(number);
break;
}
}
setDefault();
invalidate();
return true;
case MotionEvent.ACTION_CANCEL: //取消
//恢复默认值
setDefault();
return true;
default:
break;
}
return false;
}
private void handleDown() {
for (int i = 0; i < numValues.length; i++) {
if (rects[i].contains(clickX, clickY)) {
number = numValues[i];
pressedRect = rects[i];
switch (number) {
case "null":
case "delete":
case "next":
pressedRect = null;
break;
default:
break;
}
return;
}
}
}
private void setDefault() {
clickX = 0;
clickY = 0;
number = null;
pressedRect = null;
isPressed = false;
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
int drawableState[] = super.onCreateDrawableState(extraSpace + 1);
if (next_enable) {
mergeDrawableStates(drawableState, NEXT_STATE_ENABLE);
} else {
mergeDrawableStates(drawableState, NEXT_STATE_DISABLE);
}
return drawableState;
}
}