Android 自定义ViewGroup 流式布局

自定义View的基本方法

自定义View的最基本的三个方法分别是: onMeasure()、onLayout()、onDraw();
View在Activity中显示出来,要经历测量、布局和绘制三个步骤,分别对应三个动作:measure、layout和draw。

  • 测量:onMeasure()决定View的大小;
  • 布局:onLayout()决定View在ViewGroup中的位置;
  • 绘制:onDraw()决定绘制这个View。

自定义控件分类

  • 自定义View: 只需要重写onMeasure()和onDraw()
  • 自定义ViewGroup: 则只需要重写onMeasure()和onLayout()

自定义View基础

View的分类

视图View主要分为两类

类别 解释 特点
单一视图 即一个View,如TextView 不包含子View
视图组 即多个View组成的ViewGroup,如LinearLayout 包含子View

View类简介

  • View类是Android中各种组件的基类,如View是ViewGroup基类
  • View表现为显示在屏幕上的各种视图

Android中的UI组件都由View、ViewGroup组成。

  • View的构造函数:共有4个
// 如果View是在Java代码里面new的,则调用第一个构造函数
 public CarsonView(Context context) {
        super(context);
    }

// 如果View是在.xml里声明的,则调用第二个构造函数
// 自定义属性是从AttributeSet参数传进来的
    public  CarsonView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

// 不会自动调用
// 一般是在第二个构造函数里主动调用
// 如View有style属性时
    public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    //API21之后才使用
    // 不会自动调用
    // 一般是在第二个构造函数里主动调用
    // 如View有style属性时
    public  CarsonView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

AttributeSet与自定义属性

系统自带的View可以在xml中配置属性,对于写的好的自定义View同样可以在xml中配置属性,为了使自定义的View的属性可以在xml中配置,需要以下4个步骤:

  1. 通过为自定义View添加属性
  2. 在xml中为相应的属性声明属性值
  3. 在运行时(一般为构造函数)获取属性值
  4. 将获取到的属性值应用到View

代码如下

package com.zthx.deviceui.view;

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

import com.zthx.deviceui.R;

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

/**
 * 类名称: FlowLayout.java

* 类描述: 自定义流式ViewGroup

* * * @author darryrzhong * @since 2019/12/11 9:37 */ public class FlowLayout extends ViewGroup { /** * 每一行的子view * */ private List lineView; /** * 整个布局的所有行 * */ private List> lines; /** * 每一行的高度 * */ private List heights; 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); } private void init(){ lineView = new ArrayList<>(); lines = new ArrayList<>(); heights = new ArrayList<>(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //测量自身 super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取限制信息的 mode和 size int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //记录当前行的宽度个高度 //宽度是当前行子view的宽度之和 int lineWidth = 0; //高度是当前行所有子view中高度的最大值 int lineHeight = 0; //整个流式布局的宽度和高度 //所有行中宽度的最大值 int flowLayoutWidth = 0; //所有行的高度叠加 int flowLayoutHeight = 0; //初始化参数列表 init(); //遍历所有的子view,对子view进行measure,分配到具体的行 int countView = getChildCount(); for (int i = 0;i widthSize){ //换行 lines.add(lineView); lineView = new ArrayList<>(); flowLayoutWidth = Math.max(flowLayoutWidth,lineWidth); flowLayoutHeight +=lineHeight; heights.add(lineHeight); lineHeight = 0; lineWidth = 0; } lineView.add(child); lineWidth +=childWidth; lineHeight = Math.max(lineHeight,childHeight); } //FlowLayout最终宽高 setMeasuredDimension(widthMode == MeasureSpec.EXACTLY? widthSize:flowLayoutWidth, heightMode == MeasureSpec.EXACTLY?heightSize:flowLayoutHeight); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { int lineCount = lines.size(); int currX = 0; int currY = 0; //所有的子view,一行一行的布局 for (int i = 0;i lineViews = lines.get(i); //取出这一行的高度值 int lineHeight = heights.get(i); int lineSize = lineViews.size(); //遍历当前行的子View for (int j=0;j

效果如下

欢迎关注作者darryrzhong,更多干货等你来拿哟.

请赏个小红心!因为你的鼓励是我写作的最大动力!

更多精彩文章请关注

  • 个人博客:darryrzhong
  • 掘金
  • SegmentFault
  • 慕课网手记

你可能感兴趣的:(Android 自定义ViewGroup 流式布局)