自定義View(一)

寫在前面

Android已經(jīng)為我們提供了豐富的組件庫爆哑,讓我們可以實現(xiàn)各種UI效果什猖。但是如果如此眾多的組件還不能滿足我們的需求牧愁,怎么辦呢素邪?別急,android組件也為我們提供了非常方便的拓展方法递宅,通過對現(xiàn)有系統(tǒng)組件的繼承,可以方便地實習(xí)那我們自己的功能苍狰。
自定義View作為Android的一項重要技能办龄,一直以來被初學(xué)者認(rèn)為是代表高手的象征,這篇文章就帶大家了解下自定義View的過程淋昭。

自定義View的分類
  • 繼承 View重寫 onDraw 方法
    這種方式主要用于顯示不規(guī)則的效果哦俐填,即這種效果不方便用布局組合來實現(xiàn),往往需要靜態(tài)或者動態(tài)的顯示一些不規(guī)則的圖形采用這種方式需要自己支持wrap_content翔忽,并且padding也需要自己處理英融。
  • 繼承ViewGroup派生特殊的Layout
    這種方法主要用于實現(xiàn)自定義布局,即除了LinearLayout歇式,RelativeLayout等系統(tǒng)布局之外的一種重新定義的全新的布局驶悟,當(dāng)某種效果很像
    幾種View組合在一起的時候就可以采用這種方法。
    這種方法稍微復(fù)雜一些材失,需要合適的處理ViewGroup的測量和布局這倆個過程
  • 繼承特定的View(比如TextView)
    這種方法一般用于擴(kuò)展某種已有的View功能痕鳍。這種方法不需要自己支持wrap_content,padding等龙巨。
  • 繼承特定的ViewGroup(比如LinearLayout等)
    當(dāng)某種效果很像幾種View組合在一起的時候就可以采用這種方法笼呆。這種方法不需要自己處理ViewGroup的測量和布局這倆個過程。
自定義View的注意事項
  • 讓View支持wrap_content
    這是因為直接繼承View或ViewGroup的控件旨别,如果不在onMeasure中處理wrap_content,那么外界在布局中使用wrap_content時就無法達(dá)到預(yù)期效果
  • 讓View支持padding
    直接繼承View的控件诗赌,如果不再draw方法中處理padding,那么這個屬性是無法起作用的秸弛。直接繼承ViewGroup的控件需要在onMeasure和onLayout中考慮padding和子元素的margin對其造成的影響铭若,不然將導(dǎo)致pading和子元素的margin失效
  • 不要在View中使用Handler
    這是因為View內(nèi)部本身就提供了post系列方法洪碳,完全可以替代Handler的作用。除非你很明確要用Handler來發(fā)送消息奥喻。
  • View中如果有線程和動畫偶宫,及時停止
    如果有線程和動畫需要停止的時候,onDetachedFromWindow就惡意做到环鲤。這是因為當(dāng)包含此View的Activity退出或者當(dāng)前View被remove時纯趋,View的onDetachedFromWindow方法就會被調(diào)用。相對的冷离,當(dāng)包含此View的Activity啟動時onAttachedToWindow會被調(diào)用吵冒。同時,View不可見時西剥,我們也需要停止線程和動畫痹栖,如果不及時停止,可能會導(dǎo)致內(nèi)存泄漏瞭空。
  • 如果有滑動嵌套時揪阿,當(dāng)然要處理好滑動沖突的問題。
注意事項

在自定義View中咆畏,通常有下列比較重要的方法:

  • onFinishInflate():從xml中加載組件后調(diào)用
  • onSizeChanged():當(dāng)組件的大小發(fā)生變化時調(diào)用
  • onMeasure():測量組件時調(diào)用,是View支持wrap_content屬性
  • onLayout():確定組件顯示位置時調(diào)用
    -onTouchEvent():界面上有觸摸事件時調(diào)用
    當(dāng)然南捂,創(chuàng)建自定義View的時候,不一定要全部重寫上述方法旧找,只需按照需要重寫即可溺健。
    通常,有以下三種方法實現(xiàn)自定義View
  • 對現(xiàn)有控件進(jìn)行擴(kuò)展
  • 通過組合實現(xiàn)新的控件
  • 重寫View實現(xiàn)全新控件
下面就用代碼展示下自定義View的基本步驟:
  • 新建BasicCustomView繼承View
    完整代碼如下
public class BasicCustomView extends View {

    private Paint mPaint;

    public BasicCustomView(Context context) {
        super(context);
        initView();
    }

    public BasicCustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public BasicCustomView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStyle(Paint.Style.FILL);
    }

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

