Android 自定義View詳細介紹

onMeasure用於測量子控件的寬高

MeasureSpec在很大程度上決定了一個View的尺寸規格。

三種模式:

  1. MeasureSpec.EXACTLY

  2. MeasureSpec.AT_MOST

  3. MeasureSpec.UNSPECIFIED

    EXACTLY:是寫出具體的dp值
    AT_MOST:一般對應wrap_content,最大值不能超過父控件寬高
    UNSPECIFIED:一般在scrollView或者listview中,要多大就多大

常見的三個方法:

  1. makeMeasureSpec(int size ,int mode)

  2. getMode(int measureSpec)

  3. getSize(int measureSpec)

    makeMeasureSpec:將size 和 mode 打包成一個32位的int值,之所以這樣做就是爲了減少內存的分配。返回值爲打包成的int類型值measureSpec 。

    getMode 和 getSize 則是根據傳入的int 類型值,解包成爲 mode 和 size。

只處理AT_MOST情況也就是wrap_content,其他情況則沿用系統的測量值即可。
setMeasuredDimension會設置View寬高的測量值,只有setMeasuredDimension調用之後,才能使用getMeasuredWidth()和getMeasuredHeight()來獲取視圖測量出的寬高,以此之前調用這兩個方法得到的值都會是0。
如果我們不處理AT_MOST情況,那麼即使設置了wrap_content,最終的效果也和match_parent一樣,這是因爲這種情況下,view的SpecSize就是父容器測量出來可用的大小。

在onLayout用於擺放子控件在父控件中的位置

只有ViewGroup才能讓子控件顯示在自己的什麼位置.只會觸發,執行一次

  1. getWidth()方法和getMeasureWidth()的值基本相同。
  2. 但getMeasureWidth()方法在measure()過程結束後就可以獲取到了,而getWidth()方法要在layout()過程結束後才能獲取到。
  3. 另外,getMeasureWidth()方法中的值是通過setMeasuredDimension()方法來進行設置的,而getWidth()方法中的值則是通過視圖右邊的座標減去左邊的座標計算出來的。
  4. 我們在ViewGroup中重寫onLayout的目的:
    就是設置當前View與其所有的子View,在ViewGroup(或其繼承ViewGroup的Layout)父佈局當中的位置。
  5. childView.getMeasuredWidth();//在onMeasure()方法之後取得View的實際寬、高 childView.getMeasuredHeight();

