實現(xiàn)自己想要的View效果(2):自定義ViewGroup

整理自:
http://blog.csdn.net/huachao1001/article/details/51577291


最近一個項目里面要用到自定義View的技術(shù),看了很多博客症杏,準(zhǔn)備在這里整理一下
首先,我把實現(xiàn)自己想要的View效果的方法分為3類:

  1. 自定義View

有多種實現(xiàn)方式:
Ⅰ篙梢、繼承現(xiàn)有控件,對其控件的功能進(jìn)行拓展美旧。
Ⅱ渤滞、將現(xiàn)有控件進(jìn)行組合,實現(xiàn)功能更加強(qiáng)大控件陈症。
Ⅲ蔼水、重寫View實現(xiàn)全新的控件

  1. 自定義ViewGroup

這個實際上就是我們自定義一個布局,我們在實際使用的時候依然要在這個自定義ViewGroup中加子元素(比如TextView录肯、ImageView或者其他)


上一篇把自定義View講了趴腋,這一篇講自定義ViewGroup:
首先先明確設(shè)計ViewGroup的要點:

  1. 首先,我們得知道各個子View的大小吧,只有先知道子View的大小优炬,我們才知道當(dāng)前的ViewGroup該設(shè)置為多大去容納它們颁井。
  1. 根據(jù)子View的大小,以及我們的ViewGroup要實現(xiàn)的功能蠢护,決定出ViewGroup的大小
  2. ViewGroup和子View的大小算出來了之后雅宾,接下來就是去擺放了吧,具體怎么去擺放呢葵硕?這得根據(jù)你定制的需求去擺放了眉抬,比如,你想讓子View按照垂直順序一個挨著一個放懈凹,或者是按照先后順序一個疊一個去放蜀变,這是你自己決定的。
  3. 已經(jīng)知道怎么去擺放還不行啊介评,決定了怎么擺放就是相當(dāng)于把已有的空間”分割”成大大小小的空間库北,每個空間對應(yīng)一個子View,我們接下來就是把子View對號入座了们陆,把它們放進(jìn)它們該放的地方去寒瓦。

我們來個具體的案例:將子View按從上到下垂直順序一個挨著一個擺放,即模仿實現(xiàn)LinearLayout的垂直布局坪仇。

一杂腰、重寫onMeasure

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //將所有的子View進(jìn)行測量,這會觸發(fā)每個子View的onMeasure函數(shù)
        //注意要與measureChild區(qū)分烟很,measureChild是對單個view進(jìn)行測量
        measureChildren(widthMeasureSpec, heightMeasureSpec);

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

        int childCount = getChildCount();

        if (childCount == 0) {//如果沒有子View,當(dāng)前ViewGroup沒有存在的意義颈墅,不用占用空間
            setMeasuredDimension(0, 0);
        } else {
            //如果寬高都是包裹內(nèi)容,即wrap_content
            if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
                //我們將高度設(shè)置為所有子View的高度相加蜡镶,寬度設(shè)為子View中最大的寬度
                int height = getTotleHeight();
                int width = getMaxChildWidth();
                setMeasuredDimension(width, height);

            } else if (heightMode == MeasureSpec.AT_MOST) {//如果只有高度是包裹內(nèi)容
                //寬度設(shè)置為ViewGroup自己的測量寬度雾袱,高度設(shè)置為所有子View的高度總和
                setMeasuredDimension(widthSize, getTotleHeight());
            } else if (widthMode == MeasureSpec.AT_MOST) {//如果只有寬度是包裹內(nèi)容
                //寬度設(shè)置為子View中寬度最大的值,高度設(shè)置為ViewGroup自己的測量值
                setMeasuredDimension(getMaxChildWidth(), heightSize);

            }
        }
    }
    /***
     * 獲取子View中寬度最大的值
     */
    private int getMaxChildWidth() {
        int childCount = getChildCount();
        int maxWidth = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView.getMeasuredWidth() > maxWidth)
                maxWidth = childView.getMeasuredWidth();

        }

        return maxWidth;
    }

    /***
     * 將所有子View的高度相加
     **/
    private int getTotleHeight() {
        int childCount = getChildCount();
        int height = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            height += childView.getMeasuredHeight();

        }

        return height;
    }

