麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 學(xué)院 > 開發(fā)設(shè)計 > 正文

自定義View系列的總結(jié)

2019-11-09 16:17:47
字體:
供稿:網(wǎng)友

在自定義View系列中以上9篇都是”谷歌的小弟”的原創(chuàng)博文,在這個系列教程中對大部分知識點都做了詳細(xì)的闡述。在我通讀了以上文章后受益匪淺啊,原理明白了就算不強記以后很容易能想得到但是一些用法之類的查找確實麻煩,所以再來一篇總結(jié),結(jié)合自己的理解和應(yīng)用加深理解且以后回憶起來也提供一條思路。順序就按照小弟的來。

常用的工具類

Configuration.class:設(shè)備信息類,用來描述設(shè)備的配置信息,比如用戶的配置信息,設(shè)備的相關(guān)信息(輸入模式、屏幕方向)。//獲取對象Configuration configuration = getResources().getConfiguration();//用戶LocaleLocale locale = configuration.locale;//信號的國家碼int mcc = configuration.mcc;//信號的網(wǎng)絡(luò)碼int mnc = configuration.mnc;//橫豎屏int screen = configuration.orientation;ViewConfiguration.class:它提供了自定義View可能用到的一些標(biāo)準(zhǔn)常量,比如尺寸大小、靈敏度。舉個例子,獲取距離至少為多大系統(tǒng)才會認(rèn)為是滑動而不是點擊。它提供了一系列的方法供我們查詢這些標(biāo)準(zhǔn)常量,既然是常量便是不可修改的。//獲取對象ViewConfiguration viewConfiguration = ViewConfiguration.get(mContext);//對象方法-系統(tǒng)識別滑動的最小距離int touchSlop = viewConfiguration.getScaledTouchSlop();//對象方法-是否有物理按鍵boolean flag = viewConfiguration.haspermanentMenuKey();//靜態(tài)方法-雙擊間隔時間,在時間內(nèi)判定為雙擊,超出為兩次單擊int doubleTimeout = ViewConfiguration.getDoubleTapTimeout();//靜態(tài)方法-按住變成長按動作需要的時間int longPRessTimeout = ViewConfiguration.getLongPressTimeout();GestureDetector.class:這個類是用來簡化Touch處理,可以實現(xiàn)一些常用的操作,比如拖動,滑動等。通過設(shè)置GestureDetector.OnGestureListener來監(jiān)聽手勢,我們通過實現(xiàn)接口代碼處理相應(yīng)手勢動作下的邏輯。/** * step1:實現(xiàn)GestureDetector.OnGestureListener */ //觸摸屏幕時均會調(diào)用該方法 boolean onDown(MotionEvent e);//手指在屏幕上拖動時會調(diào)用該方法 boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);//手指長按屏幕時均會調(diào)用該方法public void onLongPress(MotionEvent e);//手指在屏幕上滾動時會調(diào)用該方法public boolean onScroll(MotionEvent e1,MotionEvent e2, float distanceX,float distanceY);//手指在屏幕上按下,且未移動和松開時調(diào)用該方法public void onShowPress(MotionEvent e);//輕擊屏幕時調(diào)用該方法public boolean onSingleTapUp(MotionEvent e);/** * step2:生成GestureDetector對象 */ GestureDetector gestureDetector = new GestureDetector(context,newGestureListenerImpl());/** * step3:將View的onTouch時間交由GestureDetector處理 */ @Override public boolean onTouchEvent(MotionEvent event) { return mGestureDetector.onTouchEvent(event); } VelocityTracker.class:速度追蹤器。用于跟蹤觸摸屏事件的速率。private void startVelocityTracker(MotionEvent event) { /** * step1-開始追蹤,追蹤誰? */ VelocityTracker velocityTracker = VelocityTracker.obtain(); velocityTracker.addMovement(event); /** * step2-追蹤處理,獲取具體的數(shù)值 */ //設(shè)置VelocityTracker單位.1000表示1秒時間內(nèi)運動的像素 velocityTracker.computeCurrentVelocity(1000); //獲取在1秒內(nèi)X方向所滑動像素值 int xVelocity = (int) velocityTracker.getXVelocity(); //獲取追蹤到的速度 int velocity_x = Math.abs(xVelocity); /** * step3-釋放 */ if (velocityTracker != null) { velocityTracker.recycle(); velocityTracker = null; }}

Scroller.class:下節(jié)總結(jié)。

ViewDragHelper.class:處理拖拽動作的類。

mViewDragHelper = ViewDragHelper.create(this, 1.0f, new ViewDragHelper.Callback() {});

創(chuàng)建實例,三個參數(shù)基本就是這三個。對應(yīng)的內(nèi)部方法。