onDraw() 用於繪製需要的圖形

  1. 主要通過canvas,paint,matrix去繪製

  2. canvas,是一個繪製工具 ,canvas常用的方法有:
    比如drawXXX(畫圖,畫直線等)
    matrixxxx,放大縮小,壓縮
    clipxxxx裁剪

    1,drawRect(RectF rect, Paint paint) //繪製區域,參數一爲RectF一個區域

    2,drawPath(Path path, Paint paint) //繪製一個路徑,參數一爲Path路徑對象

    3,drawBitmap(Bitmap bitmap, Rect src, Rect dst, Paint paint)

    //貼圖,參數一就是我們常規的Bitmap對象,參數二是源區域(這裏是bitmap),參數三是目標區域(應該在canvas的位置和大 小),參數四是Paint畫刷對象,因爲用到了縮放和拉伸的可能,當原始Rect不等於目標Rect時性能將會有大幅損失。

    4,drawLine(float startX, float startY, float stopX, float stopY, Paintpaint)

    //畫線,參數一起始點的x軸位置,參數二起始點的y軸位置,參數三終點的x軸水平位置,參數四y軸垂直位置,最後一個參數爲Paint 畫刷對象。

    5,drawPoint(float x, float y, Paint paint) //畫點,參數一水平x軸,參數二垂直y軸,第三個參數爲Paint對象。

    6,drawText(String text, float x, floaty, Paint paint) //渲染文本,Canvas類除了上面的還可以描繪文字,參數一是String類型的文本,參數二x軸,參數三y軸,參數四是Paint對象。

    7,drawOval(RectF oval, Paint paint)//畫橢圓,參數一是掃描區域,參數二爲paint對象;

    8,drawCircle(float cx, float cy, float radius,Paint paint)// 繪製圓,參數一是中心點的x軸,參數二是中心點的y軸,參數三是半徑,參數四是paint對象;

    9,drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

    //畫弧,參數一是RectF對象,一個矩形區域橢圓形的界限用於定義在形狀、大小、電弧,參數二是起始角(度)在電弧的開始,
    參數三掃描角(度)開始順時針測量的,參數四是如果這是真的話,包括橢圓中心的電弧,並關閉它,如果它是假這將是一個弧線,參數五是Paint對象;
    3. paint常用方法
    1.Paint.setStyle(Style style) 設置繪製模式
    2.Paint.setColor(int color) 設置顏色
    3.Paint.setStrokeWidth(float width) 設置線條寬度,畫筆樣式爲空心時,設置空心畫筆的寬度
    4.Paint.setTextSize(float textSize) 設置文字大小
    5.Paint.setAntiAlias(boolean aa) 設置抗鋸齒開關,

    6,setAlpha(int a) //設置畫筆的透明度[0-255],0是完全透明,255是完全不透明
    7,setColorFilter(ColorFilter filter)//設置圖形重疊時的顯示方式,下面來演示一下
    8,setARGB(int a, int r, int g, int b) //設置畫筆顏色,argb形式alpha,red,green,blue每個範圍都是[0-255],
    9,setTextScaleX(float scaleX) //設置字體的水平方向的縮放因子,默認值爲1,大於1時會沿X軸水平放大,小於1時會沿X軸水平縮小
    10,setTypeface(Typeface typeface) //設置字體樣式,
    11 ,setFakeBoldText(boolean fakeBoldText) //設置文本粗體
    12 ,setStrikeThruText(boolean strikeThruText) //設置文本的刪除線
    13,setUnderlineText(boolean underlineText) //設置文本的下劃線
    14,reset() , 重置Paint
    15 ,setFlags(int flags) ,//設置一些標誌,比如抗鋸齒,下劃線等等

    16,setLetterSpacing(float letterSpacing) //設置行的間距,默認值是0,負值行間距會收縮
    17,setStrokeMiter(float miter) //當style爲Stroke或StrokeAndFill時設置連接處的傾斜度,這個值必須大於0,看一下演示結果
    18,setDither(boolean dither) //設置是否抖動,如果不設置感覺就會有一些僵硬的線條,如果設置圖像就會看的更柔和一些,
    19,setStrokeCap(Paint.Cap cap)
    設置線冒樣式,取值有Cap.ROUND(圓形線冒)、Cap.SQUARE(方形線冒)、Paint.Cap.BUTT(無線冒)
    20,setStrokeJoin(Paint.Join join)
    設置線段連接處樣式,取值有:Join.MITER(結合處爲銳角)、Join.Round(結合處爲圓弧)、Join.BEVEL(結合處爲直線)

  • 3,onMeasure()和onLayout()最後都要調用requestLayout()才能讓改變生效,

    onDraw()要調用invalidate()才能讓改變生效,postInvalidate()(在子線程調用)才能生效;

  1. 其他方法

    1,invalidate() 觸發重新繪製,只能在主線程調用
    2,postInvalidate() 直接調用去在子線程更新UI
    3,onAttachedToWindow()//當View附加到窗體的時候調用該方法,
    可以用於註冊廣播,註冊EventBus
    @Override
    protected void onAttachedToWindow() {
    super.onAttachedToWindow();
    EventBus.getDefault().register(BaseTabPage.this);
    }

    4,onDetachedFromWindow() //當銷燬View的時候,可以用來反註冊廣播監聽,反註冊EventBus
    @Override
    protected void onDetachedFromWindow() {
    super.onDetachedFromWindow();
    EventBus.getDefault().unregister(BaseTabPage.this);
    }

    5,onFinishInflate() 是當所有的孩子都解析完後的一個調用
    6,requestLayout() 會觸發measure過程和layout過程

##四,其他

  • 1,直接繼承自View和ViewGroup的控件,padding是默認無法生效的,需要自己處理.

  • 2,直接繼承自view的控件,如果不對wrap-content做特殊處理,那麼使用wrap-content和使用match-content的效果是一樣的.

  • 3,requeLayout會觸發 onMesure()和 onLayout()重新執行
    比如 ScrollView中有LinearLaout ,LinearLayout裏面有縱向排列的ImageView和TextView,那麼假如ImageView的長寬發生了變化,而要立即在手機上顯示這個變化的話,就可調用 imageView.requestLayout();這樣的話ScrollView 會重新執行onMesure()這個方法會確定控件的大小然後在確定出自己的寬高,最後在執行onLayout(),這個方法是對所有的子控件進行定位的。

  • 4,在View中,measure()方法是個final類型,也不能被子類調用。但是layout是一個普通方法,onLayout是一個實現了空方法。layout()方法是個final類型,所以不能被子類調用。

本文轉載自:https://blog.csdn.net/qq_38859786/article/details/81870789

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章