您的位置:首页 > 移动开发 > Android开发

Android——自定义View实现9宫格解锁

2017-12-19 16:52 363 查看

                          
Android——自定义View实现9宫格解锁

自定义View
1.实现一个子类继承View
2.覆盖onDrow()函数,渲染图像
3.覆盖onTouchEvent()函数
4.监听按下、移动,松开手指的动作
5.重新在onDrow()中渲染对应的的图像



这是一个仿京东金融的一个九宫格解锁,最上面的日期显示使用的Time()获取到当前的时间,我们得到日期对其赋值就好了。
九宫格解锁有两个模式:CREATE_MODE  CHECK_MODE两种模式,一种是用来设置密码的模式,一种是输入密码的模式。验证登录密码会有三次机会,三次都输入错误会退出项目,忘记密码了也可以进行重新设置密码,非常的方便。重新设置手势密码这块没有进行验证,有兴趣的朋友可以用推送实现一个短信验证的效果。

添加依赖:

compile 'com.facebook.fresco:fresco:0.11.0'


自定义一个view:

public class UnlockView extends View{
public static final int CIRCLE_NORMAL = 1;//normal state of circle
public static final int CIRCLE_SELECTED = 2;//selected state of circle
public static final int CIRCLE_ERROR = 3;//error state of circle
public static final int CREATE_MODE = 22;//this mode is used for creating gesture
public static final int CHECK_MODE = 33;//this mode is used for checking gesture
@UnlockMode
private int mode;//define the mode
private int width;//the width of screen,valued in onMeasure
private int height;//the height of screen,valued in onMeasure
private int rootX;//root position of the line which can move
private int rootY;//root position of the line which can move
private Context ctx;
private ArrayList<Circle> circleList = new ArrayList<>();//store the circles on screen
private ArrayList<Circle> pathCircleList = new ArrayList<>();//store the selected circles
private Bitmap circletBmp;//used for drawing circles
private Canvas mCanvas;
private Paint cirNorPaint;//paint of normal state circles
private Paint cirSelPaint;//paint of selected state circles
private Paint smallCirSelPaint;//paint of selected state small circles
private Paint cirErrPaint;//paint of error state circles
private Paint smallcirErrPaint;//paint of error state small circles
private Paint pathPaint;//paint of the lines
private Path mPath;
private Path tempPath;
private int pathWidth = 3;//width of the paint of path
private int normalR = 15;//radius of small circles;
private int selectR = 30;//radius of big circles;
private int strokeWidth = 2;//width of big circles;
private int normalColor = Color.parseColor("#D5DBE8");//defalt color of normal state
private int selectColor = Color.parseColor("#508CEE");//defalt color of selected state
private int errorColor = Color.parseColor("#FF3153");//defalt color of error state

private boolean isUnlocking;
private boolean isShowError;
private boolean hasNewCircles;
private ArrayList<Integer> passList = new ArrayList<>();
private OnUnlockListener listener;//the listener of unlock
private CreateGestureListener createListener;//the listener of creating gesture

/**
* used for refresh the canvas after MotionEvent.ACTION_UP
*/
private Handler handler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
resetAll();
invalidate();
return true;
}
});

public UnlockView(Context context) {
super(context);
this.ctx = context;
strokeWidth = dip2px(ctx, strokeWidth);
normalR = dip2px(ctx, normalR);
selectR = dip2px(ctx, selectR);
pathWidth = dip2px(ctx, pathWidth);
}

public UnlockView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.ctx = context;
strokeWidth = dip2px(ctx, strokeWidth);
normalR = dip2px(ctx, normalR);
selectR = dip2px(ctx, selectR);
pathWidth = dip2px(ctx, pathWidth);
}

public UnlockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.ctx = context;
strokeWidth = dip2px(ctx, strokeWidth);
normalR = dip2px(ctx, normalR);
selectR = dip2px(ctx, selectR);
pathWidth = dip2px(ctx, pathWidth);

}

/**
* reset all states
*/
private void resetAll() {
isShowError = false;
isUnlocking = false;
mPath.reset();
tempPath.reset();
pathCircleList.clear();
passList.clear();
for (Circle circle : circleList) {
circle.setState(CIRCLE_NORMAL);
}
pathPaint.setColor(selectColor);
cirSelPaint.setColor(selectColor);
smallCirSelPaint.setColor(selectColor);
clearCanvas();
}

