本篇文章讲的是Android 自定义ViewGroup之实现标签流式布局-FlowLayout,开发中我们会经常需要实现类似于热门标签等自动换行的流式布局的功能,网上也有很多这样的FlowLayout,但不影响我对其的学习。和往常一样,主要还是想总结一下自定义ViewGroup的开发过程以及一些需要注意的地方。
按照惯例,我们先来看看效果图
一、写代码之前,有几个是问题是我们先要弄清楚的:
1、什么是ViewGroup:从名字上来看,它可以被翻译为控件组,言外之意是ViewGroup内部包含了许多个控件,是一组View。在Android的设计中,ViewGroup也继承了View,这就意味着View本身就可以是单个控件也可以是由多个控件组成的一组控件;
2、ViewGroup的种类:常见的有LinearLayout、RelativeLayout、FrameLayout、AbsoluteLayout、GirdLayout、TableLayout。其中LinearLayout和RelativeLayout使用的最多的两种;
3、ViewGroup的职责:给childView计算出建议的宽和高和测量模式 ,然后决定childView的位置;
4、话说何为流式布局(FlowLayout):就是控件根据ViewGroup的宽,自动的从左往右添加。如果当前行还能放得这个子View,就放到当前行,如果当前行剩余的空间不足于容纳这个子View,则自动添加到下一行的最左边;
二、先总结下自定义ViewGroup的步骤:
1、自定义ViewGroup的属性
2、在ViewGroup的构造方法中获得我们自定义的属性
3、重写onMesure
4、重写onLayout
三、ViewGroup的几个构造函数:
1、public FlowLayout(Context context)
—>Java代码直接new一个FlowLayout实例的时候,会调用这个只有一个参数的构造函数;
2、public FlowLayout(Context context, AttributeSet attrs)
—>在默认的XML布局文件中创建的时候调用这个有两个参数的构造函数。AttributeSet类型的参数负责把XML布局文件中所自定义的属性通过AttributeSet带入到View内;
3、public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr)
—>构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或者Activity所用的Theme中的默认Style,且只有在明确调用的时候才会调用
4、public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)
—>该构造函数是在API21的时候才添加上的
四、下面我们就开始来看看自定义ViewGroup的主要代码啦
1、自定义ViewGroup的属性,首先在res/values/ 下建立一个attr.xml , 在里面定义我们的需要用到的属性以及声明相对应属性的取值类型
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--每个item纵向间距-->
<attr name="verticalSpacing" format="dimension" />
<!-- 每个item横向间距-->
<attr name="horizontalSpacing" format="dimension" />
<declare-styleable name="FlowLayout">
<attr name="verticalSpacing" />
<attr name="horizontalSpacing" />
</declare-styleable>
</resources>
我们定义了verticalSpacing以及horizontalSpacing2个属性,分别表示每个标签之间纵向间距和横向间距,其中format是值该属性的取值类型,format取值类型总共有10种,包括:string,color,demension,integer,enum,reference,float,boolean,fraction和flag。
2、然后在XML布局中声明我们的自定义View
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="#38353D"
android:gravity="center"
android:text="标签"
android:textColor="@android:color/white"
android:textSize="16dp" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_remind"
android:layout_width="match_parent"
android:layout_height="46dp"
android:background="@android:color/white"
android:gravity="center_vertical"
android:paddingLeft="15dp"
android:text="我的标签(最多5个) "
android:textSize="16dp" />
<com.per.flowlayoutdome.FlowLayout
android:id="@+id/tcy_my_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:padding="5dp"
android:visibility="gone"
custom:horizontalSpacing="6dp"
custom:verticalSpacing="12dp" />
<View
android:layout_width="match_parent"
android:layout_height="10dp"
android:background="#f6f6f6" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="46dp"
android:background="@android:color/white">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingLeft="15dp"
android:text="推荐标签 "
android:textSize="16dp" />
</RelativeLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#f6f6f6" />
<com.per.flowlayoutdome.FlowLayout
android:id="@+id/tcy_hot_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/white"
android:padding="5dp"
custom:horizontalSpacing="6dp"
custom:verticalSpacing="12dp" />
</LinearLayout>
</ScrollView>
</LinearLayout>
一定要引入xmlns:custom=”http://schemas.android.com/apk/res-auto”,Android Studio中我们可以使用res-atuo命名空间,就不用在添加自定义View全类名。
3、在View的构造方法中,获得我们的自定义的样式
/**
* 每个item纵向间距
*/
private int mVerticalSpacing;
/**
* 每个item横向间距
*/
private int mHorizontalSpacing;
public FlowLayout(Context context) {
this(context, null);
}
public FlowLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
/**
* 获得我们所定义的自定义样式属性
*/
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FlowLayout, defStyle, 0);
for (int i = 0; i < a.getIndexCount(); i++) {
int attr = a.getIndex(i);
switch (attr) {
case R.styleable.FlowLayout_verticalSpacing:
mVerticalSpacing = a.getDimensionPixelSize(R.styleable.FlowLayout_verticalSpacing, 5);
break