自定义View(四) ViewGroup 动态添加变长Tag标签 支持自动换行

    欲实现如下效果

    

    思路很简单就2步:

    1、测量出ViewGroup的大小

    2、找出子View的位置

若要实现动态添加标签view,就要实现ViewGroup的onMeasure()、onLayout()方法,这两个方法可由该ViewGroup的requestLayout()方法触发,

onMeasure是对ViewGroup的大小计算,onLayout是针对View(可以是子View也可以是本View)的位置设置

 

关于这几个方法的流程图如下:

    

        

/**
 * 流式标签(动态的,根据传入的数据动态添加标签)
 */
public class CustomerFlowLayout extends ViewGroup {
    
    private List<String> mTags = new ArrayList<String>();
    
    public CustomerFlowLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }
 
    public CustomerFlowLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    public CustomerFlowLayout(Context context) {
        super(context);
    }
    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        
        //当前ViewGroup的总高度
        int totalHeight= 0;
        //所有行中的最大宽度
        int maxLineWidth = 0;
        
        //当前行的最大高度
        int lineMaxHeight = 0;
        //当前行的总宽度
        int currentLineWidth = 0;
        
        //每个childView所占用的宽度
        int childViewWidthSpace = 0;
        //每个childView所占用的高度
        int childViewHeightSpace = 0;
        
        int count = getChildCount();
        MarginLayoutParams layoutParams;
        
        for(int i = 0; i < count; i++){
            View child = getChildAt(i);
            
            if(child.getVisibility() != View.GONE){//只有当这个View能够显示的时候才去测量
                //测量每个子View,以获取子View的宽和高
                measureChild(child, widthMeasureSpec, heightMeasureSpec);
                
                layoutParams = (MarginLayoutParams) child.getLayoutParams();
                
                childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
                childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
                
                if(currentLineWidth + childViewWidthSpace > widthSize){//表示如果当前行再加上现在这个子View,就会超出总的规定宽度,需要另起一行
                    totalHeight += lineMaxHeight;
                    if(maxLineWidth < currentLineWidth){//如果行的最长宽度发生了变化,更新保存的最长宽度
                        maxLineWidth = currentLineWidth;
                    }
                    currentLineWidth = childViewWidthSpace;//另起一行后,需要重置当前行宽
                    lineMaxHeight = childViewHeightSpace;
                }else{//表示当前行可以继续添加子元素
                    currentLineWidth += childViewWidthSpace;
                    if(lineMaxHeight < childViewHeightSpace){
                        lineMaxHeight = childViewHeightSpace;
                    }
                }
            }
        }
        
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : maxLineWidth, heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight);
        
    }
 
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        //当前是第几行
        int currentLine = 1;
        //存放每一行的最大高度
        List<Integer> lineMaxHeightList = new ArrayList<Integer>();
        
        //每个childView所占用的宽度
        int childViewWidthSpace = 0;
        //每个childView所占用的高度
        int childViewHeightSpace = 0;
        
        //当前行的最大高度
        int lineMaxHeight = 0;
        //当前行的总宽度
        int currentLineWidth = 0;
        
        int count = getChildCount();
        MarginLayoutParams layoutParams;
        
        for(int i = 0; i < count; i++){
            int cl= 0, ct = 0, cr = 0, cb = 0;
            View child = getChildAt(i);
            if(child.getVisibility() != View.GONE){//只有当这个View能够显示的时候才去测量
            
                layoutParams = (MarginLayoutParams) child.getLayoutParams();
                childViewWidthSpace = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;
                childViewHeightSpace = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;
                
                System.out.println("getWidth()---->"+getWidth());
                
                if(currentLineWidth + childViewWidthSpace > getWidth()){//表示如果当前行再加上现在这个子View,就会超出总的规定宽度,需要另起一行
                    lineMaxHeightList.add(lineMaxHeight);//此时先将这一行的最大高度加入到集合中
                    //新的一行,重置一些参数
                    currentLine++;
                    currentLineWidth = childViewWidthSpace;
                    lineMaxHeight = childViewHeightSpace;
                    
                    cl = layoutParams.leftMargin;
                    if(currentLine > 1){
                        for(int j = 0; j < currentLine - 1; j++){
                            ct += lineMaxHeightList.get(j);
                        }
                        ct += layoutParams.topMargin ;
                    }else{
                        ct = layoutParams.topMargin;
                    }
                }else{//表示当前行可以继续添加子元素
                    cl = currentLineWidth + layoutParams.leftMargin;
                    if(currentLine > 1){
                        for(int j = 0; j < currentLine - 1; j++){
                            ct += lineMaxHeightList.get(j);
                        }
                        ct += layoutParams.topMargin;
                    }else{
                        ct = layoutParams.topMargin;
                    }
                    currentLineWidth += childViewWidthSpace;
                    if(lineMaxHeight < childViewHeightSpace){
                        lineMaxHeight = childViewHeightSpace;
                    }
                }
                
                cr = cl + child.getMeasuredWidth();
                cb = ct + child.getMeasuredHeight();
                
                child.layout(cl, ct, cr, cb);
            
            }
        }
    }
    
    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }
    
    public void setTags(List<String> tags){
        if(tags!= null){
            mTags.clear();
            mTags.addAll(tags);
            for(int i = 0; i < mTags.size(); i++){
                TextView tv = new TextView(getContext());
                MarginLayoutParams lp = new MarginLayoutParams(MarginLayoutParams.WRAP_CONTENT, MarginLayoutParams.WRAP_CONTENT);
                lp.setMargins(15, 15, 15, 15);
//                lp.width = MarginLayoutParams.WRAP_CONTENT;
//                lp.height = MarginLayoutParams.WRAP_CONTENT;
                tv.setLayoutParams(lp);
                tv.setBackgroundResource(R.drawable.tv_bg);
                /*
                 * setPadding一定要在setBackgroundResource后面使用才有效!!!
                 * http://stackoverflow.com/questions/18327498/setting-padding-for-textview-not-working
                 */
                tv.setPadding(15, 15, 15, 15);
                tv.setTextColor(Color.WHITE);
                
                tv.setText(mTags.get(i));
                
                tv.setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if(listener != null){
                            listener.onClick(v);
                        }
                    }
                });
                
                addView(tv);
            }
            requestLayout();
        }
    }
    
    private OnTagItemClickListener listener;
    public interface OnTagItemClickListener{
        public void onClick(View v);
    }
    public void setOnTagItemClickListener(OnTagItemClickListener l){
        listener = l;
    }
 
}

          针对在Activity中的使用:

