android attrs获取_Android自定义View,敢说都知道吗?
发布日期:2021-06-24 17:36:32 浏览次数:2 分类:技术文章

本文共 4396 字,大约阅读时间需要 14 分钟。

f45eb8f0ba57a84df19239ecbcb28a24.gif

码个蛋(codeegg) 第 906 次推文

作者:ClericYi链接:https://juejin.im/post/5e54e2de5188254945386529

1d4d695b7ad2d4b6ed2d9f187692b141.png

前言

View,有很多的名称。不论是你熟知的布局,还是控件,他们全部都继承自View

292aa0739e5acb4a9f0ee2b8b7614c6e.png

文内部分图片转载自Carson_Ho大佬的文章

思维导图

195ff06254140dc1819b8855f67f58e3.png

工作流程

measure

其实通过layout中的第二张图我们已经知道了控件大小的计算了。

  • height = bottom - top
  • width = right - left
对于ViewGroup而言,就是对容器内子控件的遍历和计算了。

因为直接继承自View的控件使用wrap_cotentmatch_parent是显示出来的效果是相同的。需要我们使用MeasureSpec中的getMode()方法来对当前的模式进行区分和比较。

模式 状态
UNSPECIFIED 未指定模式,View想多大就多大,父容器不做限制,一般用于系统内部的测量
AT_MOST 最大模式,对应wrap_content,View的大小不大于SpecSize的值
EXACTLY 精确模式,对应match_parent,View的大小为SpecSize的值
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); //用于获取设定的模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); // 用于获取设定的长度 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); // 类似这样的判断,后面不过多复述 // 用于判断是不是wrap_content // 如果不进行处理,效果会是match_parent if(widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST){
setMeasuredDimension(20, 20);        }}

layout

在确定位置时,我们有一个非常需要主要的地方—— 坐标系。

Android系统的坐标系和平时画的坐标系并不相同。

9fb7c1f2f2db1ab34d47c72d684960b6.png

所以相对应的,我们的位置计算方法自然和我们原来的正好是相反的。

4bcdf449c481f3e9f47c05391a9b8b32.png

4个顶点的位置分别由4个值决定:

  • top:子View上边界到所在容器上边界的距离。

  • left:子View左边界到所在容器左边界的距离。

  • bottom:子View下边界到所在容器上边界的距离。

  • right:子View右边界到所在容器左边界的距离。

所有的计算都是相对于所在容器才能够开始的。

draw

一共有6个步骤:

  1. 如果需要,则绘制背景 --drawBackground(canvas);

  2. 保存当前canvas层 --saveCount = canvas.getSaveCount();

  3. 绘制View的内容 --if (!dirtyOpaque) onDraw(canvas);

  4. 绘制子View --dispatchDraw(canvas);

  5. 如果需要,则绘制View的褪色边缘,类似于阴影效果 --canvas.restoreToCount(saveCount);

  6. 绘制装饰,比如滚动条 --onDrawForeground(canvas);

关于开发者需要重写的方法一般是第三步绘制View的内容对应的onDraw()

private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);    @Override    protected void onDraw(Canvas canvas) {
super.onDraw(canvas); int width = getWidth(); int height = getHeight(); // 在画布上进行类似这样的操作 canvas.drawLine(0, height/2, width,height/2, paint); }

入门自定义View

在日常项目的布局文件中我们经常会使用到xmlns:app="http://schemas.android.com/apk/res-auto"这样标签,其实他就是用来引入我们自定义的标签使用的。

  1. res/values目录下创建attrs

declare-styleable>resources>
  1. DefaultView(Context context, @Nullable AttributeSet attrs)中获取。以下是整个完整代码。

/** * author: ClericYi * time: 2020-01-30 */public class DefaultView extends View {
private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private int mColor = Color.RED; public DefaultView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs); initAttrs(context, attrs); initDraw(); } private void initAttrs(Context context, @Nullable AttributeSet attrs) {
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DefaultView); // 从styleable中获取的名字是系统会生成的,一般是 类名_name 的形式 mColor = array.getColor(R.styleable.DefaultView_color, Color.GREEN); // 获取完资源后即使回收 array.recycle(); } private void initDraw() {
paint.setColor(mColor); paint.setStrokeWidth(3f); } @Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas); int width = getWidth(); int height = getHeight(); canvas.drawLine(0, height/2, width,height/2, paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if(widthMode == MeasureSpec.AT_MOST){
setMeasuredDimension(20, 20); } }}
 

基础的性能优化

首先的话我们先了解如何去知道一个View是否被过度绘制了?

其实在我们手机中的开发模式已经存在这个选项了。

开启前 开启后
61f5585d3bfa20e42e25070114a26143.png
c584926362dcf9fab5c6922a22528608.png

下方给出绘制的次数对应图

c573e25b81ec9ba47b087d4ecc64a91b.png

那如何做到性能优化呢?

在这个问题之前,需要了解什么是过度绘制,你可以理解为同一位置的控件不断的叠加而产生的无用数据,那我们就来说说集中解决方案吧。

方案1:减少嵌套层数。

使用线性布局 使用约束布局
87efcd4e3c4a846d55bc60b6e0c7ba87.png
2b70d4f0e949202f16523970d8f340cd.png

因为只是一个案例,想说的意思,如果多个LinearLayout嵌套实现的效果,如果能被一个ConstraintLayout直接实现,那么就用后者替代,因为不会这样在同一个区域重复出现。

方案2:去除默认的背景

e4a0fc32c096781c20503f57db3a5e50.png

这个解决方案其实针对的背景会被自动绘制的问题,如果我们把这个层次消去,从绘制角度老说也是一种提升了。正如图示一般直接减少了一层的绘制。

在代码中的具体表现,通过对style.xml中的Theme进行修改:

@nullitem>
 

总结

13a3e2f759a06433a89e67d3961442bf.png

相关文章:

  • 行情艰难,Android初中级面试题助你逆风翻盘,每题都有详细答案

  • 来了!他来了!支持MVVM的BRVAH来了!

  • 个人的成长是曲线,还是直线?

今日问题:

敢说都懂了嘛?

b39710f3031506627f431f744bd2e1c0.png

专属升级社区:《这件事情,我终于想明白了》 

84a5d9bab76e9d9b7e68ddf3e87d96b1.gif

转载地址:https://blog.csdn.net/weixin_34182361/article/details/112329143 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:rust办宏G什么意思_双频路由器是什么意思 2.4G和5G用哪个好 双频路由器使用攻略!...
下一篇:菜鸟驿站是什么快递_又是菜鸟驿站!再获快递新业态许可证

发表评论

最新留言

初次前来,多多关照!
[***.217.46.12]2024年04月03日 17时21分19秒