android中自訂ViewGroup的實現

來源:互聯網
上載者:User

標籤:

     在android中提供了常見的幾種ViewGroup的實現,包括LinearLayout、Relativeayout、FrameLayout等。這些ViewGroup可以滿足我們一般的開發需求,但是對於介面要求複雜的,這幾個布局就顯得捉襟見肘了。所以自訂的ViewGroup在我們接觸過的應用中比比皆是。

     要想實現一個自訂的ViewGroup,第一步是學會自訂屬性,這些自訂的屬性將讓我們配置布局檔案的時候更加的靈活。自訂屬性是在value目錄下聲明一個attrs.xml檔案。

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="CascadeViewGroup">        <attr name="verticalspacing" format="dimension"/>        <attr name="horizontalspacing" format="dimension"/>    </declare-styleable>    <declare-styleable name="CascadeViewGroup_LayoutParams">        <attr name="layout_paddingleft" format="dimension"/>        <attr name="layout_paddinTop" format="dimension"/>    </declare-styleable></resources>

      在這裡我們聲明了兩個自訂屬性集,CascadeViewGroup中的屬性是針對我們自訂的CascadeViewGroup組件設定的,也就是可以在布局檔案中<CascadeViewGroup>標籤中可以使用的屬性。另外一個CascadeViewGroup_LayoutParams則是針對於CascadeViewGroup中的子View設定的屬性。

    在編寫代碼前,我們還設定了一個預設的寬度和高度供CascadeLayout使用。這兩個屬性在dimens.xml定義。

<?xml version="1.0" encoding="utf-8"?><resources>      <dimen name="default_horizontal_spacing">10dp</dimen>    <dimen name="default_vertical_spacing">10dp</dimen></resources>


下面開始編寫自訂的組件CascadeLayout了。

package com.app.CustomViewMotion;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.view.View;import android.view.ViewGroup;/** * Created by charles on 2015/8/13. */public class CascadeViewGroup extends ViewGroup {    //自訂布局中設定的寬度和高度    private int mHoriztonalSpacing;    private int mVerticalSpacing;    public CascadeViewGroup(Context context) {        this(context, null);    }    public CascadeViewGroup(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public CascadeViewGroup(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CascadeViewGroup);        try {            //擷取設定的寬度            mHoriztonalSpacing = a.getDimensionPixelSize(R.styleable.CascadeViewGroup_horizontalspacing,                    this.getResources().getDimensionPixelSize(R.dimen.default_horizontal_spacing));            //擷取設定的高度            mVerticalSpacing = a.getDimensionPixelSize(R.styleable.CascadeViewGroup_verticalspacing,                    this.getResources().getDimensionPixelSize(R.dimen.default_vertical_spacing));        } catch (Exception e) {            e.printStackTrace();        } finally {            a.recycle();        }    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        final int count = this.getChildCount();        int width = this.getPaddingLeft();        int height = this.getPaddingTop();        for (int i = 0; i < count; i++) {            final View currentView = this.getChildAt(i);            this.measureChild(currentView, widthMeasureSpec, heightMeasureSpec);            CascadeViewGroup.LayoutParams lp = (CascadeViewGroup.LayoutParams) currentView.getLayoutParams();            if(lp.mSettingPaddingLeft != 0){                width +=lp.mSettingPaddingLeft;            }            if(lp.mSettingPaddingTop != 0){                height +=lp.mSettingPaddingTop;            }            lp.x = width;            lp.y = height;            width += mHoriztonalSpacing;            height += mVerticalSpacing;        }        width +=getChildAt(this.getChildCount() - 1).getMeasuredWidth() + this.getPaddingRight();        height += getChildAt(this.getChildCount() - 1).getMeasuredHeight() + this.getPaddingBottom();        this.setMeasuredDimension(resolveSize(width, widthMeasureSpec), resolveSize(height, heightMeasureSpec));    }    @Override    protected void onLayout(boolean b, int l, int i1, int i2, int i3) {        final int count = this.getChildCount();        for (int i = 0; i < count; i++) {            final View currentView = this.getChildAt(i);            CascadeViewGroup.LayoutParams lp = (CascadeViewGroup.LayoutParams) currentView.getLayoutParams();            currentView.layout(lp.x, lp.y, lp.x + currentView.getMeasuredWidth(),                    lp.y + currentView.getMeasuredHeight());        }    }    public static class LayoutParams extends ViewGroup.LayoutParams {        int x;        int y;        int mSettingPaddingLeft;        int mSettingPaddingTop;        public LayoutParams(Context c, AttributeSet attrs) {            super(c, attrs);            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.CascadeViewGroup_LayoutParams);            mSettingPaddingLeft = a.getDimensionPixelSize(R.styleable.CascadeViewGroup_LayoutParams_layout_paddingleft, 0);            mSettingPaddingTop = a.getDimensionPixelSize(R.styleable.CascadeViewGroup_LayoutParams_layout_paddinTop, 0);            a.recycle();        }        public LayoutParams(int width, int height) {            super(width, height);        }        public LayoutParams(ViewGroup.LayoutParams source) {            super(source);        }    }    @Override    protected ViewGroup.LayoutParams generateDefaultLayoutParams() {        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);    }    @Override    protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {        return new LayoutParams(p);    }    @Override    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {        return new LayoutParams(this.getContext(), attrs);    }}

   代碼稍微優點長,但是結構還是很清晰的。

  1)構造方法中或者XML檔案中配置屬性的值。通過TypedArray中的方法擷取我們在layout布局中設定的屬性,並且將他們儲存在成員變數中。

2)構造自訂的內部類LayoutParams。構造這個內部類,可以方便我們在測量我們的子View的時候儲存他們的屬性值,以便在Layout階段布局。

3)generateLayoutParams()、generateDefaultParams()等方法。在這些方法中返回我們自訂的layoutParams。至於為什麼要重寫這些方法,可以查看ViewGroup類的addView()方法就很清楚了。

4)measure階段。在measure階段,我們會測量自己的大小,同時也要測量子View的大小,並且將子View的資訊儲存在LayoutParams中。

5)layout階段。根據各個子View的資訊,布局他們的位置。


最後加上布局檔案。

<?xml version="1.0" encoding="utf-8"?><!--添加自訂屬性給viewGroup--><!--新添加的命名空間的尾碼必須保持和.xml中聲明的包名一致--><com.app.CustomViewMotion.CascadeViewGroup        xmlns:android="http://schemas.android.com/apk/res/android"        xmlns:ts="http://schemas.android.com/apk/res/com.app.CustomViewMotion"        android:layout_width="match_parent"        android:layout_height="match_parent"        ts:horizontalspacing="15dp"        ts:verticalspacing="15dp">    <TextView android:layout_width="100dp"              android:layout_height="100dp"              android:gravity="center"              android:text="text1"              android:background="#668B8B"/>    <TextView android:layout_width="100dp"              android:layout_height="100dp"              android:gravity="center"              android:text="text2"              android:background="#FFDAB9"/>    <TextView android:layout_width="100dp"              android:layout_height="100dp"              android:gravity="center"              android:text="text3"              android:background="#43CD80"/><!--這個子view中添加自訂子view屬性-->    <TextView android:layout_width="100dp"              android:layout_height="100dp"              android:gravity="center"              android:text="text4"              ts:layout_paddingleft="100dp"              ts:layout_paddinTop="100dp"              android:background="#00CED1"/></com.app.CustomViewMotion.CascadeViewGroup>


實現的效果如下:

  

   

android中自訂ViewGroup的實現

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.