package cn.mutils.app.ui.pattern;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import java.util.ArrayList;
import cn.mutils.app.util.AppUtil;
@SuppressLint("ClickableViewAccessibility")
public class PatternCanvas extends View {
public interface OnPasswordDrawnListener {
public void onPasswordDrawn(PatternCanvas canvas, String password);
}
private Paint mCircleStonePaint;
private Paint mCircleEarthPaint;
private Paint mCircleFiredPaint;
private Paint mCircleGlowPaint;
private Paint mDotFiredPaint;
private Paint mDotGlowPaint;
private Paint mLinePaint;
private static class Circle {
public boolean mUsed;
public Point mCenter;
public Rect mOuterBound;
}
private int mCircleInnerRadius;
private int mCircleOuterRadius;
private int mDotInnerRadius;
private int mDotOuterRadius;
private Circle[] mCircles = new Circle[0];
private boolean mDrawingStarted = false;
private int mX, mY;
private int mFireColor = 0xffff5f;
private int mStoneColor = 0xc0c0c0;
private int mEarthColor = 0xffffff;
private ArrayList<Integer> mPassword = new ArrayList<Integer>();
private OnPasswordDrawnListener mOnPasswordDrawnListener;
public void setOnPasswordDrawnListener(OnPasswordDrawnListener listener) {
mOnPasswordDrawnListener = listener;
}
public PatternCanvas(Context context) {
super(context);
init();
}
public PatternCanvas(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
Paint paint = new Paint();
paint.setAntiAlias(true);
mCircleStonePaint = new Paint(paint);
mCircleStonePaint.setColor(0xff000000 | mStoneColor);
mCircleStonePaint.setStyle(Paint.Style.STROKE);
mCircleStonePaint.setStrokeWidth(AppUtil.getRawSize(getContext(), TypedValue.COMPLEX_UNIT_DIP, 1));
mCircleEarthPaint = new Paint(paint);
mCircleEarthPaint.setColor(0x10000000 | mEarthColor);
mCircleEarthPaint.setStyle(Paint.Style.FILL);
mCircleFiredPaint = new Paint(mCircleStonePaint);
mCircleFiredPaint.setColor(0xff000000 | mFireColor);
mCircleGlowPaint = new Paint(mCircleStonePaint);
mCircleGlowPaint.setColor(0x40000000 | mFireColor);
mCircleGlowPaint.setStrokeWidth(AppUtil.getRawSize(getContext(), TypedValue.COMPLEX_UNIT_DIP, 6));
mDotFiredPaint = new Paint(paint);
mDotFiredPaint.setColor(0xff000000 | mFireColor);
mDotFiredPaint.setStyle(Paint.Style.FILL);
mDotGlowPaint = new Paint(mDotFiredPaint);
mDotGlowPaint.setColor(0x40000000 | mFireColor);
mLinePaint = new Paint(paint);
mLinePaint.setColor(0x80000000 | mFireColor);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setStrokeWidth(AppUtil.getRawSize(getContext(), TypedValue.COMPLEX_UNIT_DIP, 8));
}
private void initCircles(double w, double h) {
int cw = (int) Math.floor(Math.min(Math.floor(w / 3.0), Math.floor(h / 3.0)));
mCircleOuterRadius = (int) Math.floor((double) cw * 0.3);
mCircleInnerRadius = mCircleOuterRadius - (int) AppUtil.getRawSize(getContext(), TypedValue.COMPLEX_UNIT_DIP, 4);
mDotOuterRadius = (int) Math.floor((double) cw * 0.15);
mDotInnerRadius = mDotOuterRadius - (int) AppUtil.getRawSize(getContext(), TypedValue.COMPLEX_UNIT_DIP, 4);
int x0, y0, x3, y3;
if (h > w) {
x0 = 0;
y0 = (int) Math.floor((h - w) / 2.0);
x3 = (int) Math.floor(w);
y3 = y0 + (int) Math.floor(cw * 3.0);
} else {
x0 = (int) Math.floor((w - h) / 2.0);
y0 = 0;
x3 = x0 + (int) Math.floor(cw * 3.0);
y3 = (int) Math.floor(h);
}
int x1 = x0 + cw;
int x2 = x3 - cw;
int y1 = y0 + cw;
int y2 = y3 - cw;
mCircles = new Circle[9];
for (int i = 0; i < mCircles.length; i++)
mCircles[i] = new Circle();
mCircles[0].mOuterBound = new Rect(x0, y0, x1, y1);
mCircles[1].mOuterBound = new Rect(x0, y1, x1, y2);
mCircles[2].mOuterBound = new Rect(x0, y2, x1, y3);
mCircles[3].mOuterBound = new Rect(x1, y0, x2, y1);
mCircles[4].mOuterBound = new Rect(x1, y1, x2, y2);
mCircles[5].mOuterBound = new Rect(x1, y2, x2, y3);
mCircles[6].mOuterBound = new Rect(x2, y0, x3, y1);
mCircles[7].mOuterBound = new Rect(x2, y1, x3, y2);
mCircles[8].mOuterBound = new Rect(x2, y2, x3, y3);
for (int i = 0; i < mCircles.length; i++)
mCircles[i].mCenter = new Point(mCircles[i].mOuterBound.centerX(), mCircles[i].mOuterBound.centerY());
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
initCircles(right - left, bottom - top);
}
@Override
protected void onDraw(Canvas canvas) {
int i, j, m, n, x, y, x1, y1;
canvas.drawColor(Color.TRANSPARENT);
// Circle.
for (i = 0; i < mCircles.length; i++) {
x = mCircles[i].mCenter.x;
y = mCircles[i].mCenter.y;
canvas.drawCircle(x, y, mCircleInnerRadius, mCircleEarthPaint);
canvas.drawCircle(x, y, mCircleOuterRadius, mCircleStonePaint);
}
for (i = 0, j = -1; i < mPassword.size(); i++, j++) {
m = mPassword.get(i);
x = mCircles[m].mCenter.x;
y = mCircles[m].mCenter.y;
// Circle Glow.
canvas.drawCircle(x, y, mCircleOuterRadius, mCircleGlowPaint);
// Circle Highlight.
canvas.drawCircle(x, y, mCircleOuterRadius, mCircleFiredPaint);
// Dot Glow.
canvas.drawCircle(x, y, mDotOuterRadius, mDotGlowPaint);
// Dot Highlight.
canvas.drawCircle(x, y, mDotInnerRadius, mDotFiredPaint);
if (j >= 0) {
n = mPassword.get(j);
x1 = mCircles[n].mCenter.x;
y1 = mCircles[n].mCenter.y;
canvas.drawLine(x1, y1, x, y, mLinePaint);
}
}
if (mDrawingStarted && i > 0) {
m = mPassword.get(i - 1);
x = mCircles[m].mCenter.x;
y = mCircles[m].mCenter.y;
canvas.drawLine(x, y, mX, mY, mLinePaint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mDrawingStarted = true;
break;
case MotionEvent.ACTION_MOVE:
mX = (int) event.getX();
mY = (int) event.getY();
int idx = inCycle(mX, mY);
if (idx >= 0) {
if (mPassword.size() > 0) {
int last = mPassword.get(mPassword.size() - 1);
int x1 = last % 3;
int x2 = idx % 3;
int y1 = last / 3;
int y2 = idx / 3;
int dx = Math.abs(x2 - x1);
int dy = Math.abs(y2 - y1);
int x3 = -1;
int y3 = -1;
if (dx == 2)
x3 = 1;
else if (dx == 0)
x3 = x1;
if (dy == 2)
y3 = 1;
else if (dy == 0)
y3 = y1;
if (x3 >= 0 && y3 >= 0) {
int i = x3 + y3 * 3;
if (!mPassword.contains(i))
mPassword.add(i);
}
}
mPassword.add(idx);
}
invalidate();
break;
case MotionEvent.ACTION_UP:
mDrawingStarted = false;
invalidate();
if (mOnPasswordDrawnListener != null && mPassword.size() > 0) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mPassword.size(); i++)
sb.append(mPassword.get(i).toString());
mOnPasswordDrawnListener.onPasswordDrawn(this, sb.toString());
}
break;
}
return true;
}
private int inCycle(int x, int y) {
int i;
for (i = 0; i < mCircles.length; i++) {
if (mCircles[i].mOuterBound.contains(x, y))
break;
}
if (i == mCircles.length)
return -1;
if (mCircles[i].mUsed)
return -1;
if (Math.sqrt(
Math.pow(x - mCircles[i].mCenter.x, 2) + Math.pow(y - mCircles[i].mCenter.y, 2)) > mCircleOuterRadius)
return -1;
mCircles[i].mUsed = true;
return i;
}
public void refresh() {
for (int i = 0; i < mCircles.length; i++)
mCircles[i].mUsed = false;
mPassword.clear();
invalidate();
}
}