android 怎么自定义view

news2025/1/12 23:26:39

首先了解view的绘制流程:

所以onmeasure ---测量view  

onlayout---确定view大小----》所以继承ViewGroup必须要重写onlayout,确定子view

而onDraw----是继承view时候需要操作的。

所以:自定义ViewGroup一般是利用现有的组件根据特定的布局方式来组成新的组件。

              自定义View,一般是没有现成的view

序列化,大概有这个意思,不一定对。
自定义序列化: IOT
协议比如:物联网:蓝牙:传递的数据 串口 协议:

onmeasure的测量 是先从子布局开始还是先从父布局开始的?

----根据算法来控制的,比如view pageer就是父布局开始

MeasureSpec是什么

public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;

        /** @hide */
        @IntDef({UNSPECIFIED, EXACTLY, AT_MOST})
        @Retention(RetentionPolicy.SOURCE)
        public @interface MeasureSpecMode {}

----是view里面的一个类---我们知道int 是32位

------上面代码里的30,就是高两位是00,后面30位---》所以这组成里MeasureSpec

-------高两位表示UNSPECIFIED,EXACTLY,AT_MOST

关于getChildMeasureSpec(int spec, int padding, int childDimension)算法

第一个参数,父亲给的,

第二个参数,父亲的

第三个参数,孩子需要的

-----》根据UNSPECIFIED,EXACTLY,AT_MOST来计算

 public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = MeasureSpec.getMode(spec);
        int specSize = MeasureSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
        // Parent has imposed an exact size on us
        case MeasureSpec.EXACTLY:
            if (childDimension >= 0) {
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size. So be it.
                resultSize = size;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent has imposed a maximum size on us
        case MeasureSpec.AT_MOST:
            if (childDimension >= 0) {
                // Child wants a specific size... so be it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size, but our size is not fixed.
                // Constrain child to not be bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size. It can't be
                // bigger than us.
                resultSize = size;
                resultMode = MeasureSpec.AT_MOST;
            }
            break;

        // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let them have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
        }
        //noinspection ResourceType
        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
    }

下面是一个流式布局的例子:

 * desc   : 官方FlexboxLayout 流式布局
 */
class FlowLayout(context: Context) : ViewGroup(context) {
    private val mHorizontalSpacing = dp2px(16f) //每个item横向间距

    private val mVerticalSpacing = dp2px(8f) //每个item竖向间距

    private  var allLines:MutableList<MutableList<View>> = ArrayList<MutableList<View>>() //记录所有的行,一行一行保存,用于layout
    var lineHeights:ArrayList<Int> = ArrayList()//记录每一行的行高,用于layout


    constructor(context: Context,attrs:AttributeSet):this(context){
        // 在这里处理从 XML 布局 --序列化格式--建值对
    }
    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : this(context, attrs) {
        // 在这里处理从 XML 布局文件中传入的属性和样式
    }

    //初始化,因为是onMeasure递归的方式,所以要放在onMeasure
    private fun clearMeasureParams(){
//        allLines =ArrayList<MutableList<View>>()
//        lineHeights = ArrayList()
        allLines.clear()
        lineHeights.clear()
    }
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        clearMeasureParams()// 内存抖动
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        //先度量孩子
        val childCount =childCount
        //ViewGroup解析父控件给我的宽高
        val selfWidth = MeasureSpec.getSize(widthMeasureSpec)
        val selfHeight =MeasureSpec.getSize(heightMeasureSpec)

        //measure过程中 子view要求的父viewgroup的宽高
        var parentNeededWidth =0;
        var parentNeededHeight =0;

