Android 9格锁屏

这个教程呢,并不是up原创的,而是参考了网上的一篇素材 https://www.300168.com/yidong/show-1593.html

但是代码中存在一些bug,并做了一些改进和优化,(bug如下中间经过的键未能被选中)并没有轻视原楼主的意思,还是很棒的吐舌头

修改方法是只要在ACTION_MOVE里加这样一段代码就可以了(源代码待会儿我会贴出来的,大家不要觉得麻烦



第一个javaBean

[java]  view plain  copy
  1. /** 
  2.  * 锁屏中点的信息类 
  3.  */  
  4.   
  5. public class PointInfo {  
  6.   
  7.     private int mId; //点的id  
  8.   
  9.     private int mNextId;//当前点指向的下一个点的id  
  10.   
  11.     private boolean mSelected;//是否选中  
  12.   
  13.     private int mDefaultX;//默认图片左上角的X坐标  
  14.   
  15.     private int mDefaultY;//默认图片左上角Y的坐标  
  16.   
  17.     private int mSelectedX; // 被选中时图片的左上角X坐标  
  18.   
  19.     private int mSelectedY; // 被选中时图片的左上角Y坐标  
  20.   
  21.     private int mRangeWidth;//范围  
  22.   
  23.     private int mSelectedWidth;//选中显示的范围  
  24.   
  25.     /** 
  26.      *  构造方法 
  27.      *  selectedWidth 选中时图片选中的范围 
  28.      *  rangeWidth 触摸能响应事件的范围 如果rangeWidth > selectedWith 相当于加了个padding 
  29.      */  
  30.     public PointInfo(int id, int defaultX, int defaultY, int selectedX,  
  31.                      int selectedY, int selectedWidth, int rangeWidth) {  
  32.         mId = id;  
  33.         mNextId = mId;  
  34.         mDefaultX = defaultX;  
  35.         mDefaultY = defaultY;  
  36.         mSelectedX = selectedX;  
  37.         mSelectedY = selectedY;  
  38.         mSelectedWidth = selectedWidth;//选中范围  
  39.         mRangeWidth = rangeWidth;  
  40.     }  
  41.   
  42.     public void setNextId(int nextId) {  
  43.         mNextId = nextId;  
  44.     }  
  45.   
  46.     public void setSelected(boolean selected) {  
  47.         mSelected = selected;  
  48.     }  
  49.   
  50.     public int getId() {  
  51.         return mId;  
  52.     }  
  53.   
  54.     public int getNextId() {  
  55.         return mNextId;  
  56.     }  
  57.   
  58.     public boolean isSelected() {  
  59.         return mSelected;  
  60.     }  
  61.   
  62.     public int getDefaultX() {  
  63.         return mDefaultX;  
  64.     }  
  65.   
  66.     public int getDefaultY() {  
  67.         return mDefaultY;  
  68.     }  
  69.   
  70.     public int getSelectedX() {  
  71.         return mSelectedX;  
  72.     }  
  73.   
  74.     public int getSelectedY() {  
  75.         return mSelectedY;  
  76.     }  
  77.   
  78.     /** 
  79.      * 是否有下一个id 
  80.      * @return 
  81.      */  
  82.     public boolean hasNextId() {  
  83.         return mNextId != mId;  
  84.     }  
  85.   
  86.     /** 
  87.      * 得到中心X 
  88.      * @return 
  89.      */  
  90.     public int getCenterX(){  
  91.         return mSelectedX + mSelectedWidth/2;  
  92.     }  
  93.   
  94.     /** 
  95.      * 得到中心Y 
  96.      * @return 
  97.      */  
  98.     public int getCenterY(){  
  99.         return mSelectedY + mSelectedWidth/2;  
  100.     }  
  101.   
  102.     /** 
  103.      * 坐标(x,y)是否在当前点的范围内 
  104.      * @return 
  105.      */  
  106.     public boolean isInMyPlace(int x, int y) {  
  107.         boolean inX = x >= getCenterX() - mRangeWidth/2 && x <= getCenterX() + mRangeWidth/2;  
  108.         boolean inY = y >= getCenterY() - mRangeWidth/2 && y <= getCenterY() + mRangeWidth/2;  
  109.         return (inX && inY);//只有两个都为真时才为真  
  110.     }  
  111.   
  112. }  

第二个主类

[java]  view plain  copy
  1. package com.example.ninepointtask.view;  
  2.   
  3. import android.content.Context;  
  4. import android.graphics.Bitmap;  
  5. import android.graphics.BitmapFactory;  
  6. import android.graphics.Canvas;  
  7. import android.graphics.Color;  
  8. import android.graphics.Matrix;  
  9. import android.graphics.Paint;  
  10. import android.graphics.Rect;  
  11. import android.support.annotation.Nullable;  
  12. import android.util.AttributeSet;  
  13. import android.util.Log;  
  14. import android.view.MotionEvent;  
  15. import android.view.View;  
  16.   
  17. import com.example.ninepointtask.R;  
  18. import com.example.ninepointtask.bean.PointInfo;  
  19. import com.example.ninepointtask.util.DensityUtil;  
  20.   
  21. import java.util.ArrayList;  
  22. import java.util.List;  
  23.   
  24. /** 
  25.  * Created by xiaohan on 2018/4/25. 
  26.  */  
  27.   
  28. public class NinePointView extends View {  
  29.   
  30.     private static final String TAG = "NinePointView";  
  31.   
  32.     private Paint mLinePaint = new Paint();//线画笔  
  33.     private Bitmap mDefaultBitmap;  
  34.     private int mDefaultBitmapRadius;//宽为20dp  
  35.     private Bitmap mSelectedBitmap;//选中的图片  
  36.     private int mSelectedBitmapDiameter;//选中图片的直径  
  37.     private int mSelectedBitmapRadius;//选中图片的半径  
  38.     //private Bitmap mFailBitmap;//失败图  
  39.     private int mPointRange;//点的半径  
  40.   
  41.     private PointInfo[] mPoints = new PointInfo[25];//点信息  
  42.   
  43.     private int mWidth, mHeight;//用于记录view的宽高  
  44.     private int mMoveX, mMoveY;//获取用户的移动坐标  
  45.     private int mStartX, mStartY;//开始坐标  
  46.     private boolean mIsUp = false;//用户是否抬起  
  47.   
  48.     private List<Integer> mPasswordArr = new ArrayList<>();//存放用户输入的密码  
  49.     private int mColumnNum;  
  50.   
  51.     public NinePointView(Context context) {  
  52.         super(context);  
  53.         init();  
  54.     }  
  55.   
  56.     public NinePointView(Context context, @Nullable AttributeSet attrs) {  
  57.         super(context, attrs);  
  58.         init();  
  59.     }  
  60.   
  61.     public NinePointView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {  
  62.         super(context, attrs, defStyleAttr);  
  63.         init();  
  64.     }  
  65.   
  66.     /** 
  67.      * 初始化 
  68.      */  
  69.     private void init() {  
  70.   
  71.         mColumnNum = (int) Math.sqrt(mPoints.length);  
  72.   
  73.         mDefaultBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.lock);//默认图形  
  74.         mDefaultBitmapRadius = DensityUtil.dipToPx(40);//默认图形的大小半径  
  75.         mSelectedBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.indicator_lock_area);//选中的图片  
  76.         mSelectedBitmapDiameter = DensityUtil.dipToPx(40);//选中的图形直径为40  
  77.         mPointRange = DensityUtil.dipToPx(55);//直径给个50吧  
  78.         mSelectedBitmapRadius = mSelectedBitmapDiameter / 2;//选中时图片的半径  
  79.   
  80.         Matrix matrix = new Matrix();  
  81.         matrix.postScale(mDefaultBitmapRadius * 2 / (float) mDefaultBitmap.getWidth(), mDefaultBitmapRadius * 2 / (float) mDefaultBitmap.getHeight());//先压缩一下  
  82.         mDefaultBitmap = Bitmap.createBitmap(mDefaultBitmap, 00, mDefaultBitmap.getWidth(),  
  83.                 mDefaultBitmap.getHeight(), matrix, true);//压缩一下默认图片  
  84.   
  85.         Matrix matrix1 = new Matrix();  
  86.         matrix1.postScale(mSelectedBitmapDiameter / (float) mSelectedBitmap.getWidth(), mSelectedBitmapDiameter / (float) mSelectedBitmap.getHeight());//先压缩一下  
  87.         mSelectedBitmap = Bitmap.createBitmap(mSelectedBitmap, 00, mSelectedBitmap.getWidth(), mSelectedBitmap.getHeight(), matrix1, true);  
  88.   
  89.         initPaint();  
  90.     }  
  91.   
  92.     /** 
  93.      * 初始化一下画笔 
  94.      */  
  95.     private void initPaint() {  
  96.         mLinePaint.setColor(Color.GRAY);//线性画笔的颜色  
  97.         mLinePaint.setStrokeWidth(DensityUtil.dipToPx(5));//线的宽度默认为5dp  
  98.         mLinePaint.setAntiAlias(true);  
  99.         mLinePaint.setStrokeCap(Paint.Cap.ROUND);//圆帽  
  100.     }  
  101.   
  102.     /** 
  103.      * 初始化点数组 
  104.      */  
  105.     private void initPoint() {  
  106.   
  107.         int len = mPoints.length;  
  108.         boolean HGreaterThanW = (mHeight > mWidth);//高是否大于宽  
  109.         int selectedSpacing = (Math.min(mWidth, mHeight) - mSelectedBitmapDiameter * mColumnNum) / (mColumnNum + 1);//分割宽度为宽度和高度中小的值减去6个圆的大小除以列数加1  
  110.         mPointRange = Math.min(mPointRange, mSelectedBitmapDiameter + selectedSpacing);防止点范围越界  
  111.   
  112.         int selectedX = HGreaterThanW ? selectedSpacing : (mWidth - mHeight) / 2 + selectedSpacing;//图片的左上角X点坐标  
  113.         int selectedY = HGreaterThanW ? mHeight - mWidth + selectedSpacing : selectedSpacing;//左上角Y坐标  
  114.   
  115.         int defaultX = selectedX + mSelectedBitmapRadius - mDefaultBitmapRadius;//默认X  
  116.         int defaultY = selectedY + mSelectedBitmapRadius - mDefaultBitmapRadius;//选中的中心点  
  117.   
  118.         for (int i = 0; i < len; i++) {  
  119.             if ((i % mColumnNum) == 0 && i != 0) {  
  120.                 selectedX = HGreaterThanW ? selectedSpacing : (mWidth - mHeight) / 2 + selectedSpacing; //重归开头  
  121.                 selectedY += mSelectedBitmapDiameter + selectedSpacing;//向下偏移  
  122.                 defaultX = selectedX + mSelectedBitmapRadius - mDefaultBitmapRadius;  
  123.                 defaultY += mSelectedBitmapDiameter + selectedSpacing;//向下偏移  
  124.             }  
  125.             mPoints[i] = new PointInfo(i, defaultX, defaultY, selectedX, selectedY, mSelectedBitmapDiameter, mPointRange);//初始化  
  126.             selectedX += mSelectedBitmapDiameter + selectedSpacing;//向右偏  
  127.             defaultX += mSelectedBitmapDiameter + selectedSpacing;//向右偏  
  128.         }  
  129.   
  130.     }  
  131.   
  132.     /** 
  133.      * 用户的触摸事件 
  134.      * 这个DOWN和MOVE、UP是成对的,如果没从UP释放,就不会再获得DOWN; 
  135.      * 而获得DOWN时,一定要确认消费该事件,否则MOVE和UP不会被这个VIEW的onTouchEvent接收 
  136.      */  
  137.     @Override  
  138.     public boolean onTouchEvent(MotionEvent event) {  
  139.         if (mIsUp) {  
  140.             finishDraw();  
  141.         }  
  142.         handleEvent(event);  
  143.         return true;  
  144.     }  
  145.   
  146.     /** 
  147.      * 结束绘制  重置一下 
  148.      */  
  149.     private void finishDraw() {  
  150.         if (mIsUp) {  
  151.             for (PointInfo pointInfo : mPoints) {  
  152.                 pointInfo.setSelected(false);//  
  153.                 pointInfo.setNextId(pointInfo.getId());  
  154.             }  
  155.             mPasswordArr.clear();//清除已存的密码  
  156.             mIsUp = false;  
  157.             invalidate();//重绘一下  
  158.         }  
  159.     }  
  160.   
  161.     /** 
  162.      * 处理事件 
  163.      * 
  164.      * @param event 
  165.      */  
  166.     private void handleEvent(MotionEvent event) {  
  167.   
  168.         switch (event.getAction()) {  
  169.   
  170.             case MotionEvent.ACTION_DOWN:  
  171.   
  172.                 int downX = (int) event.getX();  
  173.                 int downY = (int) event.getY();  
  174.                 for (PointInfo pointInfo : mPoints) {  
  175.                     if (pointInfo.isInMyPlace(downX, downY)) {  
  176.                         pointInfo.setSelected(true);//选中  
  177.                         mStartX = pointInfo.getCenterX();  
  178.                         mStartY = pointInfo.getCenterY();  
  179.                         mPasswordArr.add(pointInfo.getId());//将id加进去  
  180.                         break;//找到了就不用再找了  
  181.                     }  
  182.                 }  
  183.                 invalidate();//重绘  
  184.                 break;  
  185.   
  186.             case MotionEvent.ACTION_MOVE:  
  187.                 mMoveX = (int) event.getX();  
  188.                 mMoveY = (int) event.getY();  
  189.                 handlePoint();  
  190.                 invalidate();//重绘  
  191.                 break;  
  192.   
  193.             case MotionEvent.ACTION_UP:  
  194.                 mStartX = mStartY = mMoveX = mMoveY = 0;  
  195.                 mIsUp = true;  
  196.                 invalidate();//重绘  
  197.   
  198.                 if (mOnEndListener != null && mPasswordArr != null && mPasswordArr.size() != 0)  
  199.                     mOnEndListener.onFinish(mPasswordArr);//将数组传递出去  
  200.   
  201.                 break;  
  202.   
  203.         }  
  204.   
  205.     }  
  206.   
  207.     /** 
  208.      * 处理一下这些点 
  209.      */  
  210.     private void handlePoint() {  
  211.   
  212.         for (PointInfo pointInfo : mPoints) {  
  213.             if (pointInfo.isInMyPlace(mMoveX, mMoveY) && !pointInfo.isSelected()) {//如果在范围之内 并且没选中  
  214.                 pointInfo.setSelected(true);//设为选中  
  215.                 mStartX = pointInfo.getCenterX();  
  216.                 mStartY = pointInfo.getCenterY();  
  217.                 if (mPasswordArr.size() != 0) {  
  218.                     int preId = mPasswordArr.get(mPasswordArr.size() - 1);//前一个点的id  
  219.   
  220.                     int firstRax = preId / mColumnNum;//求出行数  
  221.                     int firstColumn = preId % mColumnNum;//求出列数  
  222.                     int secondRax = pointInfo.getId() / mColumnNum;//当前经过点的行数  
  223.                     int secondColumn = pointInfo.getId() % mColumnNum;//当前经过点的列数  
  224.   
  225.                     Log.d(TAG, "handlePoint: firstRaw  " + firstRax + " firstColumn " + firstColumn);  
  226.                     Log.d(TAG, "handlePoint: secondRaw " + secondRax + " secondColumn  " + secondColumn);  
  227.   
  228.                     if (firstRax == secondRax) {//如果在同一行上  
  229.                         for (int i = firstColumn + 1; i < secondColumn; i++)  //如果第二个点在第一个点右边  
  230.                             preId = connect(preId, firstRax * mColumnNum + i);  
  231.                         for (int i = firstColumn - 1; i > secondColumn; i--) //如果第二个点在第一个点左边  
  232.                             preId = connect(preId, firstRax * mColumnNum + i);  
  233.                     } else if (firstColumn == secondColumn) {//如果在同一列上  
  234.                         for (int i = firstRax + 1; i < secondRax; i++) //如果第一个点在第二个点上面  
  235.                             preId = connect(preId, i * mColumnNum + firstColumn);  
  236.                         for (int i = firstRax - 1; i > secondRax; i--) //如果第一个点在第二个点下面  
  237.                             preId = connect(preId, i * mColumnNum + firstColumn);  
  238.                     } else if (Math.abs(firstColumn - secondColumn) == Math.abs(firstRax - secondRax)) {//如果在同一对角线上  
  239.                         int raxOffset = (firstRax > secondRax) ? -1 : 1;  
  240.                         int indexRax = firstRax + raxOffset;//临时行  
  241.                         for (int i = firstColumn + 1; i < secondColumn; i++) { //如果第二个点在第一个点右边  
  242.                             preId = connect(preId, indexRax * mColumnNum + i);//再连一下  
  243.                             indexRax = firstRax + raxOffset;//行数也跟着偏移  
  244.                         }  
  245.                         for (int i = firstColumn - 1; i > secondColumn; i--) { //如果第二个点在第一个点左边  
  246.                             preId = connect(preId, indexRax * mColumnNum + i);//再连一下  
  247.                             indexRax = firstRax + raxOffset;//行数也跟着偏移  
  248.                         }  
  249.                     }  
  250.                     mPoints[preId].setNextId(pointInfo.getId());  
  251.                 }  
  252.                 mPasswordArr.add(pointInfo.getId());//将点id添加进去  
  253.                 break;//返回  
  254.             }  
  255.         }  
  256.   
  257.     }  
  258.   
  259.     /** 
  260.      * 将两个点相连返回下一个点 
  261.      */  
  262.     private int connect(int preId, int nextId) {  
  263.         Log.d(TAG, "connect: preId " + preId + " nextId " + nextId);  
  264.         if (!mPoints[nextId].isSelected()) {//如果没被选中  
  265.             mPoints[preId].setNextId(nextId);//下一个id  
  266.             mPasswordArr.add(nextId);//把这个点加进来  
  267.             mPoints[nextId].setSelected(true);//设为选中  
  268.             return nextId;  
  269.         }  
  270.         return preId;//把第一个id返回去  
  271.     }  
  272.   
  273.     /** 
  274.      * 绘制 
  275.      * 
  276.      * @param canvas 
  277.      */  
  278.     @Override  
  279.     protected void onDraw(Canvas canvas) {  
  280.         if (mMoveX != 0 && mMoveY != 0 && mStartX != 0 && mStartY != 0)// 绘制当前活动的线段  
  281.             canvas.drawLine(mStartX, mStartY, mMoveX, mMoveY, mLinePaint);//绘制线  
  282.         drawNinePoint(canvas);  
  283.     }  
  284.   
  285.     /** 
  286.      * 绘制那几个点 
  287.      * 
  288.      * @param canvas 
  289.      */  
  290.     private void drawNinePoint(Canvas canvas) {  
  291.   
  292.         for (PointInfo pointInfo : mPoints) { //先把用户的画出的线绘制好  
  293.             if (pointInfo.hasNextId()) { //如果有下一个  
  294.                 int nextId = pointInfo.getNextId();//下一个的id  
  295.                 canvas.drawLine(pointInfo.getCenterX(), pointInfo.getCenterY(),  
  296.                         mPoints[nextId].getCenterX(), mPoints[nextId].getCenterY(), mLinePaint);  
  297.             }  
  298.         }  
  299.   
  300.         for (PointInfo pointInfo : mPoints) {  
  301.             if (pointInfo.isSelected()) {//如果被选中  
  302.                 canvas.drawBitmap(mSelectedBitmap, pointInfo.getSelectedX(),  
  303.                         pointInfo.getSelectedY(), mLinePaint);//绘制一下默认图片  
  304.             } else {  
  305.                 canvas.drawBitmap(mDefaultBitmap, pointInfo.getDefaultX(),  
  306.                         pointInfo.getDefaultY(), mLinePaint);//绘制一下默认图片  
  307.             }  
  308.         }  
  309.   
  310.     }  
  311.   
  312.     /** 
  313.      * 在这里得到view的宽高 
  314.      */  
  315.     @Override  
  316.     protected void onSizeChanged(int w, int h, int oldW, int oldH) {  
  317.         super.onSizeChanged(w, h, oldW, oldH);  
  318.         Log.d(TAG, "onSizeChanged: " + " w " + w + " h " + h + " oldW " + oldW + " oldH " + oldH);  
  319.         mWidth = w;  
  320.         mHeight = h;//得到view宽高  
  321.         initPoint();//当获得宽高好得到一下  
  322.     }  
  323.   
  324.     //绘制结束监听  
  325.     public interface OnEndListener {  
  326.         void onFinish(List<Integer> passwordArr);//将密码触底出去  
  327.     }  
  328.   
  329.     private OnEndListener mOnEndListener;  
  330.   
  331.     /** 
  332.      * 设置结束的监听 
  333.      */  
  334.     public void setOnEndListener(OnEndListener onEndListener) {  
  335.         mOnEndListener = onEndListener;  
  336.     }  
  337. }  

第三个一个小的工具类

[java]  view plain  copy
  1. package com.example.ninepointtask.util;  
  2.   
  3. import com.example.ninepointtask.base.MyApplication;  
  4.   
  5. /** 
  6.  * Created by xiaohan on 2018/4/25. 
  7.  */  
  8.   
  9. public class DensityUtil {  
  10.   
  11.     /** 
  12.      * 工具类 构造器私有 
  13.      */  
  14.     private DensityUtil() {  
  15.     }  
  16.   
  17.     public static int dipToPx(float dpValue) {  
  18.         float scale = MyApplication.getContext().getResources().getDisplayMetrics().density;  
  19.         return (int) (dpValue * scale + 0.5f);  
  20.     }  
  21.   
  22.     public static int pxToDp(float pxValue) {  
  23.         float scale = MyApplication.getContext().getResources().getDisplayMetrics().density;  
  24.         return (int) (pxValue / scale + 0.5f);  
  25.     }  
  26.   
  27. }  

因为代码写的比较灵活 只要把NinePointView的

private PointInfo[] mPoints = new PointInfo[16];//点信息

9改为16就变成16键了 当然25键也是可以的 不过不建议太密

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值