Android自定义ViewGroup之子控件的自动换行和添加删除

时间:2022-01-20 06:07:47

概述:

常用的布局类型并不能满足所有需求,这时就会用到ViewGroup。

ViewGroup作为一个放置View的容器,并且我们在写布局xml的时候,会告诉容器(凡是以layout为开头的属性,都是为用于告诉容器的),我们的宽度(layout_width)、高度(layout_height)、对齐方式(layout_gravity)等;当然还有margin等;于是乎,ViewGroup需要做的事情是:给childView计算出建议的宽和高和测量模式 ;决定childView的位置;为什么只是建议的宽和高,而不是直接确定呢,别忘了childView宽和高可以设置为wrap_content,这样只有childView才能计算出自己的宽和高。

博客名已经说明了这篇博客要将给出的内容,不用赘述。

先看效果图:
Android自定义ViewGroup之子控件的自动换行和添加删除

代码注释很详细,直接看代码即可,没贴源码,因为这是从一个项目里面抠出来的。

Demo

先写一个自定义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 version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@color/white"></solid>
<stroke android:width="0.3dp" android:color="@color/stroke_vertical"></stroke>
</shape>

item_select_bg:

<?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:width="0.3dp" android:color="@color/dark_brow"></stroke>
<corners android:radius="4dp"></corners>
<padding android:bottom="12dp" android:top="12dp"></padding>
</shape><?xml version="1.0" encoding="utf-8"?>

item_select_bg_below:

<?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:width="0.3dp" android:color="@color/stroke_vertical"></stroke>
<corners android:radius="4dp"></corners>
<padding android:bottom="12dp" android:top="12dp"></padding>
</shape>

我们猿类工作压力大,很需要有自己的乐趣,于是乎,我开通了音乐人账号,以后的作品将会上传到我的音乐人小站上。如果这篇博客帮助到您,希望您能多关注,支持,鼓励我将创作进行下去,同时也祝你能在工作和生活乐趣两发面都能出彩!

网易云音乐人,直接打开客户端搜索音乐人 “星河河”

豆瓣音乐人地址:https://site.douban.com/chuxinghe/ 星河河