`
renyuan_1991
  • 浏览: 70595 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

自定义ViewGroup实现动态创建可换行标签

阅读更多
转载请注明:http://renyuan-1991.iteye.com/blog/2232463
具体实现步骤:
1.继承ViewGroup,实现三个构造方法
2.通过generateLayoutParams给自定义的控件指定参数
3.实现onMeasure方法
       a.在这个方法里面首先要做是要知道自己的大小,onMeasure方法会通过父类获取具体的模式和大小。通过getMode方法获得模式(三种模式就不详细说了),然后通过getSize方法获取具体的尺寸。
       b.通过遍历子控件得到ViewGroup显示时的高度。当子控件(即标签)的宽度之和大于父控件的时候开启行并累加高度
       c.setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth 
                : width, (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight 
                : height);  方法确定我们ViewGroup最终的大小
4.重写onLayout方法
        a.同样的需要遍历子控件,根据宽度控制是否换行。并将每一行的空间用一个列表记录下来,并记录高度从而决定下一行需要显示的位置。
        b.上面已经将标签以行为单位分别放到适当的list里面。这里就是将list里面的控件显示出来。说到底其实就是一个道理:不管通过什么样的算法,只要知道子控件正确的显示位置即可。
        c.确定完位置后调用child.layout(lc, tc, rc, bc);  方法将它画出来即可。需要注意的地方是最后一行我们需要特殊处理。通过上面的判断我们实际的最后一行是永远不会大于ViewGroup的宽度的,而这一行的高度同样需要记录下来

完整的代码如下:


package com.xiaoying.widget;

import java.util.ArrayList;
import java.util.List;


import android.content.Context;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import com.xiaoying.common.util.Utils;
import com.xiaoying.cuishou.R;
/**
 * @author  Roy
 * @version V1.0 date:2017/11/30 下午6:07
 */
public class MyFlowLayout extends ViewGroup {

    private MarkClickListener markClickListener;

	public MyFlowLayout(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}

	public MyFlowLayout(Context context, AttributeSet attrs) {
		super(context, attrs);
	}

	public MyFlowLayout(Context context) {
		super(context);
	}

	public void setData(String[] data){
		createChild(data,10, 15, 10, 15, 10, 0, 12, 12 , 0);
	}
	public void setData(String[] data, int textSize,int pl,int pt,int pr,int pb,int ml,int mt,int mr,int mb){
		createChild(data,textSize, pl, pt, pr, pb, ml, mt, mr, mb);
	}

	public void setData(List<String> data){
        String[] mydata = null;
        if(data!=null){
            int length = data.size();
            mydata = new String[length];
            for(int i = 0 ; i<length;i++){
                mydata[i] = data.get(i);
            }
        }
		setData(mydata);
	}
	public void setData(List<String> data,int textSize,int pl,int pt,int pr,int pb,int ml,int mt,int mr,int mb){
		String[] mydata = null;
		if(data!=null){
			int length = data.size();
			mydata = new String[length];
			for(int i = 0 ; i<length;i++){
				mydata[i] = data.get(i);
			}
		}
		setData(mydata, textSize,pl, pt, pr, pb, ml, mt, mr, mb);
	}

	public void setOnClickListener(MarkClickListener markClickListener){
        this.markClickListener = markClickListener;
    }

    public interface MarkClickListener{
        void onRemarkClick(String str);
    }


    private void createChild(String[] data,int textSize,int pl,int pt,int pr,int pb,int ml,int mt,int mr,int mb){
		int size = data.length;
		for(int i = 0;i<size;i++){
			String text = data[i];
			TextView btn = new TextView(getContext());
			btn.setClickable(true);
			btn.setGravity(Gravity.CENTER);
			btn.setText(text);
            btn.setTag(text);
			btn.setTextSize(textSize);
			btn.setPadding(Utils.dip2px(getContext(), pl), Utils.dip2px(getContext(), pt), Utils.dip2px(getContext(), pr), Utils.dip2px(getContext(), pb));
			btn.setTextColor(0xff2b3041);
			/*btn.setTextColor(getResources().getColorStateList(R.color.selector_button_tc));*/
			btn.setBackgroundResource(R.drawable.mark_green);
			MarginLayoutParams params = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT);
			params.setMargins(Utils.dip2px(getContext(), ml), Utils.dip2px(getContext(), mt), Utils.dip2px(getContext(), mr), Utils.dip2px(getContext(), mb));
			btn.setLayoutParams(params);
			btn.setOnClickListener(new OnClickListener() {
				@Override
				public void onClick(View v) {
                    markClickListener.onRemarkClick(((TextView)v).getText().toString());
				}
			});
			this.addView(btn);
		}
	}

	@Override
	public LayoutParams generateLayoutParams(AttributeSet attrs) {
		return new MarginLayoutParams(getContext(), attrs);
	}


	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		int childCount = getChildCount();
		int lineWidth = 0;
		int lineHeight = 0;
		int width = 0;//warpcontet是需要记录的宽度
		int height = 0;
		for(int i = 0 ; i< childCount;i++){
			View child = getChildAt(i);
			// 测量每一个child的宽和高
			measureChild(child, widthMeasureSpec, heightMeasureSpec);
			MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
			int childWidth = child.getMeasuredWidth()+lp.leftMargin+lp.rightMargin;
			int childHeight = child.getMeasuredHeight()+lp.topMargin+lp.bottomMargin;

			if(lineWidth+childWidth>widthSize){
				//这种情况就是排除单个标签很长的情况
				width = Math.max(lineWidth, childWidth);
				//开启新行
				lineWidth = childWidth;
				//记录总行高
				height += lineHeight;
				//因为开了新行,所以这行的高度要记录一下
				lineHeight = childHeight;
			}else{
				lineWidth += childWidth;
				//记录行高
				lineHeight = Math.max(lineHeight, childHeight);
			}
			// 如果是最后一个,则将当前记录的最大宽度和当前lineWidth做比较
            if (i == childCount - 1)
            {
				//宽度
                width = Math.max(width, lineWidth);
                height += lineHeight;
            }
		}

		setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize
                : width, (heightMode == MeasureSpec.EXACTLY) ? heightSize
                : height);
	}

	/**
	 * 存储所有的View,按行记录
	 */
	private List<List<View>> mAllViews = new ArrayList<List<View>>();
	/**
	 * 记录每一行的最大高度
	 */
	private List<Integer> mLineHeight = new ArrayList<Integer>();

	//onLayout中完成对所有childView的位置以及大小的指定
	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		//清空子控件列表
		mAllViews.clear();
		//清空高度记录列表
        mLineHeight.clear();
		//得到当前控件的宽度(在onmeasure方法中已经测量出来了)
        int width = getWidth();

        int childCount = getChildCount();
		// 存储每一行所有的childView
        List<View> lineViews = new ArrayList<View>();
		//行高
        int lineWidth = 0;
		//总行高
        int lineHeight = 0;
        for(int i = 0 ; i<childCount;i++){
        	View child = getChildAt(i);
			//得到属性参数
        	MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
        	int childWidth = child.getMeasuredWidth();
        	int childHeight = child.getMeasuredHeight();
			// 如果需要换行
        	if (childWidth + lp.leftMargin + lp.rightMargin + lineWidth > width)
        	{
        		mLineHeight.add(lineHeight);
				// 将当前行的childView保存,然后开启新的ArrayList保存下一行的childView
        		mAllViews.add(lineViews);
				// 重置行宽
        		lineWidth = 0;
				lineHeight = 0;
        		lineViews = new ArrayList<View>();
        	}
			/**
			 * 如果不需要换行,则累加
			 */
			lineWidth += childWidth + lp.leftMargin + lp.rightMargin;
            lineHeight = Math.max(lineHeight, childHeight + lp.topMargin
                    + lp.bottomMargin);
            lineViews.add(child);
        }
		// 记录最后一行  (因为最后一行肯定大于父布局的宽度,所以添加最后一行是必要的)
		mLineHeight.add(lineHeight);
        mAllViews.add(lineViews);
        int left = 0;
        int top = 0;
        int lineNums = mAllViews.size();
        for(int i = 0;i<lineNums;i++){
			// 每一行的所有的views
            lineViews = mAllViews.get(i);
			// 当前行的最大高度
            lineHeight = mLineHeight.get(i);
            for(int j = 0 ;j < lineViews.size() ; j++){
            	View lineChild = lineViews.get(j);
            	if(lineChild.getVisibility() == View.GONE){
            		continue;
            	}
            	MarginLayoutParams lp = (MarginLayoutParams) lineChild.getLayoutParams();
				//开始画标签了。左边和上边的距离是要根据累计的数确定的。
            	int lc = left + lp.leftMargin;
            	int tc = top+lp.topMargin;
            	int rc = lc+lineChild.getMeasuredWidth();
            	int bc = tc+lineChild.getMeasuredHeight();
            	lineChild.layout(lc, tc, rc, bc);
            	left += lineChild.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
            }
			//将left归零
            left = 0;
            top += lineHeight;
        }

	}

}



在界面中调用的方法
@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		MyFlowLayout myview = (MyFlowLayout) findViewById(R.id.myview);
		String[] myData = {"one","two","dkfjdkf","kdfkdfj","jdkfdfkjdkfdkfkdkdfj","kdjkfdjkjkjskkjkd"};
		myview.setData(myData, this, 15, 10, 10, 10, 10, 10, 10, 10, 10);
		
	}



下载连接:http://download.csdn.net/detail/u010419467/8956027

希望爱好编程的小伙伴能加这个群,互相帮助,共同学习。群号: 141877583



最后附上完整项目(不需要积分)(ps:项目中的类请以博客中为准)
分享到:
评论

相关推荐

    android 自动换行的自定义viewgroup

    标题"android 自动换行的自定义viewgroup"所指的,就是创建一个能够根据子视图的数量和大小自动调整布局,实现自动换行效果的自定义 ViewGroup 类。这样的组件通常用于展示网格或者流式布局,例如商品列表、图片墙等...

    Android 标签,可以自动换行的ViewGroup

    在Android开发中,"标签(Tags)"通常指的是在用户界面上用于表示多个选项或类别的元素,类似于传统的HTML中...开发者可以利用现有的开源库或自定义ViewGroup来实现这一功能,以创建更加灵活、美观的Android应用程序。

    android自定义viewgroup实现等分格子布局

    在Android开发中,自定义ViewGroup是创建复杂布局或实现特定功能的重要手段。"android自定义viewgroup实现等分格子布局"这个话题涉及到如何利用自定义ViewGroup类来创建一个能够平均分配屏幕空间的网格布局。下面...

    自定义ViewGroup实现流式布局demo

    本教程将深入探讨如何通过自定义ViewGroup来实现一个流式布局(FlowLayout)的示例,这在展示多个小图标或者创建类似瀑布流的效果时非常有用。流式布局能让元素自动换行,根据屏幕尺寸动态调整布局。 首先,我们...

    Android自动换行标签控件(二)

    在这个案例中,我们将自定义一个`ViewGroup`子类,比如命名为`AutoWrapTagLayout`,来实现自动换行的功能。 实现这个功能的关键在于计算每个标签的宽度,并根据屏幕宽度动态调整它们的布局。我们需要覆写`onMeasure...

    Android 自定义ViewGroup 实战篇 -> 实现FlowLayout源码程序

    在Android开发中,自定义ViewGroup是实现复杂布局和交互的关键技术之一。本实战篇将聚焦于如何实现一个名为FlowLayout的自定义布局,它允许子视图按行排列,类似于HTML中的`&lt;div&gt;`标签。在Android应用设计中,...

    Android 自定义ViewGroup 实战篇 实现FlowLayout.rar

    在Android开发中,自定义ViewGroup是提升应用界面交互性和个性化设计的重要手段。本实战篇主要探讨如何实现一个名为FlowLayout的自定义布局组件,它允许子View按行排列,达到类似HTML流式布局的效果。在Android SDK...

    Android 自定义控件实现自动换行效果

    本示例将探讨如何创建一个自定义的ViewGroup来实现商品属性显示时的自动换行效果,类似于常见于电商应用的商品详情页。这个过程涉及到布局管理、事件处理以及自定义View的基本原理。 首先,我们需要理解Android中的...

    多标签自动换行的控件ViewGroup

    总的来说,创建一个多标签自动换行的`ViewGroup`需要理解Android布局系统的底层工作原理,以及如何通过自定义`ViewGroup`来实现特定的布局需求。通过这样的实践,开发者可以更灵活地控制界面的布局,以适应各种屏幕...

    Android自定义ViewGroup之FlowLayout(三)

    在Android开发中,自定义ViewGroup是实现特定布局需求的重要手段。本文主要讲解如何自定义一个FlowLayout,这种布局方式能够适应各种需要动态排列元素的场景,例如关键字标签和搜索热词列表。在Android SDK中并未...

    android自定义控件实现自动换行效果

    本教程将探讨如何通过自定义一个`ViewGroup`来实现类似电商平台商品属性的自动换行效果。这种效果通常用于展示商品的多维度信息,如颜色、尺寸、材质等,当一行无法容纳所有信息时,它们会自动换行到下一行。 首先...

    自动换行的ViewGroup

    1. **创建自定义ViewGroup:** 创建一个名为AutoWrapLinearLayout的新类,继承自LinearLayout,并在构造函数中设置布局参数。 2. **重写onMeasure()方法:** 这是实现自动换行的关键。遍历所有的子视图,依次测量...

    Android自定义ViewGroup实现流式布局

    在Android开发中,自定义ViewGroup是实现特定布局需求的重要手段。本文主要讲解如何通过自定义ViewGroup实现流式布局(FlowLayout)。流式布局是一种常见的布局方式,它允许子View按照从左到右的顺序填充容器,当一...

    Android自定义ViewGroup之实现FlowLayout流式布局

    在Android开发中,有时系统默认的布局管理器如LinearLayout、RelativeLayout等并不能满足所有需求,这时我们就需要自定义ViewGroup来实现特定的布局效果。本文将详细介绍如何实现一个自定义的FlowLayout,这是一种...

    Android自定义ViewGroup实现标签流容器FlowLayout

    在Android开发中,自定义ViewGroup是扩展UI功能和实现个性化布局的重要手段。本文将深入讲解如何实现一个自定义的标签流式布局——FlowLayout。在Android应用开发中,经常需要处理像热门标签那样能自动换行的布局,...

    android商品属性选择标签控件,可实现自动换行

    可以使用`LinearLayout`配合`android:orientation="vertical"`属性垂直排列标签,然后利用`FlowLayout`或者自定义布局实现换行。在`FlowLayout`中,可以通过重写`onMeasure()`方法来检测当前行是否已满,如果满了则...

    解决自动换行,自适应高度。常见与标签样式的 ViewGroup

    通过自定义ViewGroup,我们可以灵活地控制视图的行为,以满足特定的设计需求。在本例中,使用FlowLayout或自定义的类似布局,结合恰当的测量逻辑,就能创建一个能够自动换行和自适应高度的标签样式组件。

    多个view自动换行

    我们可以创建一个自定义的`ViewGroup`来实现这个功能。例如,提供的`MyViewGroup.java`可能就是一个实现了自动换行功能的自定义布局。这个类可能会重写`onMeasure()`和`onLayout()`方法,以确定每个子视图的位置,...

    添加标签,自动换行,并可以多选,单选

    在Android中,原生的FlowLayout并未包含在SDK中,但开发者可以通过自定义ViewGroup来实现类似的功能。 2. **自动添加标签**: 这个功能意味着FlowLayout被扩展以支持动态添加标签视图,比如TextView,这些标签可以...

    自定义实现流式布局

    本资源提供了一个自定义ViewGroup实现的流式布局,对于开发者来说,能够更加灵活地控制界面元素的展示,尤其是在需要动态添加或排列多个元素时。 在Android系统中,基础的布局管理器如LinearLayout、RelativeLayout...

Global site tag (gtag.js) - Google Analytics