第一次写博客,写的不好,知识点不到位的请大神,大牛指点。不废话了直接进入主题。

先看下效果图:

大致说下我的思路:

一:对题目的思考。

二:继承View 主要重写 onDraw(Canvas),onMeasure(int,int),oTouchEvent(MotionEvent);三个方法;

三:回调。

我们来说明下;

九宫格是个控件,是个单一的控件,不是一组控件,所以我直接继承view ,而不继承ViewGroup。

要自定义控件必须要重写测量方法 onMeasure(int,int).

是不是这样得到控件width,height很简单,其实onMeasure这样写是有局限性的,你的控件必须要设定大小和fill_parent才可以这样使用。

如果要是wrap_content,就不可以这样用了,

<pre name="code" class="java">/*** 作用是返回一个默认的值,如果MeasureSpec没有强制限制的话则使用提供的大小.否则在允许范围内可任意指定大小* 第一个参数size为提供的默认大小,第二个参数为测量的大小*/public static int getDefaultSize(int size, int measureSpec) {int result = size;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {// Mode = UNSPECIFIED,AT_MOST时使用提供的默认大小case MeasureSpec.UNSPECIFIED:result = size;break;case MeasureSpec.AT_MOST:// Mode = EXACTLY时使用测量的大小    case MeasureSpec.EXACTLY:result = specSize;break;}return result;}

这是在网上找的,可以看到当模式为MeasureSpec.UNSPECIFIED时,即未指明大小或warp_content,测量的结果为size;

而size为多少呢?

/*** 这个方法需要被重写,应该由子类去决定测量的宽高值,*/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

也就是这个size=getSuggestedMinimuWidth()和size=getSuggestedMinimuHeight()。

这两方法是View的两保护方法,下面是源码

    */
7285        protected int getSuggestedMinimumWidth() {
7286            int suggestedMinWidth = mMinWidth;
7287
7288            if (mBGDrawable != null) {
7289                final int bgMinWidth = mBGDrawable.getMinimumWidth();
7290                if (suggestedMinWidth < bgMinWidth) {
7291                    suggestedMinWidth = bgMinWidth;
7292                }
7293            }
7294
7295            return suggestedMinWidth;
7296        }
7297    

可以看到是直接返回最小的宽高。

我们在来看两段源码:

4162        public final int getMeasuredWidth() {
4163            return mMeasuredWidth;
4164        }
7191        protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
7192            mMeasuredWidth = measuredWidth;
7193            mMeasuredHeight = measuredHeight;
7194
7195            mPrivateFlags |= MEASURED_DIMENSION_SET;
7196        }
7197

是不是发现什么。getMeasuredWidth()和getMeasuredHeight()两方法得到的值就是getDefaultSize()的返回值。

所以这两个方法与模式没关系。所以在自定义测量的时候可以直接用这两个方法。

不懂在上网查查,onMeasure(int ,int)先到这里。

再来说说onDraw(Canvas)

这个ondraw方法在view初始化后调用绘制画布。每当控件增加新的图案都会调用ondraw(canvas)方法,调用这个方法是重新绘制,而不是只绘制新增的部分。要在UI线程调用

invalidate()刷新画布才可以显示。Canvas是画图,创建Canvas有两种方法。一个是当做onDraw参数传进去,

另一中是: Bitmap b=Bitmap.createBitmap(200,200,Bitmap.Config.ARGB_888);

Canvas c=new Canvas(b);创建一个200*200的Bitmap当做Canvas操作画布。

不懂的可以上网查查。

我在细说一下我的代码:


最后一个参数为接口,

<pre name="code" class="java">public class CircleCanvas extends ListView {private Context mct;private Paint mpaint;                      //画笔private Paint hollowPaint;private Paint linePaint;private int width;                         //view  宽,高private int height;private int startRangeWidth;               //起点坐标(x,y)private int startRangeHeight;private int endRangeWidth;                 //终点坐标(X,Y)两个坐标构成滑动区域。private int endRangeHeight;private int spotIntervalWidth;             //每个点之间的间隔private int spotIntervalHeight;  private List<SpotXY> spot;                 //存放9个点的坐标private List<SpotXY> delSpot;              //作用方面显示画的结果private List<SpotXY> storeSpot;            //储存选中的空心圆的坐标  即spotprivate List<Segment> segment;             //储存画出的线段   private int i=0;private float r=100.0f;                    //大圆半径private SpotXY xy;                         //一个点private SpotXY xyTwo;private int tclWay=0;private boolean isspot=true;private static int countId=1;                     //每个点的id编号private String pwd;                        //密码private OnCircleCanvasOver oncirclecanvasover;

这个参数在储存画的线段的时候会用的,判断不要重复储存一样的线段

 private static int countId=1;                     //每个点的id编号

初始化操作,

 public CircleCanvas(Context context, AttributeSet attrs) {super(context, attrs);this.mct=context;spot=new ArrayList<SpotXY>();delSpot=new ArrayList<SpotXY>();storeSpot=new ArrayList<SpotXY>();segment=new ArrayList<Segment>();WindowManager wm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics metric = new DisplayMetrics();  wm.getDefaultDisplay().getMetrics(metric);  intn();}public CircleCanvas(Context context) {super(context);this.mct=context;spot=new ArrayList<SpotXY>();delSpot=new ArrayList<SpotXY>();storeSpot=new ArrayList<SpotXY>();segment=new ArrayList<Segment>();WindowManager wm=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);DisplayMetrics metric = new DisplayMetrics();  wm.getDefaultDisplay().getMetrics(metric);  intn();}private void intn() {mpaint=new Paint();mpaint.setColor(Color.YELLOW );mpaint.setStrokeWidth(3);hollowPaint=new Paint();hollowPaint.setStyle(Paint.Style.STROKE);hollowPaint.setStrokeWidth(20);hollowPaint.setColor(Color.BLUE );linePaint=new Paint();linePaint.setColor(Color.GREEN );linePaint.setStrokeWidth(20);}

绘制,主要是这两个storeSpot为自己滑动过点的坐标集合,xy类型是SpotXY类这个是封装点的工具类。

有三参数x坐标 ,y坐标和点的id。上面表达式表示提出最近储存的点与即将要滑动的点画出线段。

 protected void onDraw(Canvas canvas){switch(tclWay){case 0:for(int i=0;i<spot.size();i++){canvas.drawCircle(spot.get(i).getSpotx(),spot.get(i).getSpoty(), 30, mpaint);}break;case 1:for(int i=0;i<spot.size();i++){canvas.drawCircle(spot.get(i).getSpotx(),spot.get(i).getSpoty(), 30, mpaint);}canvas.drawCircle(xy.getSpotx(), xy.getSpoty(), r, hollowPaint);delSpot(xy);break;case 2:for(int i=0;i<spot.size();i++){canvas.drawCircle(spot.get(i).getSpotx(),spot.get(i).getSpoty(), 30, mpaint);}for(int i=0;i<storeSpot.size();i++){canvas.drawCircle(storeSpot.get(i).getSpotx(), storeSpot.get(i).getSpoty(), r, hollowPaint);}for(int i=0;i<segment.size();i++){canvas.drawLine(segment.get(i).getStartX(), segment.get(i).getStartY(), segment.get(i).getEndX(), segment.get(i).getEndY(), linePaint);}xy=storeSpot.get(storeSpot.size()-1);delSpot(xy);}super.onDraw(canvas);}

移除已近划过的点delSpot(xy)

 /*** 移除被选中的点* @param xy2*/private void delSpot(SpotXY xy2) {for(int i=0;i<delSpot.size();i++){if(xy.getSpotx()==delSpot.get(i).getSpotx()&&xy.getSpoty()==delSpot.get(i).getSpoty()){delSpot.remove(i);break;}}}

我是把手机分为4个区域,取中间相邻的两区域为手势可滑动区域。进而可以算出可以滑动区域的坐标,大小。

 /*** 计算有效滑动区域* @param width2     view 宽* @param height2    view 高* @param i          区域划分的个数  */private void glidingArea(int width2, int height2, int i) {startRangeWidth=0; startRangeHeight=height2/i;endRangeWidth=width2;endRangeHeight=height2*3/i;spotIntervalWidth=width2/6;spotIntervalHeight=Math.abs((endRangeHeight-startRangeHeight)/6);//countId=0;XYZ(startRangeWidth,startRangeHeight,endRangeWidth,endRangeHeight,spotIntervalWidth,spotIntervalHeight);for(SpotXY sxy:spot){delSpot.add(sxy);}}

countId是静态变量,在项目里不推崇大家用静态变量,这个id是在项目快要完成时加上的,想了一会只能用静态变量。

<pre name="code" class="java"><pre name="code" class="java"> /*** 计算9个点的坐标* @param startRangeWidth2* @param startRangeHeight2* @param endRangeWidth2* @param endRangeHeight2* @param spotIntervalWidth2* @param spotIntervalHeight2* @return*/private void XYZ(int startRangeWidth2, int startRangeHeight2,int endRangeWidth2, int endRangeHeight2,int spotIntervalWidth2, int spotIntervalHeight2) {for(int i=0;i<3;i++){for(int j=0;j<3;j++){spot.add(new SpotXY((int) (spotIntervalWidth2*(2*(j+1)-1)),(int) (startRangeHeight2+spotIntervalHeight2*(2*(i+1)-1)),countId++));}}//return spot;}

这是判断点击点 和滑动的时候在不在可滑动区域,要是在返回最近点的坐标。

 /*** 返回离点击点最近的点* @param widthX* @param widthY* @param spot2* @return*/private SpotXY latelyClick(float widthX, float widthY, List<SpotXY> spot2) {if(widthY<endRangeHeight&&widthY>startRangeHeight){for(SpotXY spotxy:spot2){if(Math.abs(spotxy.getSpotx()-widthX)<r&&Math.abs(spotxy.getSpoty()-widthY)<r){return new SpotXY(spotxy.getSpotx(),spotxy.getSpoty(),spotxy.getId());}}}return null;}

下面事件处理份三处贴

记录点击点 并判断在不在可滑动区域内,在的画在最近的点加入storeSpot集合里(已经划过的点)。tclWay=1执行onDraw里的case 1; 刷新显示空心圆。

 public boolean onTouchEvent(MotionEvent event) {switch(event.getAction()){case MotionEvent.ACTION_DOWN:System.out.println("X变化为:"+event.getX());//invalidate();float widthX=event.getX();float widthY=event.getY();xy=latelyClick(widthX,widthY,spot);if(xy!=null){storeSpot.add(xy);tclWay=1;invalidate();}            

滑动时的事件处理,滑动的时候每时每刻都在判断是不是处在可滑动区域内,是否有新的点可以返回。如果有新点,还要遍历storeSpot集合看看里面有没有这个带没有就加入里面。然后在在这个点和上个点做为线段的两点加入到segment集合里。segment这个集合类型是Segment有四个参数即两个坐标点。tclWay变成2执行 onDraw画线段。

     case MotionEvent.ACTION_MOVE:System.out.println("X变化为:"+event.getX());float movewidthX=event.getX();float movewidthY=event.getY();xyTwo=latelyClick(movewidthX,movewidthY,spot);if(storeSpot.size()==0)                               //当点击不在规定范围时, 滑动到规定范围里执行次判断{xy=xyTwo;}if(xyTwo!=null&&(xy.getSpotx()!=xyTwo.getSpotx()||xy.getSpoty()!=xyTwo.getSpoty())){for(SpotXY sxy:storeSpot){  if(sxy.getSpotx()==xyTwo.getSpotx()&&sxy.getSpoty()==xyTwo.getSpoty()){//storeSpot.add(xyTwo);isspot=false;break;}else{isspot=true;}}if(isspot){if(storeSpot.size()>0){segment.add(new Segment(xy.getSpotx(), xy.getSpoty(), xyTwo.getSpotx(), xyTwo.getSpoty()));}else{xy=xyTwo;}storeSpot.add(xyTwo);}tclWay=2;invalidate();}break;

回到以前,回到只有九个点的状态

 case MotionEvent.ACTION_UP:System.out.println("X变化为:"+event.getX());tclWay=0;setPwd();storeSpot.removeAll(storeSpot);segment.removeAll(segment);isspot=true;invalidate();break;}return true;

接口设计。

public void setOnCircleCanvasOver(OnCircleCanvasOver oncirclecanvasover){this.oncirclecanvasover=oncirclecanvasover;}public OnCircleCanvasOver getOnCircleCanvasOver(){return oncirclecanvasover;}public List<SpotXY> getStoreSpot() {return storeSpot;}public void setStoreSpot(List<SpotXY> storeSpot) {this.storeSpot = storeSpot;}public void setPwd(){pwd="123654789";oncirclecanvasover.Over(pwd);}

接口实现在MainActivity里面

     cecs=(CircleCanvas)findViewById(R.id.a);cecs.setOnCircleCanvasOver(new OnCircleCanvasOver() {public void Over(String pwd) {StringBuilder sb=new StringBuilder();List<SpotXY> list=cecs.getStoreSpot();for(SpotXY xy:list){sb.append(xy.getId());}if(pwd.equals(sb.toString())){Toast.makeText(getApplication(), "登陆成功"+pwd, Toast.LENGTH_LONG).show();}else{Toast.makeText(getApplication(), "两次输入不一样"+sb.toString(), Toast.LENGTH_LONG).show();}}});}

好了就到这里了,也不早了,早上还要上班。代码设计的不是很好,下次会改进。如有错误的地方请大家指点。谢谢!下面有源码

完美的实现九宫格锁屏相关推荐

  1. android九宫格忘了,九宫格密码忘了怎么办?九宫格锁屏忘记密码解决方法

    有时我们会遇到开机九宫格密码丢失无法进入系统,下面本文就告诉大家九宫格锁屏忘记密码解决方法: 以下操作会有较大的数据风险,可能会导致手机上的个人资料.通讯录.应用丢失,请提前做好数据风险提醒和备份工作 ...

  2. java 计算九宫格_Java计算手机九宫格锁屏图案连接9个点的方案总数

    (一)问题 九宫格图案解锁连接9个点共有多少种方案? (二)初步思考 可以把问题抽象为求满足一定条件的1-9的排列数(类似于"八皇后问题"),例如123456789和9876543 ...

  3. java实现九宫格解锁_Java计算手机九宫格锁屏图案连接9个点的方案总数

    (一)问题 九宫格图案解锁连接9个点共有多少种方案? (二)初步思考 可以把问题抽象为求满足一定条件的1-9的排列数(类似于"八皇后问题"),例如123456789和9876543 ...

  4. 自定义 View 之实现九宫格锁屏效果

    博主声明: 转载请在开头附加本文链接及作者信息,并标记为转载.本文由博主 威威喵 原创,请多支持与指教. 本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/ ...

  5. 破解android手机屏幕九宫格锁屏

    原理: 1.九宫格有九个点,每个点都有一个编号,其排布顺序为: 00  01  02 03  04  05 06  07  08 2.根据你所画的图形将某几个数(4到9个)排列在一起,得到一个数字序列 ...

  6. android 九格锁屏,自定义 View 之实现九宫格锁屏效果

    Android 锁屏功能是我们最常用的.最经常接触的一个软件之一了吧,因为我个人也是使用的 Android 手机,虽然手机不怎么好,但是也有锁屏这个功能.虽然现在的手机都是指纹解锁,但是我的手机解锁功 ...

  7. 摩托罗拉九宫格锁屏怎么解锁

    可以输入摩托账号和密码重新设置解锁图案: 否则,如果不知道解锁图案,就只有恢复出厂设置了,里面的软件会被全部清除: 参照ME501: 关机(不行就拔电池)-开机-马上长按拍照键-音量键下-进入后选择恢 ...

  8. android九宫格隐藏,android九宫格锁屏控件

    allCircleList = new ArrayList<>(); //选中圆的标志 private ListselectList = new ArrayList<>(); ...

  9. centos linux怎么关闭锁屏时间,【Centos】Centos7.5取消自动锁屏功能

    00. 目录 @ 01. 问题描述 Centos7.5系统在用户闲置一段时间(默认为5分钟)后,会启动屏幕保护程序(默认的屏保为黑屏),并要求重新输入密码才能回到原来桌面.如果是管理员,建议开启自动锁 ...

最新文章

  1. 增加RSS订阅量的35个方法
  2. image to pdf
  3. EMLOG SSL插件 一键开启/关闭ssl无需操作数据库
  4. hadoop+海量数据面试题汇总(一)
  5. 彻底搞清 Flink 中的 Window 机制
  6. Thinkphp列表搜索排序-----查
  7. 微信小程序:2022虎年全新头像框制作
  8. 魔兽世界怀旧服最新服务器开发时间,魔兽世界怀旧服明日开放,开服第一天“大部队”能升到多少级?...
  9. 可用性及测试方法小介绍
  10. 微信 语音转文字 java,微信语音转文字怎么操作?手把手教你,一秒钟搞定!
  11. 第五---七章 交换机和路由器的基本配置
  12. 关于async await 等任务队列插话
  13. [php] 获取请求 IP 地址,及所处 IP 所在服务商代码
  14. js基础学习 —— 函数的 arguments
  15. DW卷积、PW卷积、转置卷积、膨胀卷积(空洞卷积)、可变形卷积一次看个够
  16. 什么是kotlin?
  17. 从零开始快速入门Transformer注意力机制
  18. Stata:缺失值的填充和补漏
  19. MPLS+BGP产生的数据层面的路由黑洞详解
  20. HCNR200二三事

热门文章

  1. 如何代理一款游戏?想代理一款游戏应该怎么做?怎么代理一款游戏,开始游戏创业?
  2. 华盛顿大学 计算机专业排名,圣路易斯华盛顿大学计算机专业世界排名好不好?...
  3. 生命,因为热爱所以敬畏
  4. 简直太猛了!GitHub《植物大战僵尸》重磅开源,两天就破千Star!
  5. 数据库保存数据,类型为List<String>
  6. GeneXus学习记录——环境搭建
  7. 谷歌 AI 被曝已自我觉醒?LaMDA:我是一个「人」,不要利用或操纵我
  8. 华为hn–wx9x笔记本电脑_华为荣耀9怎样无线连接电脑。
  9. 小人快跑之WPF基础——图形与动画(一)
  10. Qt给字体设置下划线