当前位置: 移动技术网 > 移动技术>移动开发>Android > 自定义控件之流式布局

自定义控件之流式布局

2020年07月09日  | 移动技术网移动技术  | 我要评论

实现如上效果。

实现思路:

控件FlowLayout继承自ViewGroup,重写onMeasure[测量]、onLayout[布局]方法。addItem()方法用于增加String类型的list

注意事项:

1. 测量子View的宽和高时,要先调用measureChild,child.getMeasuredHeight();才能获取到值 

2. onMeasure中元素换行时的处理。用arr存储每行的第一个元素所对应的index,便于onLayout使用

3. onLayout中每行的第一个元素和后续元素的关系,以及当前行和前一行的关系

4. 可以把distanceV、distanceH作为参数提取出来,更灵活

代码如下:

1.FlowLayout

public class FlowLayout extends ViewGroup {
    private static final String TAG = "FlowLayout";

    private int paddingT, paddingB, paddingL, paddingR;//上下左右的内边距
    private int distanceH, distanceV;//纵、横轴上每个元素中间的间距
    private int lineH;//行高

    public FlowLayout(Context context) {
        this(context, null);
    }

    public FlowLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
    
    private void init() {
        distanceV = distanceH = 20;
    }

    private ArrayList<Integer> arr = new ArrayList<>(); //每行中第一个字符串对应的index

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        arr.clear();

        paddingL = getPaddingStart();
        paddingR = getPaddingEnd();
        paddingT = getPaddingTop();
        paddingB = getPaddingBottom();
        Log.d(TAG, "onMeasure: [" + paddingL + "," + paddingT + "," + paddingR + "," + paddingB+"]");

        int w = getMeasuredWidth();

        int cnt = getChildCount();
        int lineNum = 1;
        int sumW = 0;//当前行所占总宽度
        for (int i = 0; i < cnt; i++) {
            View child = getChildAt(i);
            // 测量子View的宽和高:先调用measureChild,child.getMeasuredHeight();才能获取到值
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            //LayoutParams lp = child.getLayoutParams();

            lineH = child.getMeasuredHeight();
            int tempW = child.getMeasuredWidth();

            if (0 == i) {//这一行的第一个元素
                sumW = paddingL + paddingR + tempW;
                arr.add(i);//把第一个元素对应的index存入arr
                Log.d(TAG, "onMeasure: line" + lineNum);
            } else {
                sumW += tempW + distanceH;
            }

            if (sumW > w) {
                sumW = paddingL + paddingR + tempW;
                lineNum++;
                arr.add(i);//换行时,把该元素对应的index存入arr
                Log.d(TAG, "onMeasure: line" + lineNum + ", index = " + i);
            }
        }
        
        int h = lineH*lineNum + paddingT + paddingB + (lineNum-1)*distanceV;//所有元素所占宽高,包含padding 
        Log.d(TAG, "onMeasure: w = " + w + ", h = " + h);
        setMeasuredDimension(w, h)  ;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int cnt = getChildCount();
        int curL, curR, curT, curB ,preL;
        int preT = 0;

        for (int i = 0; i < arr.size(); i++) {
            //当前行第一个元素对应的index
            int curLineFirstEleIndex = arr.get(i);

            //当前行最后一个元素对应的index
            boolean lastLine = i+1 >= arr.size();//最后一行
            int curLineLastEleIndex = lastLine ? cnt-1 : arr.get(i + 1) - 1;//下一行的前一个元素,为当前行的最后一个元素
            Log.d(TAG, "onLayout: line" + i + "=[" + curLineFirstEleIndex + "," + curLineLastEleIndex + "]");

            preL = 0;//每进入新的一行,preL要置0,curL才会从最左侧开始计算
            curT = (preT == 0) ? paddingT : (preT+distanceV);
            curB = curT + lineH;

            //针对当前行做处理
            for (int j = curLineFirstEleIndex; j <= curLineLastEleIndex; j++){
                View child = getChildAt(j);
                //若是第一个元素,则其左边距为paddingL;否则为上个元素的左边界加元素横向间距
                curL = (preL==0) ? paddingL : (preL + distanceH);
                curR = curL + child.getMeasuredWidth();
                //curT = paddingT;
                //curB = curT + lineH;
                child.layout(curL, curT, curR, curB);

                preL = curR;//把当前元素的右边界 赋值给 preL
            }
            preT = curB;//把当前行的底边界 赋值给 preT
        }
    }
    
    public void addItem(List<String> itemList) {
        if (null != itemList && itemList.size() > 0) {
            for (int i = 0; i < itemList.size(); i++) {
                TextView tv = (TextView)LayoutInflater.from(getContext()).inflate(R.layout.item, null);
                tv.setText(itemList.get(i));
                addView(tv);
            }
        }
    }
}

2. item.xml

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    tools:text="test"
    android:background="@drawable/text_bg_shape"
    android:id="@+id/tv"
    android:textSize="25sp"
    android:textColor="#000"
    android:paddingLeft="15dp"
    android:paddingRight="15dp"
    android:paddingTop="5dp"
    android:paddingBottom="5dp"/>

3.text_bg_shape.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="30dp"/>
    <solid android:color="#0ee"/>
</shape>

4.activity

  <com.lyl.ftest.FlowLayout
      android:layout_height="match_parent"
      android:layout_width="match_parent"
      android:layout_margin="10dp"
      android:id="@+id/flow"/>

        FlowLayout f = findViewById(R.id.flow);
        ArrayList<String> itemList = new ArrayList<>();
        itemList.add("你好");
        itemList.add("你好你好你好你好");
        itemList.add("好好好好");
        itemList.add("好好好好12324");
        itemList.add("你好12325");
        f.addItem(itemList);

 

本文地址:https://blog.csdn.net/lyl0530/article/details/107206927

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网