ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Android自定义View之实现流式布局

2021-06-04 20:31:39  阅读:173  来源: 互联网

标签:自定义 int lineViews LayoutParams child lineHeight Android View


Android自定义View之实现流式布局

  • 流式布局
    把子控件从左到右摆放,如果一行放不下,自动放到下一行
  • 自定义布局流程
    1. 自定义属性:声明,设置,解析获取自定义值 在attr.xml中声明
    2. 测量:在onMeasure 方法测量自身的宽高和child的宽高
    3. 布局:在onLayout方法里面根据自己的规则来确定children的位置
    4. 绘制:onDraw
    5. 处理layoutParams
    6. 触摸反馈:滑动事件
  • 代码实现
    属性定义
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FlowLayout">
        <attr name="android:gravity"/>
        <attr name="android:horizontalSpacing" format="dimension |reference"/>
    </declare-styleable>
    <declare-styleable name="FlowLayout_Layout">
        <attr name="android:layout_gravity"/>
    </declare-styleable>
</resources>
  • 布局文件
<?xml version="1.0" encoding="utf-8"?>
    <!--<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"-->
    <!--xmlns:app="http://schemas.android.com/apk/res-auto"-->
    <!--android:layout_width="match_parent"-->
    <!--android:layout_height="wrap_content"-->
    <!--xmlns:tools="http://schemas.android.com/tools">-->
<LinearLayout 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:orientation="vertical"
android:layout_height="match_parent">


<com.example.as.proj.myviewgroupdemo2.FlowLayout 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=".MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="55dp"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="90dp"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小麻小儿郎呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="85dp"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小麻小儿郎呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="45dp"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="75dp"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小麻小儿郎呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="60dp"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="85dp"
        android:text="小麻小儿郎呀" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小麻小儿郎呀" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="85dp"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小麻小儿郎呀" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="85dp"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小麻小儿郎呀" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="65dp"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小麻小儿郎呀" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="75dp"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="80dp"
        android:text="小麻小儿郎呀" />


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="300dp"
        android:text="生活不止眼前的苟且,还有诗和远方" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="小麻小儿郎呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello hi ..." />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="你是谁呀" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="65dp"
        android:text="人在他在,塔亡人亡"
        android:layout_gravity="bottom"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="生活不止眼前的苟且,还有诗和远方1" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="250dp"
        android:text="发电房" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="这是结束" />

</com.example.as.proj.myviewgroupdemo2.FlowLayout>
<!--</ScrollView>-->
</LinearLayout>

自定义View

