IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android 自定义流式布局 -> 正文阅读

[移动开发]Android 自定义流式布局

先上效果

具体实现请参考如下:

import android.content.Context;
import android.os.Build;
import android.support.annotation.RequiresApi;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

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

public class WaterfallFlowLayout3 extends ViewGroup {
    // 存储每一行的View
    private List<List<View>> mEachLineView = new ArrayList<>();

    // 每一行的宽度,以每行最高View的宽度为每行的宽度
    private List<Integer> mEachLineHeight = new ArrayList<>();

    public WaterfallFlowLayout3(Context context) {
        super(context);
    }

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

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

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public WaterfallFlowLayout3(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }

    @Override
    protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
        // onLayout其实就是获取左、上、右、下四个值,然后设置此4个值
        int left;
        int top;
        int right;
        int bottom;

        // 当前行的左和上位置
        int curLeft = 0;
        int curTop = 0;

        // 一行一行的遍历
        for (int index = 0; index < mEachLineView.size(); index++) {
            List<View> listView = mEachLineView.get(index);
            if (listView == null || listView.isEmpty()) {
                continue;
            }

            // 遍历一行的每一个View
            for (View view : listView) {
                if (view == null) {
                    continue;
                }

                // 获取间距布局参数
                MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
                if (marginLayoutParams == null) {
                    continue;
                }

                // 获取当前View的四个点的坐标值
                left = curLeft + marginLayoutParams.leftMargin;
                top = curTop + marginLayoutParams.topMargin;
                right = left + view.getMeasuredWidth();
                bottom = top + view.getMeasuredHeight();

                // 最终就是调用这个方法进行摆放
                view.layout(left, top, right, bottom);

                // 将当前的左坐标位置往后移动,用于下一个view的计算
                curLeft += view.getMeasuredWidth() + marginLayoutParams.leftMargin + marginLayoutParams.rightMargin;
            }

            // 新的一行要从左边开始
            curLeft = 0;

            // 换行要将顶部的坐标往下移动
            curTop += mEachLineHeight.get(index);
        }

        mEachLineHeight.clear();
        mEachLineView.clear();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mEachLineView.clear();
        mEachLineHeight.clear();

        // 1.获取父容器的参数
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        // 2.onMeasure的目的也就是获取本View的宽和高的值,此处先定义变量
        int measureWidth = 0;
        int measureHeight = 0;

        // 3.根据父容器的模式来获设置本View的宽和高,也就是2中的数值
        // 3.1 宽和高都是match_parent
        if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
            // 如果都是match_parent那就不需要计算,直接设置为父容器的宽和高
            measureWidth = widthSize;
            measureHeight = heightSize;
        } else {
            // 用来统计当前行的宽和高
            int curLineWidth = 0;
            int curLineMaxHeight = 0;

            // 当前子view的宽度和高度
            int curChildWidth = 0;
            int curChildHeight = 0;

            // 每行摆放的子view的集合
            List<View> curLineViews = new ArrayList<>();

            // 获取该容器中所有的子View进行遍历计算
            int childCount = getChildCount();
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                if (childView == null) {
                    continue;
                }

                // 首先测量子View
                measureChild(childView, widthMeasureSpec, heightMeasureSpec);

                // 获取子view的宽高
                MarginLayoutParams marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();

                // 计算当前子view的宽和高
                curChildWidth = marginLayoutParams.leftMargin + childView.getMeasuredWidth() + marginLayoutParams.rightMargin;
                curChildHeight = marginLayoutParams.topMargin + childView.getMeasuredHeight() + marginLayoutParams.bottomMargin;

                // 判断如果当前行的宽度和已经大于父容器的宽度,那么就需要换行
                if (curChildWidth + curLineWidth > widthSize) {
                    // 需要换行,先将换行前最后一行参数(也就是当前行)
                    // 总的View的宽度和高度赋值后(这也是我们测量获取的两个目标值),然后保存当前行的信息
                    measureWidth = Math.max(measureWidth, curLineWidth);
                    measureHeight += curLineMaxHeight;
                    mEachLineView.add(curLineViews);
                    mEachLineHeight.add(curLineMaxHeight);

                    // 创建新的一行
                    curLineViews = new ArrayList<>();

                    // 将当前的子view放到新一行的开头
                    curLineViews.add(childView);

                    // 新的一行目前只摆放了一个view所以当前行的宽度和高度都是当前子view的宽和高
                    curLineWidth = curChildWidth;
                    curLineMaxHeight = curChildHeight;
                } else {
                    // 不需要换行
                    curLineViews.add(childView);
                    curLineWidth += curChildWidth;
                    curLineMaxHeight = Math.max(curLineMaxHeight, curChildHeight);
                }

                // 最后一个处在最后一行上,因最后一行因为不需要换行,所以最后一行的view数列没有被添加到数列中,此处需要单独处理
                if (i == childCount - 1) {
                    // 最后一行同样也要参与到想要获取的目标值之中
                    measureWidth = Math.max(measureWidth, curLineWidth);
                    measureHeight += curLineMaxHeight;

                    mEachLineView.add(curLineViews);
                    mEachLineHeight.add(curChildHeight);
                }
            }
        }


        // 最终目的就是在此处设置已经计算好的本容器的宽和高
        setMeasuredDimension(measureWidth, measureHeight);
    }
}

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-03-30 18:36:39  更:2022-03-30 18:40:28 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 19:31:38-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码