@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(circletBmp, 0, 0, null);
for (int i = 0; i < circleList.size(); i++) {
drawCircles(circleList.get(i));
}
canvas.drawPath(mPath, pathPaint);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
if (isShowError)
return true;
int curX = (int) event.getX();
int curY = (int) event.getY();
Circle circle = getOuterCircle(curX, curY);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
this.resetAll();
if (circle != null) {
rootX = circle.getX();
rootY = circle.getY();
circle.setState(CIRCLE_SELECTED);
pathCircleList.add(circle);
tempPath.moveTo(rootX, rootY);
addItem(circle.getPosition() + 1);
isUnlocking = true;
}
break;
case MotionEvent.ACTION_MOVE:
if (isUnlocking) {
mPath.reset();
mPath.addPath(tempPath);
mPath.moveTo(rootX, rootY);
mPath.lineTo(curX, curY);
handleMove(circle);
}
break;
case MotionEvent.ACTION_UP:
isUnlocking = false;
if (pathCircleList.size() > 0) {
mPath.reset();
mPath.addPath(tempPath);
StringBuilder sb = new StringBuilder();
for (Integer num : passList) {
sb.append(num);
}

if (this.mode
1a8ab
== CREATE_MODE) {
if(createListener!=null){
createListener.onGestureCreated(sb.toString());
}else{
Log.e("UnLockView","Please set CreateGestureListener first!");
}
} else if(this.mode == CHECK_MODE){
if(listener!=null){
if (listener.isUnlockSuccess(sb.toString())) {
listener.onSuccess();
} else {
listener.onFailure();
for (Circle circle1 : pathCircleList) {
circle1.setState(CIRCLE_ERROR);
}
pathPaint.setColor(errorColor);
}
}else{
Log.e("UnLockView","Please set OnUnlockListener first!");
}

}else{
try {
throw new Exception("Please set mode first!");
} catch (Exception e) {
e.printStackTrace();
}
}
isShowError = true;
handler.postDelayed(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage(0);
}
}, 1000);

}
break;
}
invalidate();
return true;
}

private synchronized void handleMove(Circle c) {
if (c != null&&!(c.getState()==CIRCLE_SELECTED)) {
c.setState(CIRCLE_SELECTED);
pathCircleList.add(c);
rootX = c.getX();
rootY = c.getY();
tempPath.lineTo(rootX, rootY);
addItem(c.getPosition() + 1);
}
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = getMeasuredWidth();
height = getMeasuredHeight();
}

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mode != CHECK_MODE || mode != CREATE_MODE) {
Log.e("UnlockView", "Please set mode first!");
}
//init all path/paint
mPath = new Path();
tempPath = new Path();
pathPaint = new Paint();
pathPaint.setColor(selectColor);
pathPaint.setDither(true);
pathPaint.setAntiAlias(true);
pathPaint.setStyle(Paint.Style.STROKE);
pathPaint.setStrokeCap(Paint.Cap.ROUND);
pathPaint.setStrokeJoin(Paint.Join.ROUND);
pathPaint.setStrokeWidth(pathWidth);
//普通状态小圆画笔
circletBmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(circletBmp);
cirNorPaint = new Paint();
cirNorPaint.setAntiAlias(true);
cirNorPaint.setDither(true);
cirNorPaint.setColor(normalColor);
//选中状态大圆画笔
cirSelPaint = new Paint();
cirSelPaint.setAntiAlias(true);
cirSelPaint.setDither(true);
cirSelPaint.setStyle(Paint.Style.STROKE);
cirSelPaint.setStrokeWidth(strokeWidth);
cirSelPaint.setColor(selectColor);
//选中状态小圆画笔
smallCirSelPaint = new Paint();
smallCirSelPaint.setAntiAlias(true);
smallCirSelPaint.setDither(true);
smallCirSelPaint.setColor(selectColor);
//出错状态大圆画笔
cirErrPaint = new Paint();
cirErrPaint.setAntiAlias(true);
cirErrPaint.setDither(true);
cirErrPaint.setStyle(Paint.Style.STROKE);
cirErrPaint.setStrokeWidth(strokeWidth);
cirErrPaint.setColor(errorColor);
//出错状态小圆画笔
smallcirErrPaint = new Paint();
smallcirErrPaint.setAntiAlias(true);
smallcirErrPaint.setDither(true);
smallcirErrPaint.setColor(errorColor);