        val linView = ArrayList<View>()
        var lineWidthUsed =0 //记录这一行的以及使用了多宽的size
        var lineHeight =0 //一行的杭高
        for (i in 0 until childCount){
            val childview = getChildAt(i)
            val childlayoutParams = childview.layoutParams
            if (childview.visibility != GONE) {
                val childWidthMeasureSpec = getChildMeasureSpec(
                    widthMeasureSpec,
                    paddingLeft + paddingRight,
                    childlayoutParams.width
                )
                val childHeightMeasureSpec1 = getChildMeasureSpec(
                    heightMeasureSpec,
                    paddingTop + paddingBottom,
                    childlayoutParams.height
                )
                childview.measure(childWidthMeasureSpec, childHeightMeasureSpec1)

                //获取子view的度量高度
                val childMeasureWidth = childview.measuredWidth
                val childmeasuredHeight = childview.measuredHeight

                linView.add(childview)
                //是否药换行
                if (childMeasureWidth + lineWidthUsed + mHorizontalSpacing > selfWidth) {
                    //换行判断当前行所需要的宽和高,所以要记录下来
                    allLines.add(linView)
                    lineHeights.add(lineHeight) //行高。。这个if语句会缺少最后一行,

                    parentNeededHeight = parentNeededHeight + lineHeight + mVerticalSpacing
                    parentNeededWidth =
                        Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing)

                    linView.clear()
                    lineWidthUsed = 0
                    lineHeight = 0

                }
                //每行自己的宽高
                lineWidthUsed = lineWidthUsed + childMeasureWidth + mHorizontalSpacing
                lineHeight = Math.max(lineHeight, childmeasuredHeight)

                //最后一行,
                if (i == childCount - 1) {
                    allLines.add(linView)
                    lineHeights.add(lineHeight)

                    parentNeededHeight = parentNeededHeight + lineHeight + mVerticalSpacing
                    parentNeededWidth =
                        Math.max(parentNeededWidth, lineWidthUsed + mHorizontalSpacing)
                }
            }
        }
        //度量自己
        //根据子view的度量结果,来重新度量自己的viewGroup
        //作为一个viewgroup,他自己也是个view,他的大小也需要根据他的父亲提供的宽高来度量
        var withmode: Int = MeasureSpec.getMode(widthMeasureSpec)
        var heightmode: Int = MeasureSpec.getMode(heightMeasureSpec)

        //确切的值 EXACTLY
        val realWidth =if(withmode  ==MeasureSpec.EXACTLY) selfWidth else parentNeededWidth
        val realHeight =if(heightmode  ==MeasureSpec.EXACTLY) selfHeight else parentNeededWidth
        setMeasuredDimension(realWidth,realHeight)
    }
    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val lineCount =allLines.size
        var curl =paddingLeft //左边届
        var curT= paddingTop //上边界
        for(i in 0 until lineCount){
            val Lineviews = allLines.get(i)
            val lineHeight = lineHeights.get(i)
            for (j in 0 until Lineviews.size){//其中一行
                val view = Lineviews.get(j)//这一行的view
                //计算边界
                val left =curl //左边届
                val top= curT //上边界
                val right = left+measuredWidth//左边届
                val botton = top +measuredHeight  //上边界

                view.layout(left,top,right,botton)
                curl =right+mHorizontalSpacing//下一个左边
            }
            curT =curT +lineHeight+mVerticalSpacing //下一个top
            curl = paddingLeft //每一个新的行要重制左边届
        }

//        view.layout(left,top,right,bottom)
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1524036.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python电梯楼层数字识别

程序示例精选 Python电梯楼层数字识别 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《Python电梯楼层数字识别》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0c;易读。 学习与应…

操作系统内功篇:硬件结构之如何写出让CPU执行更快的代码?

一 前言 因为CPU要操作的数据都在CPU Cache中的话&#xff0c;就不用再从内存中读取数据了&#xff0c;这样就提高了效率&#xff0c;访问的数据在CPU Cache中越多&#xff0c;有个专业名词称为缓存命中率高&#xff0c;所以说&#xff0c;缓存命中率越高&#xff0c;自然执行…

Jupyter Notebook出错提示An error occurred while retrieving package information解决办法

出错日志信息&#xff1a; To access the notebook, open this file in a browser:file:///C:/Users/colda/AppData/Roaming/jupyter/runtime/nbserver-14564-open.htmlOr copy and paste one of these URLs:http://localhost:8888/?token3c0113e5da07c0b8b8c9de74ffb453c5047…

在idea中配置tomcat服务器,然后部署一个项日

1.下载tomcat Tomcat下载 点击右边的tomcat8 找到zip点击下载 下载完&#xff0c;解压到你想放置的路径下 2.配置环境变量 打开设置找到高级系统设置点击环境变量 点击新建&#xff0c;变量名输入&#xff1a;CATALINA_HOME&#xff0c;变量值就是Tomcat的安装路径&#x…

机器人阻抗控制中的transparency(透明度)

在机器人控制领域&#xff0c;transparency&#xff08;透明性&#xff09;是一个描述机器人在物理交互过程中如何响应外部力或运动的术语。透明性在这里通常意味着机器人的运动和行为对于与其交互的用户或环境来说是直观且可预测的。换句话说&#xff0c;透明性意味着机器人的…

【二分查找详细解析】 【图解】 例题【洛谷P2249 【深基13.例1】查找】

文章目录 二分查找的基础解释例题【洛谷P2249 【深基13.例1】查找】code↓ 二分查找的基础解释 二分的时间复杂度为 O ( l o g n ) O(log n) O(logn)&#xff0c;进行二分查找的序列必须满足单调性 我们可以先定义两个值 l , r l,r l,r &#xff0c;来表示查找到的左端点 l…

接口与多态

通过接口实现多态 接口中声明若干个 bstract方法&#xff1b; 方法体的内容细节由实现接口的类去完成&#xff0c;不同的类有 不同的实现方式 → 则接口变量在回调接口方法时具有多 种形态。 用接口进行程序设计的核心思想 使用接口回调技术&#xff1a;接口变量存放实现该接口…

DP最长上升子序列模型

