Android流式布局控件

1,自定义flowlayout代码

package com.hyang.administrator.studentproject.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

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

/**
 * Created by Administrator on 2017/6/20.
 */
public class FlowGroupView extends ViewGroup {
    /**
     * 储存所有的view 按行记录
     */
    private List<List<View>> mAllViews = new ArrayList<List<View>>();
    /**
     * 记录每一行的高度
     */
    private List<Integer> mLineHeight = new ArrayList<Integer>();
    private String TAG = "TAG";

    public FlowGroupView(Context context, AttributeSet attrs,
                         int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

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

    public FlowGroupView(Context context) {
        super(context);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 置空 view 容器 和 lineHeight 容器  重新赋值
        //因为OnMeasure方法会走两次,第一次是实例化这个对象的时候高度和宽度都是0
        //之后走了OnSizeChange()方法后 又走了一次OnMeasure,所以要把第一次加进去的数据清空。
        mAllViews.clear();
        mLineHeight.clear();
        //得到上级容器为其推荐的宽高和计算模式
        int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);
        int specHeighMode = MeasureSpec.getMode(heightMeasureSpec);
        int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);
        int specHeighSize = MeasureSpec.getSize(heightMeasureSpec);
        // 计算出所有的 child 的 宽和高
//      measureChildren(specWidthSize, specHeighSize);
        // 记录如果是 warp_content 是设置的宽和高
        int width = 0;
        int height = 0;
        // 得到子view的个数
        int cCount = getChildCount();
        /**
         * 记录每一行的宽度,width不断取最大宽度
         */
        int lineWidth = 0;
        /**
         * 每一行的高度,累加至height
         */
        int lineHeight = 0;

        // 存储每一行所有的childView
        List<View> lineViews = new ArrayList<View>();

        for (int i = 0; i < cCount; i++) {
            // 得到每个子View
            View child = getChildAt(i);
            // 测量每个子View的宽高
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            // 当前子view的lp
            MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
            // 子view的宽和高
            int cWidth = 0;
            int cheight = 0;
            // 当前子 view 实际占的宽
            cWidth = child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;
            // 当前子View 实际占的高
            cheight = child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
            lineHeight=cheight;
            // 需要换行
            if(lineWidth + cWidth > specWidthSize){
                width = Math.max(lineWidth, cWidth);// 取最大值
                lineWidth = cWidth; // 开启新行的时候重新累加width
                // 开启新行时累加 height
//              lineHeight = cheight; // 记录下一行的高度
                mAllViews.add(lineViews);
                mLineHeight.add(cheight);
                lineViews = new ArrayList<>();
                // 换行的时候把该 view 放进 集合里
                lineViews.add(child);// 这个  view(child) 是下一行的第一个view
                height += cheight; //每个View高度是一样的,直接累加
                Log.e("需要换行", "hight--" + height);
                Log.e("onMeasure", "AllViews.size()  --  > " + mAllViews.size());
            }else {
                // 不需要换行
                lineWidth += cWidth;//
                Log.e("不需要换行","hight--"+height);
                // 不需要换行时 把子View add 进集合
                lineViews.add(child);
            }

            if(i == cCount-1){
                // 如果是最后一个view
                width = Math.max(lineWidth, cWidth);
                height += cheight;
                Log.e("最后一个view","hight--"+height);
            }
        }
        // 循环结束后 把最后一行内容add进集合中
        mLineHeight.add(lineHeight); // 记录最后一行
        mAllViews.add(lineViews);
        // MeasureSpec.EXACTLY 表示设置了精确的值
        // 如果 mode 是 MeasureSpec.EXACTLY 时候,则不是 warp_content 用计算来的值,否则则用上级布局分给它的值
        setMeasuredDimension(
                specWidthMode == MeasureSpec.EXACTLY ? specWidthSize : width,
                specHeighMode == MeasureSpec.EXACTLY ? specHeighSize : height
        );
        Log.e("onMeasure", "mAllViews.size() -- > " + mAllViews.size() + "   mLineHeight.size() -- > " + mLineHeight.size() + "Height -- > "+height);
    }

    /**
     * 所有childView的位置的布局
     */
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 当前行的最大高度
        int lineHeight = 0;
        // 存储每一行所有的childView
        List<View> lineViews = new ArrayList<View>();
        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);

            Log.e("onLayout" , "第" + i + "行 :" + lineViews.size()+"-------lineHeight"+ lineHeight);

            // 遍历当前行所有的View
            for (int j = 0; j < lineViews.size(); j++)
            {
                View child = lineViews.get(j);
                if (child.getVisibility() == View.GONE)
                {
                    continue;
                }
                MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

                //计算childView的left,top,right,bottom
                int lc = left + lp.leftMargin;
                int tc = top + lp.topMargin;
                int rc =lc + child.getMeasuredWidth();
                int bc = tc + child.getMeasuredHeight();

                child.layout(lc, tc, rc, bc);

                left += child.getMeasuredWidth() + lp.rightMargin + lp.leftMargin;
            }
            left = 0;
            top += lineHeight;
        }
        Log.v("onLayout", "onLayout   mAllViews.size() -- > " + mAllViews.size() + "   mLineHeight.size() -- > "+ mLineHeight.size());
    }


    /**
     * 这个一定要设置,否则会包强转错误
     * 设置它支持 marginLayoutParams
     */
    @Override
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {

        return new MarginLayoutParams(getContext(),attrs);
    }
}