//init all circles
int hor = width / 6;
int ver = height / 6;
if(!hasNewCircles){
for (int i = 0; i < 9; i++) {
int tempX = (i % 3 + 1) * 2 * hor - hor;
int tempY = (i / 3 + 1) * 2 * ver - ver;
Circle circle = new Circle(i, tempX, tempY, CIRCLE_NORMAL);
circleList.add(circle);
}
}
hasNewCircles=true;

}

/**
* called in onDraw for drawing all circles
*
* @param circle
*/
private void drawCircles(Circle circle) {
switch (circle.getState()) {
case CIRCLE_NORMAL:
mCanvas.drawCircle(circle.getX(), circle.getY(), normalR, cirNorPaint);
break;
case CIRCLE_SELECTED:
mCanvas.drawCircle(circle.getX(), circle.getY(), selectR, cirSelPaint);
mCanvas.drawCircle(circle.getX(), circle.getY(), normalR, smallCirSelPaint);
break;
case CIRCLE_ERROR:
mCanvas.drawCircle(circle.getX(), circle.getY(), selectR, cirErrPaint);
mCanvas.drawCircle(circle.getX(), circle.getY(), normalR, smallcirErrPaint);
break;
}
}

/**
* clear canvas
*/
private void clearCanvas() {
Paint p = new Paint();
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
mCanvas.drawPaint(p);
p.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
mPath.reset();
tempPath.reset();
}

/**
* J U S T  A  T O O L !
*
* @param context  Context
* @param dipValue value of dp
* @return
*/
public static int dip2px(Context context, float dipValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dipValue * scale + 0.5f);
}

/**
* check whether the point is in a circle
*
* @param x
* @param y
* @return
*/
@Nullable
private Circle getOuterCircle(int x, int y) {
for (int i = 0; i < circleList.size(); i++) {
Circle circle = circleList.get(i);
if ((x - circle.getX()) * (x - circle.getX()) + (y - circle.getY()) * (y - circle.getY()) <= normalR * normalR) {
if (circle.getState() != CIRCLE_SELECTED) {
return circle;
}
}
}
return null;
}

/**
* check whether the password list contains the number
*
* @param num
* @return
*/
private boolean arrContainsInt(int num) {
for (Integer value : passList) {
if (num == value) {
return true;
}
}
return false;
}

/**
* put the num into password list
*
* @param num
*/
private void addItem(Integer num) {
if (!arrContainsInt(num)) {
passList.add(num);
}
}

/**
* Create Mode Listener
*/
interface CreateGestureListener {
void onGestureCreated(String result);
}

public void setGestureListener(CreateGestureListener listener) {
this.createListener = listener;
}

/**
* Check Mode Listener
*/
interface OnUnlockListener {
boolean isUnlockSuccess(String result);

void onSuccess();

void onFailure();
}

public void setOnUnlockListener(OnUnlockListener listener) {
this.listener = listener;
}

public void setPathWidth(int pathWidth) {
this.pathWidth = pathWidth;
}

public void setNormalR(int normalR) {
this.normalR = normalR;
}

public void setSelectR(int selectR) {
this.selectR = selectR;
}

public void setNormalColor(int normalColor) {
this.normalColor = normalColor;
}

public void setSelectColor(int selectColor) {
this.selectColor = selectColor;
}

public void setErrorColor(int errorColor) {
this.errorColor = errorColor;
}

public void setMode(@UnlockMode int mode) {
this.mode = mode;
}

class Circle {
/**
* position of the circle
*/
private int position;
private int x;
private int y;
/**
* the state of circle
*/
private int state;

public Circle() {
}

public Circle(int position, int x, int y, int state) {
this.position = position;
this.x = x;
this.y = y;
this.state = state;
}

public int getPosition() {
return position;
}

public void setPosition(int position) {
this.position = position;
}

public int getX() {
return x;
}

public void setX(int x) {
this.x = x;
}

public int getY() {
return y;
}

public void setY(int y) {
this.y = y;
}

public int getState() {
return state;
}

public void setState(int state) {
this.state = state;
}
}

/**
* It's an annotation
*/
@IntDef({CREATE_MODE, CHECK_MODE})
@interface UnlockMode {
}
}