解釋基本注釋說的就很清楚了

二官还、拜訪子View芹橡,使用onLayout

   @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        //記錄當(dāng)前的高度位置
        int curHeight = t;
        //將子View逐個擺放
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            int height = child.getMeasuredHeight();
            int width = child.getMeasuredWidth();
            //擺放子View,參數(shù)分別是子View矩形區(qū)域的左望伦、上林说、右、下邊
            child.layout(l, curHeight, l + width, curHeight + height);
            curHeight += height;
        }
    }

上面child.layout(l, curHeight, l + width, curHeight + height);代碼就是設(shè)置這個子View的左邊上邊右邊下邊的位置分別在哪里屯伞。
然后因為我們是想要實現(xiàn)一個在一個下面的效果腿箩,所以我們設(shè)置curHeight += height;
我們測試一下,將我們自定義的ViewGroup里面放3個Button ,將這3個Button的寬度設(shè)置不一樣劣摇,把我們的ViewGroup的寬高都設(shè)置為包裹內(nèi)容wrap_content
珠移,為了看的效果明顯,我們給ViewGroup加個背景:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.hc.studyview.MyViewGroup
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ff9900">

        <Button
            android:layout_width="100dp"
            android:layout_height="wrap_content"
            android:text="btn" />

        <Button
            android:layout_width="200dp"
            android:layout_height="wrap_content"
            android:text="btn" />

        <Button
            android:layout_width="50dp"
            android:layout_height="wrap_content"
            android:text="btn" />


    </com.hc.studyview.MyViewGroup>

</LinearLayout>

注意看上面的MyViewGroup里面包的三個Button子元素就是我們在寫MyViewGroup遇到的int childCount = getChildCount();這些東西!>濉暇韧!我們就是把這些Child按照我們呢自己想要的方式來排序~~

看看最后的效果吧~


自定義ViewGroup

最后附上MyViewGroup的完整源碼:

public class MyViewGroup extends ViewGroup {
    public MyViewGroup(Context context) {
        super(context);
    }

    public MyViewGroup(Context context, AttributeSet attrs) {

        super(context, attrs);
    }

    /***
     * 獲取子View中寬度最大的值
     */
    private int getMaxChildWidth() {
        int childCount = getChildCount();
        int maxWidth = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            if (childView.getMeasuredWidth() > maxWidth)
                maxWidth = childView.getMeasuredWidth();

        }

        return maxWidth;
    }

    /***
     * 將所有子View的高度相加
     **/
    private int getTotleHeight() {
        int childCount = getChildCount();
        int height = 0;
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);
            height += childView.getMeasuredHeight();

        }

        return height;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //將所有的子View進(jìn)行測量,這會觸發(fā)每個子View的onMeasure函數(shù)
        //注意要與measureChild區(qū)分浓瞪,measureChild是對單個view進(jìn)行測量
        measureChildren(widthMeasureSpec, heightMeasureSpec);

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

        int childCount = getChildCount();

        if (childCount == 0) {//如果沒有子View,當(dāng)前ViewGroup沒有存在的意義懈玻,不用占用空間
            setMeasuredDimension(0, 0);
        } else {
            //如果寬高都是包裹內(nèi)容
            if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
                //我們將高度設(shè)置為所有子View的高度相加,寬度設(shè)為子View中最大的寬度
                int height = getTotleHeight();
                int width = getMaxChildWidth();
                setMeasuredDimension(width, height);

            } else if (heightMode == MeasureSpec.AT_MOST) {//如果只有高度是包裹內(nèi)容
                //寬度設(shè)置為ViewGroup自己的測量寬度乾颁,高度設(shè)置為所有子View的高度總和
                setMeasuredDimension(widthSize, getTotleHeight());
            } else if (widthMode == MeasureSpec.AT_MOST) {//如果只有寬度是包裹內(nèi)容
                //寬度設(shè)置為子View中寬度最大的值涂乌,高度設(shè)置為ViewGroup自己的測量值
                setMeasuredDimension(getMaxChildWidth(), heightSize);

            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int count = getChildCount();
        //記錄當(dāng)前的高度位置
        int curHeight = t;
        for (int i = 0; i < count; i++) {
            View child = getChildAt(i);
            int height = child.getMeasuredHeight();
            int width = child.getMeasuredWidth();
            child.layout(l, curHeight, l + width, curHeight + height);
            curHeight += height;
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市英岭,隨后出現(xiàn)的幾起案子骂倘,更是在濱河造成了極大的恐慌,老刑警劉巖巴席,帶你破解...
    沈念sama閱讀 211,123評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件历涝,死亡現(xiàn)場離奇詭異,居然都是意外死亡漾唉,警方通過查閱死者的電腦和手機(jī)荧库,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赵刑,“玉大人分衫,你說我怎么就攤上這事“愦耍” “怎么了蚪战?”我有些...
    開封第一講書人閱讀 156,723評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長铐懊。 經(jīng)常有香客問我邀桑,道長,這世上最難降的妖魔是什么科乎? 我笑而不...
    開封第一講書人閱讀 56,357評論 1 283
  • 正文 為了忘掉前任壁畸,我火速辦了婚禮,結(jié)果婚禮上茅茂,老公的妹妹穿的比我還像新娘捏萍。我一直安慰自己,他們只是感情好空闲,可當(dāng)我...
    茶點故事閱讀 65,412評論 5 384
  • 文/花漫 我一把揭開白布令杈。 她就那樣靜靜地躺著,像睡著了一般碴倾。 火紅的嫁衣襯著肌膚如雪逗噩。 梳的紋絲不亂的頭發(fā)上悔常,一...
    開封第一講書人閱讀 49,760評論 1 289
  • 那天,我揣著相機(jī)與錄音给赞,去河邊找鬼机打。 笑死,一個胖子當(dāng)著我的面吹牛片迅,可吹牛的內(nèi)容都是我干的残邀。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼柑蛇,長吁一口氣:“原來是場噩夢啊……” “哼芥挣!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起耻台,我...
    開封第一講書人閱讀 37,672評論 0 266
  • 序言:老撾萬榮一對情侶失蹤空免,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后盆耽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蹋砚,經(jīng)...
    沈念sama閱讀 44,118評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,456評論 2 325
  • 正文 我和宋清朗相戀三年摄杂,在試婚紗的時候發(fā)現(xiàn)自己被綠了坝咐。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,599評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡析恢,死狀恐怖墨坚,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情映挂,我是刑警寧澤泽篮,帶...
    沈念sama閱讀 34,264評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站柑船,受9級特大地震影響帽撑,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜椎组,卻給世界環(huán)境...
    茶點故事閱讀 39,857評論 3 312
  • 文/蒙蒙 一油狂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寸癌,春花似錦、人聲如沸弱贼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吮旅。三九已至溪烤,卻和暖如春味咳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背檬嘀。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評論 1 264
  • 我被黑心中介騙來泰國打工槽驶, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人鸳兽。 一個月前我還...
    沈念sama閱讀 46,286評論 2 360
  • 正文 我出身青樓掂铐,卻偏偏與公主長得像,于是被迫代替她去往敵國和親揍异。 傳聞我的和親對象是個殘疾皇子全陨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,465評論 2 348

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,743評論 25 707
  • 好種子開花: 1,每天的種子實踐都會分享給閨蜜衷掷,貼心的閨蜜每次都會認(rèn)真的回復(fù)她的讀后感辱姨,給予了我極大的支持,而且時...
    成長中的小蝸牛閱讀 114評論 0 1
  • “報――軍部急報戚嗅,熙芬公主雨涛,風(fēng)族大軍已兵臨城下,熙雲(yún)王子要求城內(nèi)雷族血統(tǒng)從玄武門逃離懦胞!” 她手中的銀針刺破了她無名...
    高躅不腆塵鞅閱讀 345評論 0 2
  • 曾經(jīng)有一個業(yè)務(wù)員問老板:“市場上有一個小廠镜悉,價格很低,很難對付医瘫,怎么辦侣肄?”老板反問道:“既然這家廠這么厲害,為什么...
    湘子書畫閱讀 223評論 0 0
  • 愛人早晨打電話說有時間要和我倆出去玩醇份,一會就到家稼锅。一進(jìn)門看見我穿的是平時的衣服就說你沒有運動服裝嗎?這身衣...