package com.example.as.proj.myviewgroupdemo2;

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

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

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

    private List<View> lineViews; //每一行的子View
    private List<List<View>> views; //所有的行,一行一行的存储
    private List<Integer> heights; //每一行的高度


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

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

    public FlowLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    private void init(){
        views = new ArrayList<>();
        lineViews = new ArrayList<>();
        heights = new ArrayList<>();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        //记录当前行的宽和高
        int lineWidth = 0; //宽度是当前行子View的宽度之和
        int lineHeight = 0; //高度是当前行所有子View中高度的最大值

        //整个流式布局的宽度和高度
        int flowlayoutWidth = 0; //所有行中宽度的最大值
        int flowlayoutHeight = 0; //所有行高度的累加

        //初始化参数列表
        init();

        //遍历所有子View,对子View进行测量,分配到具体的行
        int childCount = this.getChildCount();
        for(int i = 0; i < childCount; i++){
            View child = this.getChildAt(i);
            //测量子View 获取当前子View的测量宽高
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            //获取到当前子View的测量的宽高
            int childWidth = child.getMeasuredWidth();
            int childHeight = child.getMeasuredHeight();

            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            //看下当前的行的剩余宽度是否可以容纳下一个子View
            //如果放不下,换行 保存当前行所有的子View,累加行高,当前的宽度 高度 置零
            if(lineWidth + childWidth > widthSize){
                //换行
                views.add(lineViews);
                lineViews = new ArrayList<>();// 创建新的一行
                flowlayoutWidth = Math.max(flowlayoutWidth, lineWidth);
                flowlayoutHeight += lineHeight;
                heights.add(lineHeight);
                lineWidth = 0;
                lineHeight = 0;
            }
            lineViews.add(child);
            lineWidth += childWidth;
            if(lp.height != LayoutParams.MATCH_PARENT){
                //暂时先不要处理layout_height=match_parent
                lineHeight = Math.max(lineHeight, childHeight);
            }
            Log.i(TAG, "onMeasure: " + lineHeight);

            if(i == childCount - 1){
                //最后一行
                flowlayoutHeight += lineHeight;
                flowlayoutWidth = Math.max(flowlayoutWidth, lineWidth);
                heights.add(lineHeight);
                views.add(lineViews);
            }
        }
        //重新测量一次layout_height = match_parent
        remeasureChild(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : flowlayoutWidth,
                heightMode == MeasureSpec.EXACTLY ? heightSize : flowlayoutHeight);
    }

    private void remeasureChild(int widthMeasureSpec, int heightMeasureSpec){
        int lineSize = views.size();
        for(int i = 0; i < lineSize; i++){
            int lineHeight = heights.get(i); //每一行行高
            Log.i(TAG, "remeasureChile: " + lineHeight);
            List<View> lineViews = views.get(i); //每一行的子View
            int size = lineViews.size();
            for(int j = 0; j < size; j++){
                View child = lineViews.get(j);
                LayoutParams lp = (LayoutParams) child.getLayoutParams();
                if(lp.height == LayoutParams.MATCH_PARENT){

                    int childWidthSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
                    int childHeightSpec = getChildMeasureSpec(heightMeasureSpec, 0, lineHeight);
                    child.measure(childWidthSpec, childHeightSpec);
                }
            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int lineCount = views.size();
        int currX = 0;
        int currY = 0;

        for (int i = 0; i < lineCount; i++){
            //大循环,所有子View一行一行的布局
            List<View> lineViews = views.get(i); //取出一行、
            int lineHeight = heights.get(i); //取出这一行的高度
            //遍历当前行的子View
            int size = lineViews.size();
            for(int j = 0; j < size; j++){
                //布局当前行的每一个View
                View child = lineViews.get(j);
                int left = currX;
                int top = currY;
                int right = left + child.getMeasuredWidth();
                int bottom = top + child.getMeasuredHeight();
                child.layout(left, top, right, bottom);
                currX += child.getMeasuredWidth();
            }
            currY += lineHeight;
            currX = 0;
        }
    }

    @Override
    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }


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


    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    }

    @Override
    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
        return super.checkLayoutParams(p) && p instanceof LayoutParams;
    }

    public static class LayoutParams extends MarginLayoutParams{
        public int gravity = -1;

        public LayoutParams(Context c, AttributeSet attrs){
            super(c, attrs);

            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FlowLayout_Layout);
            try{
                gravity = a.getInt(R.styleable.FlowLayout_Layout_android_layout_gravity, -1);
            }finally {
                a.recycle();
            }
        }


        public LayoutParams(int width, int height) {
            super(width, height);
        }

        public LayoutParams(ViewGroup.LayoutParams source) {
            super(source);
        }

        @Override
        public String toString() {
            return "LayoutParams{" +
                    "gravity=" + gravity +
                    ", bottomMargin=" + bottomMargin +
                    ", leftMargin=" + leftMargin +
                    ", rightMargin=" + rightMargin +
                    ", topMargin=" + topMargin +
                    ", height=" + height +
                    ", width=" + width +
                    "} ";
        }
    }
}

运行效果

在这里插入图片描述

标签:自定义,int,lineViews,LayoutParams,child,lineHeight,Android,View
来源: https://blog.csdn.net/fakerXuan/article/details/117536138

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有