首先驗證自定義View是否支持layout_margin钮蛛,padding鞭缭,wrap_content等屬性,驗證代碼如下:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_custom_view_basic"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.example.ahuang.viewandgroup.activity.CustomViewBasicActivity">
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <com.example.ahuang.viewandgroup.View.BasicCustomView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="20dp"
            android:background="#111fff"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <com.example.ahuang.viewandgroup.View.BasicCustomView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#111fff"/>
    </LinearLayout>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <com.example.ahuang.viewandgroup.View.BasicCustomView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#111fff"
            android:padding="20dp"/>
    </LinearLayout>

</LinearLayout>

上圖證明圖我們的自定義View
1.支持layout_margin屬性
2.不支持padding屬性
3.證明不支持wrap_content

讓View支持wrap_content

之所以不支持wrap_content屬性魏颓,是因為我們的自定義View沒有重寫onMeasure()方法岭辣,View默認(rèn)的onMeasure()方法只支持EXACTLY模式,所以可以指定控件的具體寬高值或者match_parent屬性甸饱,如果要自定義的view支持wrap_content屬性易结,就必須重寫onMeasure()方法。
加入代碼如下:

 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
      //  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureWidth(widthMeasureSpec),measureHeight(heightMeasureSpec));
    }


    /**
     * 獲得測量的寬度
     * @param widthMeasureSpec
     * @return
     */
    private int measureWidth(int widthMeasureSpec){
        int width = 0;
        int mode=MeasureSpec.getMode(widthMeasureSpec); //獲得測量模式
        int size=MeasureSpec.getSize(widthMeasureSpec); //獲得測量值
        if (mode==MeasureSpec.EXACTLY){ //精準(zhǔn)測量模式
            width=size;
        }else {
            width=300;
            if (mode==MeasureSpec.AT_MOST){
                width=Math.min(width,size);
            }
        }
        return  width;
    }


    /**
     * 獲得測量的高度
     * @param heightMeasureSpec
     * @return
     */
    private int measureHeight(int heightMeasureSpec){
        int height = 0;
        int mode=MeasureSpec.getMode(heightMeasureSpec); //獲得測量模式
        int size=MeasureSpec.getSize(heightMeasureSpec); //獲得測量值
        if (mode==MeasureSpec.EXACTLY){ //精準(zhǔn)測量模式
            height=size;
        }else {
            height=300;
            if (mode==MeasureSpec.AT_MOST){
                height=Math.min(width,size);
            }
        }
        return  height;
    }

可以看到柜候,重寫onMeasure()方法后搞动,VIew已經(jīng)支持wrap_content了。

讓View支持padding屬性

修改onDraw()方法如下:

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        final int paddingLeft=getPaddingLeft();
        final int paddingRight=getPaddingRight();
        final int paddingTop=getPaddingTop();
        final int paddingBottom=getPaddingBottom();
        int width = getWidth()-(paddingLeft+paddingRight);
        int height = getHeight()-(paddingTop+paddingBottom);
       canvas.translate(width/2,height/2);
        canvas.drawCircle(0,0,100,mPaint);
    }
 <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1">
        <com.example.ahuang.viewandgroup.View.BasicCustomView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:background="#111fff"
            android:paddingLeft="30dp"
            android:paddingTop="30dp"/>
    </LinearLayout>

我們看到已經(jīng)支持padding屬性了.

代碼下載 https://github.com/baojie0327/ViewAndGroup

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末渣刷,一起剝皮案震驚了整個濱河市鹦肿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辅柴,老刑警劉巖箩溃,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞭吃,死亡現(xiàn)場離奇詭異,居然都是意外死亡涣旨,警方通過查閱死者的電腦和手機歪架,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霹陡,“玉大人和蚪,你說我怎么就攤上這事∨朊蓿” “怎么了攒霹?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長浆洗。 經(jīng)常有香客問我催束,道長,這世上最難降的妖魔是什么伏社? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任抠刺,我火速辦了婚禮,結(jié)果婚禮上摘昌,老公的妹妹穿的比我還像新娘速妖。我一直安慰自己,他們只是感情好第焰,可當(dāng)我...
    茶點故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布买优。 她就那樣靜靜地躺著妨马,像睡著了一般挺举。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上烘跺,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天湘纵,我揣著相機與錄音,去河邊找鬼滤淳。 笑死梧喷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的脖咐。 我是一名探鬼主播铺敌,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼屁擅!你這毒婦竟也來了偿凭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤派歌,失蹤者是張志新(化名)和其女友劉穎弯囊,沒想到半個月后痰哨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡匾嘱,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年斤斧,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霎烙。...
    茶點故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡撬讽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吼过,到底是詐尸還是另有隱情锐秦,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布盗忱,位于F島的核電站酱床,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏趟佃。R本人自食惡果不足惜扇谣,卻給世界環(huán)境...
    茶點故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闲昭。 院中可真熱鬧罐寨,春花似錦、人聲如沸序矩。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽簸淀。三九已至瓶蝴,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間租幕,已是汗流浹背舷手。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留劲绪,地道東北人男窟。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像贾富,于是被迫代替她去往敵國和親歉眷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,514評論 2 348

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