[轉(zhuǎn)]Android自定義View赏廓,你摸的透透的了?

[轉(zhuǎn)] Android自定義View傍妒,你摸的透透的了幔摸?

本文轉(zhuǎn)載自ClericYi的Android自定義View,你摸的透透的了颤练?

前言

View既忆,有很多的名稱。不論是你熟知的布局嗦玖,還是控件患雇,他們?nèi)慷祭^承自View

文內(nèi)部分圖片轉(zhuǎn)載自Carson_Ho大佬的文章

思維導圖

思維導圖

工作流程

measure

其實通過layout中的第二張圖我們已經(jīng)知道了控件大小的計算了宇挫。

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

對于ViewGroup而言苛吱,就是對容器內(nèi)子控件的遍歷和計算了。

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

模式 狀態(tài)
UNSPECIFIED 未指定模式绘雁,View想多大就多大,父容器不做限制援所,一般用于系統(tǒng)內(nèi)部的測量
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系統(tǒng)的坐標系和平時畫的坐標系并不相同澈蟆。

坐標系

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

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

  • top:子View上邊界到所在容器上邊界的距離趴俘。
  • left:子View左邊界到所在容器左邊界的距離。
  • bottom:子View下邊界到所在容器上邊界的距離奏赘。
  • right:子View右邊界到所在容器左邊界的距離寥闪。

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

draw

一共有6個步驟:

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

關于開發(fā)者需要重寫的方法一般是第三步繪制View的內(nèi)容對應的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

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

  1. res/values目錄下創(chuàng)建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中獲取的名字是系統(tǒng)會生成的构舟,一般是 類名_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);
        }
    }
}
復制代碼

基礎的性能優(yōu)化

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

其實在我們手機中的開發(fā)模式已經(jīng)存在這個選項了。

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

下方給出繪制的次數(shù)對應圖

過度繪制

那如何做到性能優(yōu)化呢狗超?

在這個問題之前弹澎,需要了解什么是過度繪制,你可以理解為同一位置的控件不斷的疊加而產(chǎn)生的無用數(shù)據(jù)抡谐,那我們就來說說集中解決方案吧裁奇。

方案1: 減少嵌套層數(shù)。

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

因為只是一個案例麦撵,想說的意思刽肠,如果多個LinearLayout嵌套實現(xiàn)的效果溃肪,如果能被一個ConstraintLayout直接實現(xiàn),那么就用后者替代音五,因為不會這樣在同一個區(qū)域重復出現(xiàn)

方案2: 去除默認的背景

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

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

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

總結(jié)

總結(jié)

以上就是我的學習成果坚嗜,如果有什么我沒有思考到的地方或是文章內(nèi)存在錯誤夯膀,歡迎與我分享。

?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苍蔬,一起剝皮案震驚了整個濱河市诱建,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌碟绑,老刑警劉巖俺猿,帶你破解...
    沈念sama閱讀 212,718評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異格仲,居然都是意外死亡押袍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評論 3 385
  • 文/潘曉璐 我一進店門凯肋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來谊惭,“玉大人,你說我怎么就攤上這事否过∥绲眩” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評論 0 348
  • 文/不壞的土叔 我叫張陵苗桂,是天一觀的道長药磺。 經(jīng)常有香客問我,道長煤伟,這世上最難降的妖魔是什么癌佩? 我笑而不...
    開封第一講書人閱讀 56,755評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮便锨,結(jié)果婚禮上围辙,老公的妹妹穿的比我還像新娘。我一直安慰自己放案,他們只是感情好姚建,可當我...
    茶點故事閱讀 65,862評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著吱殉,像睡著了一般掸冤。 火紅的嫁衣襯著肌膚如雪厘托。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,050評論 1 291
  • 那天稿湿,我揣著相機與錄音铅匹,去河邊找鬼。 笑死饺藤,一個胖子當著我的面吹牛包斑,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涕俗,決...
    沈念sama閱讀 39,136評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼罗丰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了再姑?” 一聲冷哼從身側(cè)響起丸卷,我...
    開封第一講書人閱讀 37,882評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎询刹,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體萎坷,經(jīng)...
    沈念sama閱讀 44,330評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡凹联,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,651評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了哆档。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蔽挠。...
    茶點故事閱讀 38,789評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖瓜浸,靈堂內(nèi)的尸體忽然破棺而出澳淑,到底是詐尸還是另有隱情,我是刑警寧澤插佛,帶...
    沈念sama閱讀 34,477評論 4 333
  • 正文 年R本政府宣布杠巡,位于F島的核電站,受9級特大地震影響雇寇,放射性物質(zhì)發(fā)生泄漏氢拥。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,135評論 3 317
  • 文/蒙蒙 一锨侯、第九天 我趴在偏房一處隱蔽的房頂上張望嫩海。 院中可真熱鬧,春花似錦囚痴、人聲如沸叁怪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽奕谭。三九已至涣觉,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間展箱,已是汗流浹背旨枯。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評論 1 267
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 46,598評論 2 362
  • 正文 我出身青樓傲诵,卻偏偏與公主長得像澜汤,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子唇辨,可洞房花燭夜當晚...
    茶點故事閱讀 43,697評論 2 351

推薦閱讀更多精彩內(nèi)容