安卓自定义流式布局

废话少说直接上代码,对自定义view有一定了解的同学想必都能看懂:

package com.example.text.view;

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

/**
 * 

*

* 横向排列的布局 * @author jinzhenhua * @version 1.0 ,create at:2019/11/28 20:33 */
public class FlowLayout extends ViewGroup { private String TAG = FlowLayout.class.getSimpleName(); public FlowLayout(Context context) { super(context); } public FlowLayout(Context context, AttributeSet attrs) { super(context, attrs); } public FlowLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); measureChildren(widthMeasureSpec,heightMeasureSpec);//测量所有小孩的宽高,否则取不到子控件的大小和边距等 int paddingTop = getPaddingTop(); int paddingLeft = getPaddingLeft(); int paddingRight = getPaddingRight(); int paddingBottom = getPaddingBottom(); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec);//经测试与getMeasuredWidth方法返回值一致 int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec);//控件的总高度 int totalHeight = paddingTop + paddingBottom;//控件总高 Log.e(TAG,"paddingTop:" + paddingTop + ",paddingBottom:" + paddingBottom); int totalWidth = 0;//控件总宽,也可以默认取最大值 // if (heightMode == MeasureSpec.EXACTLY) {//固定宽高 // Log.e("test", "onMeasure:heightSize="+heightSize); // totalHeight = heightSize; // } int currentLineWidth = paddingLeft + paddingRight;//当前行前边所有孩子的宽度之和 (包括左右边距) int currentLineHeight = 0;//当前行的高度(取决于本行最高的孩子的高度加上下间距),高度只有一个上下padding,不用每行都算padding int childCount = getChildCount(); for(int i = 0; i < childCount; i++){ View childView = getChildAt(i); MarginLayoutParams lp = (MarginLayoutParams) childView.getLayoutParams(); //加上当前子控件后,是否不超越父控件的宽度 int testWidth = currentLineWidth + childView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; if(testWidth > widthSize){//超过最大限制,需要换行 totalWidth = Math.max(totalWidth,currentLineWidth);//取最大的那一行的宽度 currentLineWidth = paddingLeft + paddingRight;//重置宽度 totalHeight += currentLineHeight;//计算总高度 currentLineHeight = 0;//重置高度 } currentLineWidth += childView.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;//每次插入一个子控件,增加当前行的宽度 currentLineHeight = Math.max(currentLineHeight,childView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);//当前行,最高的高度 Log.e(TAG,"childView.getMeasuredHeight():" + childView.getMeasuredHeight() + "," + i + "lp.topMargin:" + lp.topMargin + "," + i +"lp.topMargin:" + lp.bottomMargin); } totalWidth = Math.max(totalWidth,currentLineWidth);//有可能最后一个元素所在的最后一行,宽度最大 totalHeight += currentLineHeight;//加上最后一行的高度 totalHeight = Math.min(totalHeight,heightSize);//使用高度最小的那个 Log.e(TAG,"totalHeight:" + totalHeight + ",heightSize:" + heightSize); setMeasuredDimension(totalWidth,totalHeight); } @Override protected void onLayout(boolean b, int i, int i1, int i2, int i3) { Log.e(TAG,"i:" + i + ",i1:" + i1 + ",i2:" + i2 + ",i3:" + i3); int paddingLeft = getPaddingLeft(); int paddingTop = getPaddingTop(); int currentLineWidth = paddingLeft;//当前行对应父组件所在的坐标,父组件的左上角为原点 int totalHeight = paddingTop;//之前所有行的总高度(包括上下边距); int currentLineHeight = 0;//当前行的高度(取决于本行最高的孩子的高度加上下间距) int childCount = getChildCount(); for (int j = 0; j < childCount; j++) { View child = getChildAt(j); MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); //测试一下新来的小孩,能否插入当前行 int testWidth = currentLineWidth + child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin; if(testWidth > getMeasuredWidth()){ totalHeight += currentLineHeight;//准备换行,总行高要增加当前行的高度 currentLineWidth = paddingLeft;//准备换行,重新开始计算行宽 currentLineHeight = 0;//准备换行,当前行高清零 } //插入小孩 int left = currentLineWidth + lp.leftMargin; int right = left + child.getMeasuredWidth(); int top = totalHeight + lp.topMargin; int bottom = top + child.getMeasuredHeight(); child.layout(left, top, right, bottom); // child.layout(50, 50, 300, 300); //每插完一个小孩,刷新当前行的行宽与行高 currentLineWidth += child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin;//每摆完一个小孩,更新当前行的总宽度 currentLineHeight = Math.max(currentLineHeight,child.getMeasuredHeight()+lp.topMargin + lp.bottomMargin);//每摆完一个小孩,检查本行行高是否被刷新 } totalHeight += currentLineHeight;//最后一个小孩插完,刷新总行高 } }


<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TestRxjavaActivity">

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="400dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent">
        <TextView
            android:text="阿达是打发斯蒂芬"
            android:layout_width="wrap_content"
            android:layout_height="20dp"/>
        <com.example.text.view.FlowLayout
            android:padding="10dp"
            android:id="@+id/flowlayout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/color_click_blue" />
    LinearLayout>


androidx.constraintlayout.widget.ConstraintLayout>

activity中:

flowlayout = findViewById(R.id.flowlayout);
        for(int i = 0; i < texts.length; i++){
            TextView textView = new TextView(this);
            textView.setText(texts[i]);
            textView.setBackgroundResource(R.color.gray);
            textView.setTextColor(R.color.black );
            ViewGroup.MarginLayoutParams params= new ViewGroup.MarginLayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
            params.setMargins(10,10,10,10);//设置边距
            flowlayout.addView(textView,params);//指定params类型,不然在viewGroup中获取时会默认是LayoutParams
        }

效果如下:
安卓自定义流式布局_第1张图片
这里右边有白边是因为我计算的时候是按最大的一行来算的,你如果想让他直接铺满屏幕可以在onMeasure 方法中设置宽高的时候直接用取到的宽度设置上去

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

        measureChildren(widthMeasureSpec,heightMeasureSpec);//测量所有小孩的宽高,否则取不到子控件的大小和边距等

        int paddingTop = getPaddingTop();
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingBottom = getPaddingBottom();

        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);//经测试与getMeasuredWidth方法返回值一致
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);//控件的总高度

		//省略若干代码...

        setMeasuredDimension(widthSize ,totalHeight);

    }

你可能感兴趣的:(android,android,layout)