[轉]Android自定義View,你摸的透透的了?

[轉] Android自定義View,你摸的透透的了?

本文轉載自ClericYi的Android自定義View,你摸的透透的了?

前言

View,有很多的名稱。不論是你熟知的布局,還是控件,他們全部都繼承自View

文內部分圖片轉載自Carson_Ho大佬的文章

思維導圖

思維導圖

工作流程

measure

其實通過layout中的第二張圖我們已經知道了控件大小的計算了。

  • height = bottom - top
  • width = right - left

對于ViewGroup而言,就是對容器內子控件的遍歷和計算了。

因為直接繼承自View的控件使用wrap_cotentmatch_parent是顯示出來的效果是相同的。需要我們使用MeasureSpec中的getMode()方法來對當前的模式進行區分和比較。

模式 狀態
UNSPECIFIED 未指定模式,View想多大就多大,父容器不做限制,一般用于系統內部的測量
AT_MOST 最大模式,對應wrap_content,View的大小不大于SpecSize的值
EXACTLY 精確模式,對應match_parent,View的大小為SpecSize的值
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //用于獲取設定的模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        // 用于獲取設定的長度
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        // 類似這樣的判斷,后面不過多復述
        // 用于判斷是不是wrap_content
        // 如果不進行處理,效果會是match_parent
        if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(20, 20);
        }
    }
復制代碼

layout

在確定位置時,我們有一個非常需要主要的地方—— 坐標系。Android系統的坐標系和平時畫的坐標系并不相同。

坐標系

所以相對應的,我們的位置計算方法自然和我們原來的正好是相反的。

4個頂點的位置分別由4個值決定:

  • top:子View上邊界到所在容器上邊界的距離。
  • left:子View左邊界到所在容器左邊界的距離。
  • bottom:子View下邊界到所在容器上邊界的距離。
  • right:子View右邊界到所在容器左邊界的距離。

所有的計算都是相對于所在容器才能夠開始的。

draw

一共有6個步驟:

  1. 如果需要,則繪制背景 -- drawBackground(canvas);
  2. 保存當前canvas層 -- saveCount = canvas.getSaveCount();
  3. 繪制View的內容 -- if (!dirtyOpaque) onDraw(canvas);
  4. 繪制子View -- dispatchDraw(canvas);
  5. 如果需要,則繪制View的褪色邊緣,類似于陰影效果 -- canvas.restoreToCount(saveCount);
  6. 繪制裝飾,比如滾動條 -- onDrawForeground(canvas);

關于開發者需要重寫的方法一般是第三步繪制View的內容對應的onDraw()

    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        // 在畫布上進行類似這樣的操作
        canvas.drawLine(0, height/2, width,height/2, paint);
    }
復制代碼

入門自定義View

在日常項目的布局文件中我們經常會使用到xmlns:app="http://schemas.android.com/apk/res-auto"這樣標簽,其實他就是用來引入我們自定義的標簽使用的。

  1. res/values目錄下創建attrs
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="DefaultView">
        <attr name="color" format="color"/>
    </declare-styleable>
</resources>
復制代碼
  1. DefaultView(Context context, @Nullable AttributeSet attrs)中獲取。以下是整個完整代碼。
/**
 * author: ClericYi
 * time: 2020-01-30
 */
public class DefaultView extends View {
    private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private int mColor = Color.RED;

    public DefaultView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initAttrs(context, attrs);
        initDraw();
    }

    private void initAttrs(Context context, @Nullable AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DefaultView);
        // 從styleable中獲取的名字是系統會生成的,一般是 類名_name 的形式
        mColor = array.getColor(R.styleable.DefaultView_color, Color.GREEN);
        // 獲取完資源后即使回收
        array.recycle();
    }

    private void initDraw() {
        paint.setColor(mColor);
        paint.setStrokeWidth(3f);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
        canvas.drawLine(0, height/2, width,height/2, paint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        if(widthMode == MeasureSpec.AT_MOST){
            setMeasuredDimension(20, 20);
        }
    }
}
復制代碼

基礎的性能優化

首先的話我們先了解如何去知道一個View是否被過度繪制了?

其實在我們手機中的開發模式已經存在這個選項了。

開啟前 開啟后
開啟前
開啟后

下方給出繪制的次數對應圖

過度繪制

那如何做到性能優化呢?

在這個問題之前,需要了解什么是過度繪制,你可以理解為同一位置的控件不斷的疊加而產生的無用數據,那我們就來說說集中解決方案吧。

方案1: 減少嵌套層數。

使用線性布局 使用約束布局

因為只是一個案例,想說的意思,如果多個LinearLayout嵌套實現的效果,如果能被一個ConstraintLayout直接實現,那么就用后者替代,因為不會這樣在同一個區域重復出現

方案2: 去除默認的背景

這個解決方案其實針對的背景會被自動繪制的問題,如果我們把這個層次消去,從繪制角度老說也是一種提升了。正如圖示一般直接減少了一層的繪制。

在代碼中的具體表現,通過對style.xml中的Theme進行修改:

<item name="android:windowBackground">@null</item>
復制代碼

總結

總結

以上就是我的學習成果,如果有什么我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。

?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,572評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,071評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,409評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,569評論 1 307
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,360評論 6 404
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,895評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 42,979評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,123評論 0 286
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,643評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,559評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,742評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,250評論 5 356
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 43,981評論 3 346
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,363評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,622評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,354評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,707評論 2 370

推薦閱讀更多精彩內容