main_activity.xml:

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="08"
android:textSize="40sp"
android:textStyle="bold"
android:layout_marginTop="20dp"
android:layout_marginLeft="20dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Dec"
android:textSize="17sp"
android:textStyle="bold"
android:layout_toRightOf="@+id/time"
android:layout_marginTop="40dp"
android:layout_marginLeft="5dp"
/>
</RelativeLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="未来的事情做好计划不去担心"
android:textSize="20sp"
android:layout_marginLeft="20dp"
/>

<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/img"
android:layout_width="100dp"
android:layout_height="100dp"
fresco:roundAsCircle="true"
android:layout_marginTop="30dp"
android:layout_marginBottom="30dp"
android:layout_gravity="center_horizontal"
fresco:placeholderImage="@mipmap/ic_launcher"/>

<test.bwei.com.handler.UnlockView
android:id="@+id/unlock"
android:layout_width="400dp"
android:layout_height="400dp"
android:layout_gravity="center_horizontal"
/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="120dp"
android:layout_gravity="center_horizontal"
>
<TextView
android:id="@+id/forget"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="忘记手势密码"
android:textSize="17sp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="|"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:textSize="17sp"
/>
<TextView
android:id="@+id/type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="请设置您的手势密码!"
android:textColor="#f00"
android:textSize="17sp"
/>

</LinearLayout>
</LinearLayout>


main方法:

public class MainActivity extends AppCompatActivity {
int count = 0;
private UnlockView mUnlockView;
private String pwd;
private SharedPreferences sharedPreferences;
private SharedPreferences.Editor edit;
private SimpleDraweeView img;
private TextView time;
private TextView forget;
private TextView type;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
forget = (TextView) findViewById(R.id.forget);
type = (TextView) findViewById(R.id.type);

/*   ImmersionUtils immersionUtils = new ImmersionUtils();
immersionUtils.setImmersion(getWindow(), getSupportActionBar());*/

time = (TextView) findViewById(R.id.time);
Time t=new Time(); // or Time t=new Time("GMT+8"); 加上Time Zone资料
t.setToNow(); // 取得系统时间。
int year = t.year;
int month = t.month;
int date = t.monthDay;
int hour = t.hour;
time.setText(date+"");

img = (SimpleDraweeView) findViewById(R.id.img);
mUnlockView = (UnlockView) findViewById(R.id.unlock);

sharedPreferences = getSharedPreferences("data", Context.MODE_PRIVATE);
if (sharedPreferences.getInt("flage",0)==1){
img.setImageURI(sharedPreferences.getString("img", "img"));
}else {
img.setImageResource(R.mipmap.ic_launcher);
}
forget.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
edit = sharedPreferences.edit();
edit.putBoolean("key",false);
edit.commit();
onResume();
count=0;
}
});

}

@Override
protected void onResume() {
super.onResume();
boolean key = sharedPreferences.getBoolean("key", false);
pwd = sharedPreferences.getString("pwd", "pwd");
if (key) {

mUnlockView.setMode(UnlockView.CHECK_MODE);
type.setText("请登录你的密码");
mUnlockView.setOnUnlockListener(new UnlockView.OnUnlockListener() {
@Override
public boolean isUnlockSuccess(String result) {

if (result.equals(pwd)) {
return true;
} else {
return false;
}
}

@Override
public void onSuccess() {
Toast.makeText(MainActivity.this, "登录成功", Toast.LENGTH_SHORT).show();
Intent intent = new Intent(MainActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
@Override
public void onFailure() {
count++;
if (count == 3) {
finish();
} else {
Toast.makeText(MainActivity.this, "密码错误,还有" + (3 - count) + "次机会", Toast.LENGTH_SHORT).show();
}
}
});
} else {
mUnlockView.setMode(UnlockView.CREATE_MODE);

type.setText("请设置您的手势密码!");
mUnlockView.setGestureListener(new UnlockView.CreateGestureListener()

{
@Override
public void onGestureCreated (String result){

Log.i("zzz", "onGestureCreated: " + result);
pwd = result;
edit = sharedPreferences.edit();
edit.putBoolean("key",true);
edit.putString("pwd",pwd);
edit.commit();
Toast.makeText(MainActivity.this, "密码设置成功!", Toast.LENGTH_SHORT).show();
mUnlockView.setMode(UnlockView.CHECK_MODE);
onResume();

}
});

}
}

}

在Application初始化:
public class App extends Application{
@Override
public void onCreate() {
super.onCreate();
Fresco.initialize(this);

}
}


在manifest  Application里注册:
android:name=".App"
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: