`
renyuan_1991
  • 浏览: 69282 次
  • 性别: 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:项目中的类请以博客中为准)
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics