完美的实现九宫格锁屏
? ? ? ? 第一次寫博客,寫的不好,知識點(diǎn)不到位的請大神,大牛指點(diǎn)。不廢話了直接進(jìn)入主題。
先看下效果圖:
大致說下我的思路:
一:對題目的思考。
二:繼承View 主要重寫 onDraw(Canvas),onMeasure(int,int),oTouchEvent(MotionEvent);三個方法;
三:回調(diào)。
我們來說明下;
九宮格是個控件,是個單一的控件,不是一組控件,所以我直接繼承view ,而不繼承ViewGroup。
要自定義控件必須要重寫測量方法 onMeasure(int,int).
是不是這樣得到控件width,height很簡單,其實(shí)onMeasure這樣寫是有局限性的,你的控件必須要設(shè)定大小和fill_parent才可以這樣使用。
如果要是wrap_content,就不可以這樣用了,
<pre name="code" class="java">/*** 作用是返回一個默認(rèn)的值,如果MeasureSpec沒有強(qiáng)制限制的話則使用提供的大小.否則在允許范圍內(nèi)可任意指定大小* 第一個參數(shù)size為提供的默認(rèn)大小,第二個參數(shù)為測量的大小*/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時使用提供的默認(rèn)大小case MeasureSpec.UNSPECIFIED:result = size;break;case MeasureSpec.AT_MOST:// Mode = EXACTLY時使用測量的大小 case MeasureSpec.EXACTLY:result = specSize;break;}return result;}
這是在網(wǎng)上找的,可以看到當(dāng)模式為MeasureSpec.UNSPECIFIED時,即未指明大小或warp_content,測量的結(jié)果為size;
而size為多少呢?
/*** 這個方法需要被重寫,應(yīng)該由子類去決定測量的寬高值,*/ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }
也就是這個size=getSuggestedMinimuWidth()和size=getSuggestedMinimuHeight()。
這兩方法是View的兩保護(hù)方法,下面是源碼
*/ 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
是不是發(fā)現(xiàn)什么。getMeasuredWidth()和getMeasuredHeight()兩方法得到的值就是getDefaultSize()的返回值。
所以這兩個方法與模式?jīng)]關(guān)系。所以在自定義測量的時候可以直接用這兩個方法。
不懂在上網(wǎng)查查,onMeasure(int ,int)先到這里。
再來說說onDraw(Canvas)
這個ondraw方法在view初始化后調(diào)用繪制畫布。每當(dāng)控件增加新的圖案都會調(diào)用ondraw(canvas)方法,調(diào)用這個方法是重新繪制,而不是只繪制新增的部分。要在UI線程調(diào)用
invalidate()刷新畫布才可以顯示。Canvas是畫圖,創(chuàng)建Canvas有兩種方法。一個是當(dāng)做onDraw參數(shù)傳進(jìn)去,
另一中是:?Bitmap b=Bitmap.createBitmap(200,200,Bitmap.Config.ARGB_888);
Canvas c=new Canvas(b);創(chuàng)建一個200*200的Bitmap當(dāng)做Canvas操作畫布。
不懂的可以上網(wǎng)查查。
我在細(xì)說一下我的代碼:
最后一個參數(shù)為接口,
<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; //起點(diǎn)坐標(biāo)(x,y)private int startRangeHeight;private int endRangeWidth; //終點(diǎn)坐標(biāo)(X,Y)兩個坐標(biāo)構(gòu)成滑動區(qū)域。private int endRangeHeight;private int spotIntervalWidth; //每個點(diǎn)之間的間隔private int spotIntervalHeight; private List<SpotXY> spot; //存放9個點(diǎn)的坐標(biāo)private List<SpotXY> delSpot; //作用方面顯示畫的結(jié)果private List<SpotXY> storeSpot; //儲存選中的空心圓的坐標(biāo) 即spotprivate List<Segment> segment; //儲存畫出的線段 private int i=0;private float r=100.0f; //大圓半徑private SpotXY xy; //一個點(diǎn)private SpotXY xyTwo;private int tclWay=0;private boolean isspot=true;private static int countId=1; //每個點(diǎn)的id編號private String pwd; //密碼private OnCircleCanvasOver oncirclecanvasover; 這個參數(shù)在儲存畫的線段的時候會用的,判斷不要重復(fù)儲存一樣的線段private static int countId=1; //每個點(diǎn)的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為自己滑動過點(diǎn)的坐標(biāo)集合,xy類型是SpotXY類這個是封裝點(diǎn)的工具類。
有三參數(shù)x坐標(biāo) ,y坐標(biāo)和點(diǎn)的id。上面表達(dá)式表示提出最近儲存的點(diǎn)與即將要滑動的點(diǎn)畫出線段。
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);}移除已近劃過的點(diǎn)delSpot(xy)
/*** 移除被選中的點(diǎn)* @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;}}}
? 我是把手機(jī)分為4個區(qū)域,取中間相鄰的兩區(qū)域?yàn)槭謩菘苫瑒訁^(qū)域。進(jìn)而可以算出可以滑動區(qū)域的坐標(biāo),大小。
/*** 計算有效滑動區(qū)域* @param width2 view 寬* @param height2 view 高* @param i 區(qū)域劃分的個數(shù) */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是靜態(tài)變量,在項(xiàng)目里不推崇大家用靜態(tài)變量,這個id是在項(xiàng)目快要完成時加上的,想了一會只能用靜態(tài)變量。
<pre name="code" class="java"><pre name="code" class="java"> /*** 計算9個點(diǎn)的坐標(biāo)* @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;}這是判斷點(diǎn)擊點(diǎn) 和滑動的時候在不在可滑動區(qū)域,要是在返回最近點(diǎn)的坐標(biāo)。
/*** 返回離點(diǎn)擊點(diǎn)最近的點(diǎn)* @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;}下面事件處理份三處貼
記錄點(diǎn)擊點(diǎn) 并判斷在不在可滑動區(qū)域內(nèi),在的畫在最近的點(diǎn)加入storeSpot集合里(已經(jīng)劃過的點(diǎn))。tclWay=1執(zhí)行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();}滑動時的事件處理,滑動的時候每時每刻都在判斷是不是處在可滑動區(qū)域內(nèi),是否有新的點(diǎn)可以返回。如果有新點(diǎn),還要遍歷storeSpot集合看看里面有沒有這個帶沒有就加入里面。然后在在這個點(diǎn)和上個點(diǎn)做為線段的兩點(diǎn)加入到segment集合里。segment這個集合類型是Segment有四個參數(shù)即兩個坐標(biāo)點(diǎn)。tclWay變成2執(zhí)行 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) //當(dāng)點(diǎn)擊不在規(guī)定范圍時, 滑動到規(guī)定范圍里執(zhí)行次判斷{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;回到以前,回到只有九個點(diǎn)的狀態(tài)
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;接口設(shè)計。
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);}接口實(shí)現(xiàn)在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();}}});}好了就到這里了,也不早了,早上還要上班。代碼設(shè)計的不是很好,下次會改進(jìn)。如有錯誤的地方請大家指點(diǎn)。謝謝!下面有源碼
?
總結(jié)
以上是生活随笔為你收集整理的完美的实现九宫格锁屏的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 联想y7000-2019黑苹果安装笔记(
- 下一篇: 四人小组项目申请