现在很多app多具有滑动解锁的功能!多以就是这自己写了一个,先来看一下效果图
具体实现是先自定义了一个GestureLockView继承View,即我用看到的圆圈;在自定义一个GestureLockViewLayout继承RelativeLayout用于存放我们自定义的GestureLockView
下面是自定义GestureLockView的代码:
package com.wang.gesturelockview.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Path.FillType;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;
public class GestureLockView extends View {
/**
* GestureLockView的三种状态
*/
enum Mode {
STATUS_NO_FINGER, STATUS_FINGER_ON, STATUS_FINGER_UP;
}
/**
* 当前的状态
*/
private Mode mCurrentStatus = Mode.STATUS_NO_FINGER;
/**
* 三种状态下不同的颜色
*/
private int color_No_finger = Color.WHITE;
private int color_finger_on = Color.GREEN;
private int color_finger_up = Color.RED;
/**
* 圆心坐标和半径
*/
private float centerX;
private float centerY;
private float radius;
// 内圆的半径和外圆的半径的比值
private float innerCircleRate = 0.333f;
/**
* 三角形小箭头
*/
private float mArrowRate = 0.333f;// 缩小的比例
private int mArrowDegree = -1;// 偏转度数,初始向上
private Path mArrowPath;
/**
* 画笔 线条的宽度
*/
private Paint mPaint;
private int mStrokeWidth = 2;
private int mWidth;
public GestureLockView(Context context) {
this(context, null, 0);
}
public GestureLockView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GestureLockView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
mWidth = width < height ? width : height;
centerX = centerY = radius = mWidth / 2;
radius = radius - mStrokeWidth / 2;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
// 画三角形
mArrowPath = new Path();
float arrowWidth = mWidth * mArrowRate / 3;
mArrowPath.moveTo(mWidth * 1.0f / 2, mWidth * 1.0f / 9);
mArrowPath.lineTo(mWidth / 2 - arrowWidth / 2, mWidth * 2.0f / 9);
mArrowPath.lineTo(mWidth / 2 + arrowWidth / 2, mWidth * 2.0f / 9);
mArrowPath.close();// 会自动加上首尾线
mArrowPath.setFillType(FillType.WINDING);
}
@Override
protected void onDraw(Canvas canvas) {
if (mCurrentStatus == Mode.STATUS_NO_FINGER) {
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setColor(color_No_finger);
canvas.drawCircle(centerX, centerY, radius, mPaint);
} else if (mCurrentStatus == Mode.STATUS_FINGER_ON) {
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setColor(color_finger_on);
canvas.drawCircle(centerX, centerY, radius, mPaint);
mPaint.setStyle(Style.FILL);
canvas.drawCircle(centerX, centerY, radius * innerCircleRate,
mPaint);
} else if (mCurrentStatus == Mode.STATUS_FINGER_UP) {
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setColor(color_finger_up);
canvas.drawCircle(centerX, centerY, radius, mPaint);
mPaint.setStyle(Style.FILL);
canvas.drawCircle(centerX, centerY, radius * innerCircleRate,
mPaint);
//画箭头
if(mArrowDegree!=-1){
canvas.save();//保存原先
canvas.rotate(mArrowDegree, centerX, centerY);
canvas.drawPath(mArrowPath, mPaint);
canvas.restore();
}
}
super.onDraw(canvas);
}
public void setDegree(int mArrowDegree){
this.mArrowDegree=mArrowDegree;
}
public int getDegree(){
return mArrowDegree;
}
public void setMode(Mode mode){
this.mCurrentStatus=mode;
invalidate();
}
}
下面是 GestureLockViewLayout的代码:
package com.wang.gesturelockview.view;
import java.util.ArrayList;
import java.util.List;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Join;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.widget.RelativeLayout;
import android.widget.Toast;
import com.wang.gesturelockview.view.GestureLockView.Mode;
public class GestureLockViewLayout extends RelativeLayout {
/**
* 保存GestureLockView的数组
*/
private GestureLockView[] gestureLockViews;
/**
* 每个边上GestureLockView的个数
*/
private int mCount = 3;
/**
* 存储的答案
*/
private int[] answer={1,2,3};
/**
* 保存用户已选gestureLockView的id
*/
private List<Integer> choose = new ArrayList<Integer>();
/**
* 两个GestureLockView之间的距离
*/
private int mMarginBetweenLockView = 30;
/**
* GestureLockView的边长
*/
private int mGestureLockViewWidth = (int) TypedValue
.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50, getResources()
.getDisplayMetrics());
private int color;// 待会补充
/**
* 控件的高宽
*/
private int mWidth;
private int mHeight;
/**
* 画宽线的画笔
*/
private Paint mPaint;
/**
* 指引线对象
*/
private Path mPath;
/**
* 指引线开始的坐标
*/
private int mLastPathX = 0;
private int mLastPahtY = 0;
/**
* 指引线结束的位置
*/
private PointF mTmpTarget = new PointF();
/**
* 最大尝试次数
*/
private int mTryTimes = 5;
public GestureLockViewLayout(Context context) {
this(context, null, 0);
}
public GestureLockViewLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public GestureLockViewLayout(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
// 初始化画笔对象,线对象
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeCap(Cap.ROUND);
mPaint.setStrokeJoin(Join.ROUND);
mPath = new Path();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
mWidth = mHeight = mWidth < mHeight ? mWidth : mHeight;
// 初始化所有的GestureLockView
if (gestureLockViews == null) {
gestureLockViews = new GestureLockView[mCount * mCount];
mMarginBetweenLockView = (mWidth - mGestureLockViewWidth * mCount)
/ (mCount + 2);
// 初始化画笔的宽度
mPaint.setStrokeWidth(0.34f * mGestureLockViewWidth);
mPaint.setColor(Color.GREEN);
mPaint.setAlpha(50);
for (int i = 0; i < gestureLockViews.length; i++) {
Log.i("wangsongbin", i + "");
// 生成子view,并赋予其id;
gestureLockViews[i] = new GestureLockView(getContext());
gestureLockViews[i].setId(i + 1);
LayoutParams lp = new LayoutParams(mGestureLockViewWidth,
mGestureLockViewWidth);
if (i % mCount != 0) {// 不是第一例的
lp.addRule(RelativeLayout.RIGHT_OF,
gestureLockViews[i - 1].getId());
}
if (i >= mCount) {// 不是第一行
lp.addRule(RelativeLayout.BELOW, gestureLockViews[i
- mCount].getId());
}
// 设置左右上下的边距
float rightMargin = 0;
float leftMargin;
float topMargin;
float bottomMargin = 0;
if (i % mCount == 0) {// 第一例
leftMargin = (float) (1.5 * mMarginBetweenLockView);
} else {
leftMargin = mMarginBetweenLockView;
}
if (i < mCount) {// 第一行
topMargin = (float) (1.5 * mMarginBetweenLockView);
} else {
topMargin = mMarginBetweenLockView;
}
lp.setMargins((int) leftMargin, (int) topMargin,
(int) rightMargin, (int) bottomMargin);
gestureLockViews[i].setMode(Mode.STATUS_NO_FINGER);
addView(gestureLockViews[i], lp);
}
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
float x = event.getX();
float y = event.getY();
Log.i("wangsongbin", "x:" + x + " y:" + y);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 把所有的GestureLockView初始化为status_no_finger
resetGestureLockView();
break;
case MotionEvent.ACTION_MOVE:
// 改变连接线的颜色
mPaint.setColor(Color.GREEN);
mPaint.setAlpha(50);
GestureLockView child = getChildByPoint(x, y);
if (child != null) {
if (!choose.contains(child.getId())) {
choose.add(child.getId());
child.setMode(Mode.STATUS_FINGER_ON);
mLastPathX = (child.getLeft() + child.getRight()) / 2;
mLastPahtY = (child.getTop() + child.getBottom()) / 2;
if (choose.size() == 1) {
mPath.moveTo(mLastPathX, mLastPahtY);
} else {
mPath.lineTo(mLastPathX, mLastPahtY);
}
}
}
mTmpTarget.x = x;
mTmpTarget.y = y;
break;
case MotionEvent.ACTION_UP:
// 减少输入密码的次数
mTryTimes--;
// 将超出的点设置成最后的端点上
mTmpTarget.x = mLastPathX;
mTmpTarget.y = mLastPahtY;
//校对答案
if(checkAnswer()){
Toast.makeText(getContext(), "密码正确", Toast.LENGTH_LONG).show();
break;
}
// 改变连接线的颜色
mPaint.setColor(Color.RED);
mPaint.setAlpha(50);
// 计算偏转的角度
changeChoosedItem();
for (int i = 0; i < choose.size() - 1; i++) {
int childId = choose.get(i);
int nextChildId = choose.get(i + 1);
GestureLockView startChild = (GestureLockView) GestureLockViewLayout.this
.findViewById(childId);
GestureLockView nextChild = (GestureLockView) GestureLockViewLayout.this
.findViewById(nextChildId);
int dx=nextChild.getLeft()-startChild.getLeft();
int dy=nextChild.getTop()-startChild.getTop();
//计算角度
int degree=(int) Math.toDegrees(Math.atan2(dy, dx))+90;
startChild.setDegree(degree);
}
break;
}
invalidate();// 父容器的重新初始化,会带动,子控件的重新初始化
return true;
}
private boolean checkAnswer() {
if(answer.length!=choose.size()){
return false;
}
for(int i=0;i<answer.length;i++){
if(answer[i]!=choose.get(i)){
return false;
}
}
return true;
}
private void changeChoosedItem() {
for (int i = 0; i < choose.size(); i++) {
GestureLockView view = (GestureLockView) this.findViewById(choose
.get(i));
view.setDegree(-1);
view.setMode(Mode.STATUS_FINGER_UP);
}
}
/**
* 重新初始化所有的GestureLockView
*/
private void resetGestureLockView() {
choose.clear();
mPath.reset();
for (int i = 0; i < gestureLockViews.length; i++) {
gestureLockViews[i].setMode(Mode.STATUS_NO_FINGER);
}
}
/**
* 根据触点坐标判断它出发了那个圆圈
*
* @param x
* @param y
* @return
*/
private GestureLockView getChildByPoint(float x, float y) {
for (int i = 0; i < gestureLockViews.length; i++) {
GestureLockView view = gestureLockViews[i];
// 设置一个内边距
float padding = 0.15f * mGestureLockViewWidth;
if (x > (view.getLeft() + padding)
&& x < (view.getRight() + padding)
&& y > (view.getTop() + padding)
&& y < (view.getBottom() - padding)) {
return view;
}
}
return null;
};
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mPath != null) {
canvas.drawPath(mPath, mPaint);
}
// 绘制引线
if (mLastPathX != 0 && mLastPahtY != 0) {
canvas.drawLine(mLastPathX, mLastPahtY, mTmpTarget.x, mTmpTarget.y,
mPaint);
}
}
}
下面附上源代码: