Android自定義ViewGroup之子控制元件的自動換行和新增刪除

一葉飄舟發表於2016-01-06

常用的佈局型別並不能滿足所有需求,這時就會用到ViewGroup。


ViewGroup作為一個放置View的容器,並且我們在寫佈局xml的時候,會告訴容器(凡是以layout為開頭的屬性,都是為用於告訴容器的),我們的寬度(layout_width)、高度(layout_height)、對齊方式(layout_gravity)等;當然還有margin等;於是乎,ViewGroup需要做的事情是:給childView計算出建議的寬和高和測量模式 ;決定childView的位置;為什麼只是建議的寬和高,而不是直接確定呢,別忘了childView寬和高可以設定為wrap_content,這樣只有childView才能計算出自己的寬和高。


程式碼註釋很詳細,直接看程式碼即可,沒貼原始碼,因為這是從一個專案裡面摳出來的。

先寫一個自定義LinearLayout,它的功能是自適應子控制元件:

public class ItemContainer extends LinearLayout {
    private int width;//元件寬
    private int height;//元件高
    private int childCount;
    private int childMarginLeft = SizeConvert.dip2px(getContext(),8);//子控制元件相對左邊控制元件的距離
    private int childMarginHorizonTal = SizeConvert.dip2px(getContext(),10);//子控制元件相對最左、最右的距離
    private int childMarginTop = SizeConvert.dip2px(getContext(),8);//子控制元件相對頂部控制元件的距離
    private int childWidth;//子控制元件寬
    private int childHeight;//子控制元件高
 
    public ItemContainer(Context context) {
        super(context);
    }
 
    public ItemContainer(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        childCount = getChildCount();//得到子控制元件數量
        if(childCount>0) {
            childWidth = (width - childMarginLeft * 4) / 3;
            childHeight = SizeConvert.dip2px(getContext(),42);//給子控制元件的高度一個定值
            //根據子控制元件的高和子控制元件數目得到自身的高
            height = childHeight * ((childCount-1)/ 3+1) + childMarginHorizonTal * 2 + childMarginTop*((childCount-1)/3);
            Log.d(childHeight,childHeight+);
        }else {
            //如果木有子控制元件,自身高度為0,即不顯示
            height = 0;
        }
        width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);
        Log.d(height,height+);
        //根據自身的寬度約束子控制元件寬度
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        //設定自身寬度
        setMeasuredDimension(width, height);
    }
 
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        /**
         * 遍歷所有子控制元件,並設定它們的位置和大小
         * 每行只能有三個子控制元件,且高度固定,寬度相同,且每行正好佈滿
         */
        for (int i = 0; i < childCount; i++) {
            View childView = getChildAt(i);//得到當前子控制元件
            childView.layout((i%3) * childWidth + (i%3+1)*childMarginLeft
                    , (i / 3)*childHeight + childMarginHorizonTal + (i / 3)*childMarginTop
                    , (i%3+1) * childWidth + (i%3+1)*childMarginLeft
                    , (i / 3+1)*childHeight + childMarginHorizonTal + (i / 3)*childMarginTop);
        }
    }
 
}

主活動完成的功能就是上面貼圖演示的功能,讓兩個自定義ViewGroup能夠新增刪除子控制元件,子控制元件是在程式碼中動態搭建的,下面會給出方法:
活動:

public class ItemOperateActivity extends BaseActivity {
    private LinearLayout mContentNoItem;
    private LinearLayout mContentItemRemove;
    private ItemContainer mItemContainerAdd;
    private ItemContainer mItemContainerRemove;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_item_operate);
        mContentNoItem = (LinearLayout) findViewById(R.id.linearlayout_attention_null);
        mContentItemRemove = (LinearLayout) findViewById(R.id.linearlayout_remove);
        mItemContainerAdd = (ItemContainer) findViewById(R.id.item_container_add);
        mItemContainerRemove = (ItemContainer) findViewById(R.id.item_container_remove);
 
        initItems(new String[]{隨時定位, 客戶拜訪}, new String[]{新增客戶, 客戶總量});
 
    }
 
    /**
     * 新增條目時需要呼叫的方法
     * @param name
     */
    private void addItem(String name) {
        mContentNoItem.setVisibility(View.GONE);
        Button button = new Button(getApplicationContext());
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
                , 0);
        button.setLayoutParams(params);
        button.setText(name);
        button.setGravity(Gravity.CENTER);
        button.setBackgroundResource(R.drawable.item_select_bg);
        button.setTextSize(13);
        button.setTextColor(Color.argb(255, 47, 79, 79));//鉛灰色
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mItemContainerAdd.removeView(v);
                removeItem(((Button) v).getText().toString());
                if (mItemContainerAdd.getChildCount()== 0) {
                    mContentNoItem.setVisibility(View.VISIBLE);
                }
            }
 
        });
        mItemContainerAdd.addView(button);
 
    }
 
    /**
     * 刪除條目時需要呼叫的方法
     * @param name
     */
    private void removeItem(String name) {
        mContentItemRemove.setVisibility(View.VISIBLE);
        Button button = new Button(getApplicationContext());
        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
                , 0);
        button.setLayoutParams(params);
        button.setText(name);
        button.setGravity(Gravity.CENTER);
        button.setBackgroundResource(R.drawable.item_select_bg_below);
        button.setTextSize(13);
        button.setTextColor(Color.argb(255, 47, 79,79));//鉛灰色
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /**
                 * 點選按鈕時,刪除
                 */
                mItemContainerRemove.removeView(v);
                addItem(((Button) v).getText().toString());
 
                if (mItemContainerRemove.getChildCount() == 0) {
                    mContentItemRemove.setVisibility(View.GONE);
                }
            }
        });
        mItemContainerRemove.addView(button);
    }
 
    /**
     * 初始化子控制元件
     * @param itemsAdd 已新增的子控制元件名陣列
     * @param itemsRemove 可新增的子控制元件名陣列
     */
    private void initItems(String[] itemsAdd, String[] itemsRemove) {
        for (String itemAdd : itemsAdd) {
            addItem(itemAdd);
        }
        for (String itemRemove : itemsRemove) {
            removeItem(itemRemove);
        }
    }
}

佈局:

<?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"
              android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:background="@drawable/item_bg">
        <LinearLayout
            android:id="@+id/linearlayout_back"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="5dp"
            android:gravity="center"
            android:orientation="horizontal">
            <ImageButton
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/btn_back"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="報表"
                android:textSize="@dimen/head_left_text_size"/>
        </LinearLayout>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:text="訂閱"
            android:textSize="@dimen/head_center_text_size"/>

    </RelativeLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/item_bg"
        android:orientation="horizontal"
        android:paddingBottom="12dp"
        android:paddingLeft="8dp"
        android:paddingTop="12dp">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="已新增"
            android:textSize="16dp"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="(點選刪除)"
            android:textSize="13dp"/>
    </LinearLayout>

    <com.test.shiweiwei.myproject.selfish_view.self_defined_view_group.ItemContainer
        android:id="@+id/item_container_add"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/item_bg">

    </com.test.shiweiwei.myproject.selfish_view.self_defined_view_group.ItemContainer>

    <LinearLayout
        android:id="@+id/linearlayout_attention_null"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:paddingTop="35dp"
        android:paddingBottom="35dp"
        android:gravity="center"
        android:background="@drawable/item_bg"
        android:visibility="gone">
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@mipmap/attendance_null"/>
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="13dp"
            android:text="無報表資訊"/>
    </LinearLayout>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <LinearLayout
        android:id="@+id/linearlayout_remove"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/item_bg"
            android:orientation="horizontal"
            android:layout_marginTop="@dimen/first_page_item_margin_top"
            android:paddingBottom="12dp"
            android:paddingLeft="8dp"
            android:paddingTop="12dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="可新增"
                android:textSize="16dp"/>

        </LinearLayout>

        <com.test.shiweiwei.myproject.selfish_view.self_defined_view_group.ItemContainer
            android:id="@+id/item_container_remove"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@drawable/item_bg">

        </com.test.shiweiwei.myproject.selfish_view.self_defined_view_group.ItemContainer>
    </LinearLayout>

</LinearLayout>


drawable下的檔案:
item_bg.xml:

<!--?xml version=1.0 encoding=utf-8?-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/white"></solid>
    <stroke android:color="@color/stroke_vertical" android:width="0.3dp"></stroke>
</shape>

item_select_bg.xml:

<!--?xml version=1.0 encoding=utf-8?-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/white"></solid>
    <stroke android:color="@color/dark_brow" android:width="0.3dp"></stroke>
    <corners android:radius="4dp"></corners>
    <padding android:bottom="12dp" android:top="12dp"></padding>
</shape>

item_select_bg_below.xml:

<!--?xml version=1.0 encoding=utf-8?-->
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="@color/white"></solid>
    <stroke android:color="@color/stroke_vertical" android:width="0.3dp"></stroke>
    <corners android:radius="4dp"></corners>
 </shape>

最後感謝原創者的無私奉獻精神。



相關文章