public class MainActivity extends Activity {
    
    private CustomerFlowLayout customerFlowLayout;
    
    List<String> tags = new ArrayList<String>();
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_dynamic_tagflowlayout);
        
        CustomerFlowLayout = (CustomerFlowLayout) findViewById(R.id.dynamic_tag);
        customerFlowLayout.setOnTagItemClickListener(new OnTagItemClickListener() {
            @Override
            public void onClick(View v) {
                TextView tv = (TextView) v;
                Toast.makeText(MainActivity.this, tv.getText().toString(), Toast.LENGTH_SHORT).show();
            }
        });
        
        initData();
        customerFlowLayout.setTags(tags);
    }
 
    private void initData() {
        tags.add("阳哥你好!");
        tags.add("Android开发");
        tags.add("新闻热点");
        tags.add("热水进宿舍啦!");
        tags.add("I love you");
        tags.add("成都妹子");
        tags.add("新余妹子");
        tags.add("仙女湖");
        tags.add("创新工厂");
        tags.add("孵化园");
        tags.add("神州100发射");
        tags.add("有毒疫苗");
        tags.add("顶你阳哥阳哥");
        tags.add("Hello World");
        tags.add("闲逛的蚂蚁");
        tags.add("闲逛的蚂蚁");
        tags.add("闲逛的蚂蚁");
        tags.add("闲逛的蚂蚁");
        tags.add("闲逛的蚂蚁");
        tags.add("闲逛的蚂蚁");
    }
 
}

   上述的ViewGroup中可以添加 对标签的处理(例如:点击、选中)

  参考资料:

    https://blog.csdn.net/shakespeare001/article/details/51089128

    https://www.cnblogs.com/ldq2016/p/9035332.html

   

原文地址:https://www.cnblogs.com/bimingcong/p/9947739.html