基于RelativeLayout实现自动换行标签控件
2016-08-04 01:32
381 查看
在开发中,我们有时会遇到显示标签的功能。比如做社交app,用户有标签;做电商app,商品也同样有标签......显示标签时,我们需要一次展示出多个标签,但是单个标签的字数可能不同,当一行不足以容纳时,需要换行显示。系统自带控件无法实现该效果,所以我们需要自定义控件来解决。
实现效果如下:
![](http://img.blog.csdn.net/20160804012351770?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
关于自动换行,有多种实现方式,这里基于RelativeLayout来实现。
实现思路:
![](http://img.blog.csdn.net/20160804012552382?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
有了思路,下面进入代码实现环节。
首先,我们自定义TagLayout继承自RelativeLayout。
判断单行空间是否够用,需要用到TagLayout的宽度,我们在onSizeChanged()方法中获取。
在RelativeLayout中,动态设置child的位置,需要使用到它的addRule()方法。
addRule()方法的使用示例:
layoutParams.addRule(RelativeLayout.BELOW, 1);表示将该子View添加到viewId==1的子View的下边。
layoutParams.addRule(RelativeLayout.ALIGN_TOP, 1);表示该子View与viewId==1的子View顶部对齐。
下面进入该控件的核心方法,将所有的标签逐个添加进来。代码中附带详细注释。
到这里,核心代码已经完成。再附上该控件的完整代码。
最后附上完整工程的代码下载链接:
http://download.csdn.net/detail/ruancoder/9594176
实现效果如下:
关于自动换行,有多种实现方式,这里基于RelativeLayout来实现。
实现思路:
有了思路,下面进入代码实现环节。
首先,我们自定义TagLayout继承自RelativeLayout。
public class TagLayout extends RelativeLayout { }
判断单行空间是否够用,需要用到TagLayout的宽度,我们在onSizeChanged()方法中获取。
private int mWidth; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mWidth = w; }
在RelativeLayout中,动态设置child的位置,需要使用到它的addRule()方法。
/** * Adds a layout rule to be interpreted by the RelativeLayout. Use this for * verbs that take a target, such as a sibling (ALIGN_RIGHT) or a boolean * value (VISIBLE). * * @param verb One of the verbs defined by * {@link android.widget.RelativeLayout RelativeLayout}, such as * ALIGN_WITH_PARENT_LEFT. * @param anchor The id of another view to use as an anchor, * or a boolean value (represented as {@link RelativeLayout#TRUE} * for true or 0 for false). For verbs that don't refer to another sibling * (for example, ALIGN_WITH_PARENT_BOTTOM) just use -1. */ public void addRule(int verb, int anchor) { // ... }
addRule()方法的使用示例:
layoutParams.addRule(RelativeLayout.BELOW, 1);表示将该子View添加到viewId==1的子View的下边。
layoutParams.addRule(RelativeLayout.ALIGN_TOP, 1);表示该子View与viewId==1的子View顶部对齐。
下面进入该控件的核心方法,将所有的标签逐个添加进来。代码中附带详细注释。
private void addTags() { removeAllViews(); // 初始化各变量 int total = getPaddingLeft() + getPaddingRight();// 计算单行的宽度 int index = 1;// child的索引,同时也是child的viewId int bottomAnchor = 1;// 添加到底部的child的id int alignTopAnchor = 1;// 与之顶部对齐的child的id for (String data : mData) { // 初始化child视图 View child = mInflater.inflate(R.layout.tag, null); // setId(index)这行代码很重要,将child的索引设为其id,在下面的代码中使用 child.setId(index); TextView textView = (TextView) child.findViewById(R.id.text); textView.setText(data); // 测量child的宽度 child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int childWidth = child.getMeasuredWidth(); // 为每一个child添加底部间距,这样就形成了行间距 LayoutParams childParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); childParams.bottomMargin = mChildBottomMargin; // 如果单行已有的所有child宽度 + 本次需要添加的child的宽度和左间距 > 整个TagLayout的宽度 // 此时需要将child换行添加 if (total + mChildLeftMargin + childWidth > mWidth) { // 设置LayoutParams:添加到viewId==bottomAnchor的child的底部 childParams.addRule(RelativeLayout.BELOW, bottomAnchor); // total被重新初始化 total = getPaddingLeft() + getPaddingRight(); // 给bottomAnchor和alignTopAnchor赋值,后面会使用到 bottomAnchor = index; alignTopAnchor = index; } else {// 此时在同一行添加child // 设置LayoutParams:与viewId==alignTopAnchor的child顶部对齐 childParams.addRule(RelativeLayout.ALIGN_TOP, alignTopAnchor); // 如果该child不是本行的第一个 if (index != alignTopAnchor) { // 设置LayoutParams:将child添加到前一个child的右侧 childParams.addRule(RelativeLayout.RIGHT_OF, index - 1); // 设置LayoutParams:为child添加左边距,保持child之间的空隙 childParams.leftMargin = mChildLeftMargin; // total累加child的左间距 total += mChildLeftMargin; } } // 使用上面设置好的childParams,添加child addView(child, childParams); // total累加child的宽度 total += childWidth; // index索引+1,进入下一轮循环 index++; } }
到这里,核心代码已经完成。再附上该控件的完整代码。
package net.csdn.blog.ruancoder; import android.content.Context; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.RelativeLayout; import android.widget.TextView; import java.util.List; public class TagLayout extends RelativeLayout { private LayoutInflater mInflater; private boolean mInited = false; private int mWidth; private int mChildLeftMargin; private int mChildBottomMargin; private List<String> mData; public TagLayout(Context context) { super(context); init(context); } public TagLayout(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public TagLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } private void init(Context context) { mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); mChildLeftMargin = context.getResources().getDimensionPixelSize(R.dimen.tag_leftmargin); mChildBottomMargin = context.getResources().getDimensionPixelSize(R.dimen.tag_bottommargin); getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { if (!mInited) { mInited = true; addTags(); } } }); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { mWidth = w; } public void setData(List<String> data) { this.mData = data; addTags(); } private void addTags() { if (!mInited) { return; } if (mData == null || mData.isEmpty()) { return; } removeAllViews(); int total = getPaddingLeft() + getPaddingRight(); int index = 1; int bottomAnchor = 1; int alignTopAnchor = 1; for (String data : mData) { View child = mInflater.inflate(R.layout.tag, null); child.setId(index); TextView textView = (TextView) child.findViewById(R.id.text); textView.setText(data); child.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED); int childWidth = child.getMeasuredWidth(); LayoutParams childParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); childParams.bottomMargin = mChildBottomMargin; if (total + mChildLeftMargin + childWidth > mWidth) { childParams.addRule(RelativeLayout.BELOW, bottomAnchor); total = getPaddingLeft() + getPaddingRight(); bottomAnchor = index; alignTopAnchor = index; } else { childParams.addRule(RelativeLayout.ALIGN_TOP, alignTopAnchor); if (index != alignTopAnchor) { childParams.addRule(RelativeLayout.RIGHT_OF, index - 1); childParams.leftMargin = mChildLeftMargin; total += mChildLeftMargin; } } addView(child, childParams); total += childWidth; index++; } } }
最后附上完整工程的代码下载链接:
http://download.csdn.net/detail/ruancoder/9594176
相关文章推荐
- 基于ViewGroup实现自动换行标签控件
- android商品属性选择标签控件,可实现自动换行
- pre标签实现自动换行
- 自定义自动换行布局控件,实现子控件宽度超出整行时自动换行。setMeasuredDimension的使用
- table标签下实现内容自动换行
- <pre>标签里面的内容实现自动换行(默认不会自动换行)
- 基于读取车载RFID标签自动开关车库门c#算法实现
- 自定义ViewGroup实现控件自动换行
- 自定义View-自动换行的标签控件
- 基于读取车载RFID标签自动开关车库门c#算法实现
- 基于mfc的对话框编程中,实现控件随对话框大小自动缩放以及通过滚动条实现控件移动功能
- 子控件根据父控件行宽自动换行---LineWrapLayout实现
- p标签实现自动换行
- 如何实现:GridView 控件中显示的文本不自动换行,隐藏超出宽度部分wj-wangjun
- 自定义自动换行布局控件,实现子控件宽度超出整行时自动换行。setMeasuredDimension的使用
- MFC edit控件实现自动换行
- 标签布局,实现添加多个button自动换行,可自定义许多属性
- MFC EDIT控件实现自动换行(修改属性)
- MFC EditControl控件实现换行且滚动条自动跟随(实例)
- div+pre标签的组合实现文本原格式显示与自动换行