android如何自定義控件「android自定義控件步驟」

在Android中,無論是熟知的布局,還是控件,統統全都繼承自基類View。

Android自定義控件

自定義View實現有幾種:

  • ① 自定義組合控件:多個控件組合成為一個新的控件,方便多處復用
  • ② 繼承系統View控件:繼承自TextView等系統控件,在系統控件的基礎功能上進行擴展
  • ③ 繼承View:不復用系統控件邏輯,繼承View進行功能定義
  • ④ 繼承系統ViewGroup:繼承自LinearLayout等系統控件,在系統控件的基礎功能上進行擴展
  • ⑤ 繼承ViewGroup:不復用系統控件邏輯,繼承ViewGroup進行功能定義

一、View的繪製流程

View的繪製基本由measure()、layout()、draw()這個三個函數完成:

  • measure:測量View的寬高,主要是View中的:measure(),setMeasuredDimension(),onMeasure()方法。
  • layout:計算當前View以及子View的位置,主要是View中的:layout(),onLayout(),setFrame()方法。
  • draw:視圖的繪製工作,主要是View中的:draw(),onDraw()方法。

二、Android 屏幕坐標系

在Android坐標系中,以屏幕左上角作為原點,這個原點向右是X軸的正軸,向下是Y軸正軸。

Android自定義控件

根據以上的坐標,結合View基類的相關API,涉及View的一些坐標方法,比如:

  • getTop:View到其父布局頂邊的距離
  • getLeft:View到其父布局左邊的距離
  • getBottom:View到其父布局頂邊的距離
  • getRight:View到其父布局左邊的距離

結合以上的API,可以計算出視圖的寬度和高度,可以使用如下方式計算:

  • width = getRight – getLeft;
  • height = getBottom – getTop

三、自定義View開發的步驟

這裡我們介紹最複雜的一種,自定義View。

3.1 構造函數

無論是繼承系統View還是直接繼承View,都需要對構造函數進行重寫,構造函數有多個,至少要重寫其中一個才行。

public class CustomView extends View{
  /**
  * 代碼中New實例化時 調用該方法
  */
  public CustomView(Context context){
    super(context);
  }
  
  /**
  * 在xml布局文件中使用時會自動調用該方法
  */
  public CustomView(Context context, AttributeSet attrs){
    super(context, attrs);
  }
  ...
  //更多參數的構造函數
}

3.2 自定義屬性

Android系統的控件以android開頭的都是系統自帶的屬性。為了方便配置自定義View的屬性,我們也可以自定義屬性值。Android自定義屬性可分為以下幾步:

  • 1、自定義一個View
  • 2、編寫values/attrs.xml,在其中編寫styleable和item等標籤元素
  • 3、在布局文件中View使用自定義的屬性(注意namespace)
  • 4、在View的構造方法中通過TypedArray獲取

attrs.xml文件示例如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="test">
    <attr name="text" format="string" />
    <attr name="testAttr" format="integer" />
  </declare-styleable>
</resources>

代碼實現示例如下:

public class MyTextView extends View {
    private static final String TAG = MyTextView.class.getSimpleName();

    //在View的構造方法中通過TypedArray獲取
    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);
        String text = ta.getString(R.styleable.test_testAttr);
        int textAttr = ta.getInteger(R.styleable.test_text, -1);
        Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);
        ta.recycle();
    }
}

布局文件使用示例如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res/com.example.test"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <com.example.test.MyTextView
        android:layout_width="100dp"
        android:layout_height="200dp"
        app:testAttr="520"
        app:text="helloworld" />
</RelativeLayout>

四、Measure()

MeasureSpec

MeasureSpec是View的內部類,它封裝了一個View的尺寸,在onMeasure()當中會根據這個MeasureSpec的值來確定View的寬高。MeasureSpec的值保存在一個int值當中。一個int值有32位,前兩位表示模式mode後30位表示大小size。即MeasureSpec = mode + size。

在MeasureSpec當中一共存在三種mode:

  • UNSPECIFIED:無限制,View對尺寸沒有任何限制,View設置為多大就應當為多大。
  • EXACTLY :精準模式,View需要一個精確值,這個值即為MeasureSpec當中的Size。對應的是match_parent。
  • AT_MOST:最大模式,View的尺寸有一個最大值,View不可以超過MeasureSpec當中的Size值。對應的是wrap_content。

常見的使用方式如下:

// 獲取測量模式(Mode)
int specMode = MeasureSpec.getMode(measureSpec)
// 獲取測量大小(Size)
int specSize = MeasureSpec.getSize(measureSpec)
// 通過Mode 和 Size 生成新的SpecMode
int measureSpec=MeasureSpec.makeMeasureSpec(size, mode);

onMeasure

整個測量過程的入口位於View的measure方法當中,該方法做了一些參數的初始化之後調用了onMeasure方法,這裡我們主要分析onMeasure。onMeasure源碼如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
  • setMeasuredDimension(int measuredWidth, int measuredHeight) :該方法用來設置View的寬高,在我們自定義View時也會經常用到。
  • getDefaultSize(int size, int measureSpec):該方法用來獲取View默認的寬高。
  • getSuggestedMinimumWidth():getHeight和該方法原理是一樣的

五、Layout()

layout()過程,對於View來說用來計算View的位置參數,對於ViewGroup來說,除了要測量自身位置,還需要測量子View的位置。layout()方法是整個Layout()流程的入口,並在layout方法中調用了onLayout方法,主要是進行子View的計算。

六、Draw()

draw流程也就是的View繪製到屏幕上的過程,整個流程的入口在View的draw()方法之中,而源碼注釋也寫的很明白,整個過程可以分為6個步驟:

  • 繪製背景
  • 有過有必要,保存當前canvas
  • 繪製View的內容
  • 繪製子View
  • 根據需要,繪製邊緣、陰影等效果
  • 繪製裝飾,如滾動條等等

七、總結

自定義View在Android的開發中的重要性還是很大的,因為僅僅靠系統提供的控件和組件,無論是美觀度還是使用度,再或者是新特性上,都無法滿足特定的業務場景。因此,常常要用到自定義View,這就要求要在自己的項目自己完成特殊控件的自主開發。自定義控件在開發過程中也屬於重點和難點,應該多花時間進行學習和研究,重點有以下幾個:

  • 控件屬性的定義、設置和使用
  • 交互處理:事件交互和處理屬於重中之重,常常要和事件分髮結合在一起研究。
  • Canvas和Paint:在進行自定義View開發時,往往會通過畫布自己使用畫筆進行繪製,這就要求要對Paint、Path、Canvas要做着重的掌握。

原創文章,作者:投稿專員,如若轉載,請註明出處:https://www.506064.com/zh-hk/n/281175.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
投稿專員的頭像投稿專員
上一篇 2024-12-21 13:16
下一篇 2024-12-21 13:16

相關推薦

發表回復

登錄後才能評論