Android 自定义手势键盘
自定义手势键盘
手势键盘有三种状态,初始状态、点击状态和错误状态,分别以下列三个图片显示。
2. 数据类CircleArea
CircleArea
类用来记录手势键盘的信息。
static class CircleArea {
float x, y; // 圆心X
float radius; // 圆半径
int mValue; // 值
public CircleArea(float x, float y, float radius, int value) {
this.x = x;
this.y = y;
this.radius = radius;
this.mValue = value;
}
public CircleArea moveTo(float dx, float dy, int value) {
return new CircleArea(x + dx, y + dy, radius, value);
}
// 该点是否在圆内
public boolean contain(float x, float y) {
return Math.sqrt(Math.pow(this.x - x, 2) + Math.pow(this.y - y, 2)) < radius;
}
public int getValue() {
return mValue;
}
}
3. 手势键盘GestureView
-
初始化,在构造函数里面初始化图片和画笔,在
onMeasure(int, int)
计算手势点的位置。public class GestureView extends View { private static final int INVALID_COUNT = 4; enum Status { Normal, Error } private Bitmap mNormalBmp, mClickBmp, mErrorBmp; private int mNormalColor, mErrorColor; private Paint mBitmapPaint, mLinePaint; private List<CircleArea> mTotalCircles = new ArrayList<>(); private List<CircleArea> mLinkCircle = new ArrayList<>(); private PointF mCurrentPoint = null; private Status mStatus = Status.Normal; public GestureView(Context context) { this(context, null); } public GestureView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mNormalBmp = BitmapFactory.decodeResource(getResources(), R.drawable.gesture_circle_normal); mClickBmp = BitmapFactory.decodeResource(getResources(), R.drawable.gesture_circle_click); mErrorBmp = BitmapFactory.decodeResource(getResources(), R.drawable.gesture_circle_error); mNormalColor = getResources().getColor(R.color.gesture_normal_line); mErrorColor = getResources().getColor(R.color.gesture_error_line); mBitmapPaint = new Paint(); mBitmapPaint.setFilterBitmap(true); mLinePaint = new Paint(); mLinePaint.setStrokeWidth(10); mLinePaint.setAntiAlias(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int measuredWidth = getMeasuredWidth(); int measuredHeight = getMeasuredHeight(); int squareWidth = Math.min(measuredWidth, measuredHeight); // 界面被3等分,每个手势占1/6 float radius = squareWidth / 12.0f; float startX = (measuredWidth - squareWidth) / 2.0f; float startY = (measuredHeight - squareWidth) / 2.0f; mTotalCircles.clear(); CircleArea circleArea = new CircleArea(startX + radius*2, startY + radius*2, radius, 1); mTotalCircles.add(circleArea); mTotalCircles.add(circleArea.moveTo(radius*4, 0, 2)); mTotalCircles.add(circleArea.moveTo(radius*8, 0, 3)); mTotalCircles.add(circleArea.moveTo(0, radius*4, 4)); mTotalCircles.add(circleArea.moveTo(radius*4, radius*4, 5)); mTotalCircles.add(circleArea.moveTo(radius*8, radius*4, 6)); mTotalCircles.add(circleArea.moveTo(0, radius*8, 7)); mTotalCircles.add(circleArea.moveTo(radius*4, radius*8, 8)); mTotalCircles.add(circleArea.moveTo(radius*8, radius*8, 9)); int width = (int)(radius * 2); mNormalBmp = Bitmap.createScaledBitmap(mNormalBmp, width, width, false); mClickBmp = Bitmap.createScaledBitmap(mClickBmp, width, width, false); mErrorBmp = Bitmap.createScaledBitmap(mErrorBmp, width, width, false); } }
-
onTouchEvent(MotionEvent)
方法捕捉手势@Override public boolean onTouchEvent(MotionEvent event) { // 只有在正常状态下,触摸才生效 if (mStatus == Status.Normal) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: // 记录手势 moveTo(event.getX(), event.getY()); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: moveTo(event.getX(), event.getY()); // 如果小于有效数量 if (mLinkCircle.size() < INVALID_COUNT) { Toast.makeText(getContext(), "InvalidCount " + mLinkCircle.size(), Toast.LENGTH_LONG).show(); // 错误状态,界面不可点 mStatus = Status.Error; // 2秒后清空 postDelayed(new Runnable() { @Override public void run() { clearCircle(); postInvalidate(); } }, 2000); } else { Toast.makeText(getContext(), "Success " + getLinkText(), Toast.LENGTH_LONG).show(); clearCircle(); } break; } postInvalidate(); } return true; } private void moveTo(float x, float y) { // 当前触摸点 mCurrentPoint = new PointF(x, y); // 如果不在选择列表中,加入选择列表 for (CircleArea circle : mTotalCircles) { if (circle.contain(x, y) && !mLinkCircle.contains(circle)) { if (!mLinkCircle.contains(circle)) { mLinkCircle.add(circle); } return; } } } private String getLinkText() { String text = ""; for(CircleArea circle : mLinkCircle) { text += circle.getValue(); } return text; } private void clearCircle() { mLinkCircle.clear(); mCurrentPoint = null; mStatus = Status.Normal; }
-
onDraw(Canvas)
方法绘制手势@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(getResources().getColor(R.color.black)); // 如果选择列表不为空 if (mLinkCircle.size() > 0) { // 设置连接线的颜色 if (mStatus == Status.Normal) { mLinePaint.setColor(mNormalColor); } else { mLinePaint.setColor(mErrorColor); } // 手势键盘之间的连接线 CircleArea lastCircle = mLinkCircle.get(0); for (int i = 1; i < mLinkCircle.size(); i++) { CircleArea circle = mLinkCircle.get(i); canvas.drawLine(lastCircle.x, lastCircle.y, circle.x, circle.y, mLinePaint); lastCircle = circle; } // 手势键盘和当前点之间的连接线 if (mCurrentPoint != null && mStatus == Status.Normal) { canvas.drawLine(lastCircle.x, lastCircle.y, mCurrentPoint.x, mCurrentPoint.y, mLinePaint); } } for (CircleArea circle : mTotalCircles) { drawCircle(canvas, circle); } } private void drawCircle(Canvas canvas, CircleArea circle) { Bitmap circleBitmap = mNormalBmp; if (mLinkCircle.contains(circle)) { if (mStatus == Status.Normal) { circleBitmap = mClickBmp; } else { circleBitmap = mErrorBmp; } } // 不同状态下绘制不同的图片。 canvas.drawBitmap(circleBitmap, circle.x - circle.radius, circle.y - circle.radius, mBitmapPaint); }