目录 怪盗基德的滑翔翼代码实现 登山代码实现 合唱队形代码实现 友好城市问题分析代码实现 最大上升子序列和代码实现 *拦截导弹问题分析代码实现扩展 *导弹防御系统问题分析代码实现 *最长公共上升子序列问题分析代码实现 LIS 问题一般有三种解法 朴素版动态规划贪心二分树状…

测试用例要如何写

1、测试点与测试用例 测试点不等于测试用例&#xff0c;这是我们首先需要认识到的。 问题1&#xff1a;这些测试点在内容上有重复&#xff0c;存在冗余。 问题2&#xff1a;一些测试点的测试输入不明确&#xff0c;不知道测试时要测试哪些。 问题3&#xff1a;总是在搭相似…

树与二叉树(数据结构)

本篇博客讲解 树与二叉树&#xff0c;后续会继续讲解堆 —————————————————————— 1.树概念及结构 1.1树的概念 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看…

CMAKE_CUDA_ARCHITECTURES set to ‘native’多版本与版本号矛盾问题,报错

CMAKE_CUDA_ARCHITECTURES set to ‘native’多版本与版本号矛盾问题&#xff0c;报错 1. 报错提醒如下图2. 原因本地安装多个cuda版本导致native寻找到多个版本&#xff0c;导致报错3. 具体配置需要根据你的显卡型号来确认 1. 报错提醒如下图 2. 原因本地安装多个cuda版本导致…

Redis - String 字符串

前言 下表中包含本博客提到的所有命令 字符串类型是 Redis 最基础的数据类型&#xff0c;关于字符串需要特别注意&#xff1a; 1&#xff09;⾸先 Redis 中所有的键&#xff08;key&#xff09;的类型都是字符串类型&#xff0c;⽽且其他⼏种数据结构也都是在字符串类似基础上…

ffmpeg 滤镜实现不同采样率多音频混音

音频混音在音视频开发中是十分重要的一个环节,所谓音频混音就是将所有需要混音的数据相加得到混音数据,然后通过某个算法进行非法数据的处理;例如相加数值超过最大值,最小值等! 在实际的音频开发中,要实现混音的流程如下: 因此我们的编码实现就分为五部分:寻找…

中经评论:“人工智能+”不止是加法

以下文章来源&#xff1a;经济日报 今年&#xff0c;“人工智能”首次被写入《政府工作报告》&#xff0c;这个新提法为发展数字经济、推进数实融合指明了新路径。值得注意的是&#xff0c;“人工智能”不是简单相加&#xff0c;而是要通过新技术催生新质生产力&#xff0c;为经…

MyBatisPlus 之三:BaseMapper 详解和 CRUD 案例详解

BaseMapper详解 1. SQL 日志开启 为了更好更快学习 MyBatisPlus &#xff0c;需要配置 SQL 日志&#xff0c;这样方便我们能随时看到执行过程中使用的 SQL 语句&#xff0c;有助于理解执行原理及方便 SQL 错误调试 mybatis-plus.configuration.log-implorg.apache.ibatis.logg…

Python 多线程大批量处理文件小程序

说明 平时偶尔需要进行重复性的对文件进行重命名、格式转化等。假设以文件复制功能作为目标&#xff0c;设计一个小程序使用多线程对文件进行批量复制。&#xff08;其实以后主要目标是针对Realsense的raw文件进行批量的转化&#xff0c;并借助多线程加速&#xff09; 代码 i…

深度学习 精选笔记(12)卷积神经网络-理论基础2

学习参考&#xff1a; 动手学深度学习2.0Deep-Learning-with-TensorFlow-bookpytorchlightning ①如有冒犯、请联系侵删。 ②已写完的笔记文章会不定时一直修订修改(删、改、增)&#xff0c;以达到集多方教程的精华于一文的目的。 ③非常推荐上面&#xff08;学习参考&#x…

修改yolov9的模型打印不出来Gflops的解决办法

正在修改yolov9的模块&#xff0c;发现修改后的模型没有GFlops这个参数 解决办法&#xff1a; 找到utils/torch_utils.py这个文件&#xff0c;有一个model_info函数 然后将其中的stride改为固定的640就可以打印了。 stride max(int(model.stride.max()), 32) if hasattr(mo…

扩展学习|网络问政的价值增量与实现条件:基于数据资源挖掘的视角

文献来源&#xff1a;[1]顾丹丹傅广宛.网络问政的价值增量与实现条件:基于数据资源挖掘的视角[J].中国行政管理, 2021, 000(004):76-82.DOI:10.19735/j.issn.1006-0863.2021.04.11. 一、技术赋能网络问政的机制生成 &#xff08;一&#xff09;技术赋能网络问政的流程&#xf…

关于IP-Adapter的十几个模型,到底是干啥用的?

&#x1f3a0;背景介绍 IP-Adapter的一系列模型在stable diffusion的实际应用中&#xff0c;越来越被频繁的使用到&#xff0c;用于“换脸”或者“保证角色的一致性”&#xff0c;但是很多朋友在安装或者使用别人的工作流的时候&#xff0c;经常会遇到各种各样的问题&#xff…