日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

完美的实现九宫格锁屏

發布時間:2023/12/15 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 完美的实现九宫格锁屏 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

? ? ? ? 第一次寫博客,寫的不好,知識點不到位的請大神,大牛指點。不廢話了直接進入主題。

先看下效果圖:



大致說下我的思路:

一:對題目的思考。

二:繼承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();}}});}

好了就到這里了,也不早了,早上還要上班。代碼設計的不是很好,下次會改進。如有錯誤的地方請大家指點。謝謝!下面有源碼

?

總結

以上是生活随笔為你收集整理的完美的实现九宫格锁屏的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。