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 UI -> 正文阅读

[移动开发]Android UI

1、为什么要自定义View?

Android系统内置View无法实现我们的需求;

出于性能考虑。

2、自定义viewgroup步骤

自定义ViewGroup: 只需重写onMeasure()和onLayout()

onMeasure:

1、确定自身的大小;2、确定子view的大小

onMeasure流程:

1、ViewGroup开始测量自己的尺寸

2、为每个子View计算测量的限制信息

3、把上一步确定的限制信息,传递给每一个子view,然后子view开始measure自己的尺寸

4、获取子view测量完成后的尺寸

5、ViewGroup根据自身的情况,计算自己的尺寸

6、保存自身的尺寸

onLayout 最终是根据top left right bottom确定位置的

onMeasure()方法中常用的方法
1、getChildCount():获取子View的数量;
2、getChildAt(i):获取第i个子控件;
3、subView.getLayoutParams().width/height:设置或获取子控件的宽或高;
4、measureChild(child, widthMeasureSpec, heightMeasureSpec):测量子View的宽高;
5、child.getMeasuredHeight/width():执行完measureChild()方法后就可以通过这种方式获取子view的宽高值;
6、getPaddingLeft/Right/Top/Bottom():获取控件的四周内边距;
7、setMeasuredDimension(width, height):重新设置控件的宽高。

onLayout

1、根据规则确定子view位置

onLayout流程:

// 1、遍历子View
// 2、确定自己的规则
// 3、子View的测量尺寸
// 4、left,top,right,bottom
// 5、child.layout
示例一:
package com.huawei.javauidemo.widget;

import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;

import com.google.android.material.bottomappbar.BottomAppBar;

/**
 * < 自定义ViewGroup> <功能详细描述>
 * 下一行比上一行向右偏移100
 * 
 * @author CYN
 * @version [版本号, 2022/1/21]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class CustomViewGroup extends ViewGroup {

    private static final int OFFSET = 100; // 表示缩进的尺寸

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

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

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

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 1、测量自身
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 2、为每个子View计算测量的限制信息
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        // 3、把上一步确定的限制信息,传递给每一个子view,然后子view开始measure自己的尺寸
        int childCount = getChildCount();
        for(int i = 0; i < childCount; i ++) {
            View child = getChildAt(i);
            ViewGroup.LayoutParams lp = child.getLayoutParams();
            int childWidthSpec = getChildMeasureSpec(widthMeasureSpec, 0, lp.width);
            int childHeightSpec = getChildMeasureSpec(heightMeasureSpec, 0, lp.height);
            child.measure(childWidthSpec, childHeightSpec);

            int width = 0;
            int height = 0;
            // 4、获取子view测量完成后的尺寸
            // 5、ViewGroup根据自身的情况,计算自己的尺寸
            switch(widthMode) {
                case MeasureSpec.EXACTLY:
                    width = widthSize;
                    break;
                case MeasureSpec.AT_MOST:
                case MeasureSpec.UNSPECIFIED:
                    for(int i = 0; i < childCount; i ++) {
                        View child = getChildAt(i);
                        int widthAddOffset = i * OFFSET + child.getMeasuredWidth();
                        width = Math.max(width, widthAddOffset); // 取最大的宽度
                    }
                    break;
                default:
                    break;
            }

            switch(heightMode) {
                case MeasureSpec.EXACTLY:
                    height = heightSize;
                    break;
                case MeasureSpec.AT_MOST:
                case MeasureSpec.UNSPECIFIED:
                    for(int i = 0; i < childCount; i ++) {
                        View child = getChildAt(i);
                        height += child.getMeasuredHeight();
                    }
                    break;
                default:
                    break;
            }
            // 6、保存自身的尺寸
            setMeasuredDimension(width, height);
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        // 1、遍历子View
        // 2、确定自己的规则
        // 3、子View的测量尺寸
        // 4、left,top,right,bottom
        // 5、child.layout
        int left = 0;
        int top = 0;
        int right = 0;
        int bottom = 0;

        int childCount = getChildCount();
        for(int i = 0; i < childCount; i ++) {
            View child = getChildAt(i);
            left = i * OFFSET;
            right = left + child.getMeasuredWidth();
            bottom = top + child.getMeasuredHeight();
            child.layout(left, top, right, bottom);
            top += child.getMeasuredHeight();// 不考虑padding margin gravity ...
        }
    }
}
示例:
package com.huawei.javauidemo.widget;

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

import com.huawei.javauidemo.R;

import java.util.ArrayList;

/**
 * < 自定义ViewGroup> <把子控件从左到右排放,如果一行放不下,那么自动换行>
 *
 * 自定义布局流程
 * 1、自定义属性:声明,设置,解析获取自定义值
 * 在 attr.xml 声明
 * 2、测量:在onMeasure MeasureSpec.AT_MOST/EXACTLY
 * 自身的宽高/child的宽高
 * 3、布局:在onLayout方法里面根据自己规则来确定children的位置
 * 4、绘制:onDraw
 * 5、处理LayoutParams
 * 6、触摸反馈:滑动事件
 *
 * @author CYN
 * @version [版本号, 2022/1/21]
 * @see [相关类/方法]
 * @since [产品/模块版本]
 */
public class CustomFlowLayout extends ViewGroup {

    private static final String TAG = "CustomFlowLayout";

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

    public CustomFlowLayout(Context context) {
        super(context, null);
    }

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

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

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

    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.CustomFlowLayout_Layout);
            try {
                gravity = a.getInt(R.styleable.CustomFlowLayout_android_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 +
                    "} ";
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        // 1、测量自身
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        // 2、为每个子View计算测量的限制信息
        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();

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

            LayoutParams lp = 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;
            lineHeight = Math.max(lineHeight, childHeight);

            if (i == childCount - 1) { // 最后一行
                flowlayoutHeight += lineHeight;
                flowlayoutWidth = Math.max(flowlayoutWidth, lineWidth);
                heights.add(lineHeight);
                views.add(lineViews);
            }
        }
        // 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 = 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);
                // 确定下一个view的left
                currX = right;
            }
            currY +=lineHeight;
            currX = 0;
        }
    }
}

3、自定义view步骤

自定义View: 只需重写onMeasure()和onDraw()

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-01-25 10:42:32  更:2022-01-25 10:44:10 
 
开发: 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 12:08:55-

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