/** * 唯一的抽象接口 * 1. 返回true表示可以捕獲這個View的動作 * 2. 我們可以通過它的參數(shù)來判斷哪些 */public abstract boolean tryCaptureView(View child, int pointerId);/** * 處理水平方向的越界 * 1. 返回值是我們最終拖拽的距離 * 2. 參數(shù)left是手勢拖拽的距離 * 3. 判定X是否越界, * 最小位移min: paddingLeft * 最大位移max:parent.width-paddingRight-childView.width * a. left<min return min; * b. left>max return max; * c. else return left; */public int clampViewPositionHorizontal(View child, int left, int dx);/** * 處理垂直方向的越界(參照水平方向) */ public int clampViewPositionVertical(View child, int top, int dy);/** * 捕獲子View動作 */ public void onViewCaptured(View capturedChild, int activePointerId);/** * 釋放子View動作 */ public void onViewReleased(View releasedChild, float xvel, float yvel);

提供一個圖給大家腦補…

這里寫圖片描述

/*** ViewGroup的事件分發(fā)交給ViewDragHelper處理*/@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) { return mDragHelper.shouldInterceptTouchEvent(ev);}/*** ViewGroup的事件消費交給ViewDragHelper處理*/@Overridepublic boolean onTouchEvent(MotionEvent event) { mDragHelper.processTouchEvent(event); return true;}

onMeasure()測量過程

MeasureSpec.class

封裝了parentView對childView布局的要求;32位,高2位是mode,低30位是size;EXACTLY,精確模式,已知大小AT_MOST,未檢測出大小,但最大不超過其sizeUNSPECIFIED,不做考慮

View的測量過程,我們通過源碼可以知道,View的大小不僅僅它本身的布局有關(guān),還和parentView的MeasureSpec相關(guān),view的size由parentView的MeasureSpec.mode和其本身的布局共同決定,對源碼的分析可以得出以下結(jié)論。

① 當(dāng)View的寬高為具體值value時,不管其parentView的MeasureSpec.mode。 size = value; mode = EXACTLY;② 當(dāng)View的寬高布局為match_parent,parentView.mode為EXACTLY。 size = parentLeftSize; mode = EXACTLY;③ 當(dāng)View的寬高布局為match_parent,parentView.mode為AT_MOST。 size = parentLeftSize; mode = AT_MOST;

④ 當(dāng)View的寬高布局為wrap_content,不管其parentView的MeasureSpec.mode。 size = parentLeftSize; mode = AT_MOST; 這里寫圖片描述

parentLeftSiz表示parentView剩下的空間;

onMeasure()分析 在了解了MeasureSpec后,我們具體分析View如何獲取大小的。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension( getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec), getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec)); }protected int getSuggestedMinimumWidth()。看是否有background,沒有的話size=minWidth(或minHeight),有的話取兩者中較大的值為size;public static int getDefaultSize(int size, int measureSpec)。可知size=MeasureSpec.getSize(measureSpec);就是說onMeasure()過程中View的size是由ViewGroup()繪制 protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed);的時候設(shè)置的size決定的,也就是這個圖。

這里寫圖片描述

我們看到情況④,當(dāng)childView的布局為wrap_content時,childView的size一直都是parentLeftSize,即父控件剩余空間,也可以說是match_parent,就是填滿空間了…,所以我們在自定義View時如果不對這種情況進行處理的話就會造成我們的自定義View布局為wrap_content時,實際效果是match_parent;為什么TextView等等控件在上述情況表現(xiàn)正常呢,這是因為在源碼中已經(jīng)對上述情況作了處理。那么我們應(yīng)該如何處理?@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = 100; int height = 200; int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if( widthMode==MeasureSpec.AT_MOST && heightMode==MeasureSpec.AT_MOST ) setMeasuredDimension(width, height); else if (widthMode==MeasureSpec.AT_MOST) setMeasuredDimension(width, heightSize); else if ( heightMode==MeasureSpec.AT_MOST ) setMeasuredDimension(widthSize, height);}我們在重寫onMeasure()方法時,利用判斷childView.mode來甄別布局是否為wrap_content。當(dāng)mode為AT_MOST時我們就認(rèn)為是wrap_content。但是我們查看圖表可知,情況③也是AT_MOST模式,但是它的屬性是match_parent,Why?其實我們通過反證法,可以得出結(jié)論此種情況是不存在(不合理)的,所以正常情況不會出現(xiàn),所以可以忽略。

onLayout()擺放過程

View經(jīng)過onMeasure()階段之后,會進入到onLayout()確定View的位置過程。先要確定View應(yīng)該放在哪里我們需要理清思路。 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {};方法該方法在源碼中為空,且源碼給出的注釋說明,我們調(diào)用該方法去確定子View的位置,也就是說子View的位置是由其父View中的onLayout()方法確定的,通過查看父View的onLayout()源碼,我們知道該方法為抽象方法,即繼承ViewGroup的View(如LinearLayout等)需要按照自己的規(guī)則去實現(xiàn)onLayout()方法。所以自定義ViewGroup時,我們在onLayout()方法中我們根據(jù)parentView的參數(shù)等去計算所有chiilView的位置,并且在得到具體位置的值時調(diào)用childView的layout()確定位置(即第一步)。