2.在布局文件中使用

1 <com.hyang.administrator.studentproject.widget.FlowGroupView
2     android:id="@+id/flow_view_group"
3     android:layout_width="match_parent"
4     android:layout_height="wrap_content">
5 </com.hyang.administrator.studentproject.widget.FlowGroupView>

3.TextView的样式文件

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >

    <solid android:color="#E7E7E7" >
    </solid>
    <corners
        android:radius="30dp"
        />

    <padding
        android:bottom="2dp"
        android:left="10dp"
        android:right="10dp"
        android:top="2dp" />
</shape>

4.在Activity中使用

 1 package com.hyang.administrator.studentproject.activity;
 2 
 3 import android.graphics.Color;
 4 import android.os.Bundle;
 5 import android.support.v7.app.AppCompatActivity;
 6 import android.view.View;
 7 import android.view.ViewGroup;
 8 import android.widget.Button;
 9 import android.widget.TextView;
10 import android.widget.Toast;
11 
12 import com.hyang.administrator.studentproject.R;
13 import com.hyang.administrator.studentproject.widget.FlowGroupView;
14 
15 import org.xutils.view.annotation.ViewInject;
16 import org.xutils.x;
17 
18 import java.util.ArrayList;
19 
20 public class FlowLayoutActivity extends AppCompatActivity {
21 
22     @ViewInject(R.id.flow_button)
23     private Button addTextButton;
24     @ViewInject(R.id.flow_view_group)
25     private FlowGroupView flowView;
26 
27     private ArrayList<String> names;
28 
29 
30     @Override
31     protected void onCreate(Bundle savedInstanceState) {
32         super.onCreate(savedInstanceState);
33         setContentView(R.layout.activity_flow_layout);
34         x.view().inject(this);
35 
36         setTwoFlowLayout();
37 
38         addTextButton.setOnClickListener(new View.OnClickListener() {
39             @Override
40             public void onClick(View v) {
41                 addTextView("添加1");
42             }
43         });
44     }
45 
46     private void setTwoFlowLayout() {
47         //添加数据
48         names = new ArrayList<String>();
49         names.add("降龙十八掌");
50         names.add("黯然销魂掌");
51         names.add("左右互搏术");
52         names.add("七十二路空明拳");
53         names.add("小无相功");
54         names.add("拈花指");
55         names.add("打狗棍法");
56         names.add("蛤蟆功");
57         names.add("九阴白骨爪");
58         names.add("一招半式闯江湖");
59         names.add("醉拳");
60         names.add("龙蛇虎豹");
61         names.add("葵花宝典");
62         names.add("吸星大法");
63         names.add("如来神掌警示牌");
64         //为布局添加内容
65         for (int i = 0; i < names.size(); i++) {
66             addTextView(names.get(i));
67         }
68     }
69 
70     /**
71      * 动态添加布局
72      * @param str
73      */
74     private void addTextView(String str) {
75         TextView child = new TextView(this);
76         ViewGroup.MarginLayoutParams params = new ViewGroup.MarginLayoutParams(ViewGroup.MarginLayoutParams.WRAP_CONTENT, ViewGroup.MarginLayoutParams.WRAP_CONTENT);
77         params.setMargins(5, 5, 5, 5);
78         child.setLayoutParams(params);
79         child.setBackgroundResource(R.drawable.flag);
80         child.setText(str);
81         child.setTextColor(Color.WHITE);
82         initEvents(child);//监听
83         flowView.addView(child);
84     }
85 
86     /**
87      * 为每个view 添加点击事件
88      */
89     private void initEvents(final TextView tv){
90         tv.setOnClickListener(new View.OnClickListener() {
91 
92             @Override
93             public void onClick(View v) {
94                 Toast.makeText(FlowLayoutActivity.this, tv.getText().toString(), Toast.LENGTH_SHORT).show();
95             }
96         });
97     }
98 }

 

posted @ 2017-06-20 22:46  点滴之水  阅读(5431)  评论(0编辑  收藏  举报