有兩個方法有必要討論一下,getMeasuredWidth()和getWidth()方法。它們有何區(qū)別。

getMeasuredWidth()方法查看源碼,return mMeasuredWidth變量,查找源碼可以看出此變量在 private void setMeasuredDimensionRaw(int measuredWidth, int measuredHeight)方法中給賦值的,在網(wǎng)上查發(fā)現(xiàn),’setMeasuredDimension()’方法調(diào)用了setMeasuredDimensionRaw(),而onMeasure()調(diào)用了setMeasuredDimension()。getWidth()方法查看源碼發(fā)現(xiàn),返回值是”mRight - mLeft”,且它在onLayout()過程后才有的值。那么,getMeasuredWidth()方法的值是在onMeasure()測量過程后獲取的大小。并且它的值和我們在setMeasuredDimension()設(shè)置的值相關(guān)。那么,getWidth()方法的值是在onLayout()擺放過程之后獲取的大小,并且它的值是坐標(biāo)相減的結(jié)果。

總結(jié)完onDraw()之后,會有例子體現(xiàn)。

onDraw()繪畫過程

繪制的過程從以下幾個方面來簡述。

源碼draw(canvas)繪制過程 - 從源碼注釋可看出View的繪制大體上分為6步。 - 1. 繪制背景drawBackground(canvas); - 2. 保存當(dāng)前畫布的堆棧狀態(tài)并在該畫布上創(chuàng)建Layer用于繪制View在滑動時的邊框漸變效果(可忽略); - 3. 繪制View的內(nèi)容,protected void onDraw(Canvas canvas) {};我們需要實現(xiàn)的方法: - 4. 畫子View,dispatchDraw(canvas); - 5. 繪制當(dāng)前視圖在滑動時的邊框漸變效果(可忽略); - 6. 繪制View的滾動條;

Canvas、Bitmap、Paint的關(guān)系 - 源碼中看出我們繪制View時重寫onDraw()方法,而方法中只有參數(shù)Canvas,查看官方文檔可以得到繪制4要素 - 1. 用什么工具畫? Paint類 。 - 2. 把畫畫在哪里?Bitmap上,Bitmap承載和呈現(xiàn)了畫的各種圖形。 - 3. 畫的內(nèi)容?根據(jù)自己的需求畫圓,畫直線,畫路徑。 - 4. 怎么畫? canvas各種操作。

Canvas類的常用操作 - canvas.translate(x, y); 移動坐標(biāo)系 - canvas.rotate(angle); 旋轉(zhuǎn)坐標(biāo)系 - canvas.clipXxx(); 裁剪某個形狀,就是把坐標(biāo)系放入到裁剪區(qū)域 - canvas.save()生成一個透明的圖層Layer; - canvas.restore()Layer操作的東西覆蓋到原來的圖形上;

PorterDuffXfermode圖形合成的規(guī)則 -

Bitmap和Matrix矩陣處理圖像 -

Shader渲染圖像 -

PathEffect畫路徑時樣式效果 -


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 最近免费观看高清韩国日本大全 | 91网站免费观看 | 美女黄污视频 | 久久亚洲精品久久国产一区二区 | 亚洲影院在线 | 玩偶姐姐在线观看免费 | 欧美黄色免费视频 | 露脸各种姿势啪啪的清纯美女 | 成人短视频在线观看免费 | 国产羞羞视频免费在线观看 | 一级毛片播放 | 精品国产久 | 久久久一区二区三区视频 | 亚洲国产超高清a毛毛片 | 精品国产一区二区久久 | 激情在线视频 | 中文字幕精品一区久久久久 | 久久久日韩av免费观看下载 | 午夜视频在线免费观看 | www.17c亚洲蜜桃 | 久国产精品视频 | 国产精品av久久久久久久久久 | 精品国产一区二区三区久久久 | 久久亚洲国产午夜精品理论片 | 色骚综合 | 在线观看福利网站 | 国产福利视频 | 欧美精品一区自拍a毛片在线视频 | 亚洲国产精品高潮呻吟久久 | 国产亚洲精品久久久久久久久久 | 欧美一级做一级爱a做片性 久久久资源网 | 亚洲日色 | 欧美18一19sex性护士农村 | 精品无码久久久久久国产 | 青青草最新网址 | 亚洲精品一区中文字幕 | 毛片一级免费看 | 视频久久免费 | 天天草天天干天天射 | 国产成人77亚洲精品www | 日韩av在线网|