热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

自定义view的基本流程

有时候我们需要一些特殊的效果或者功能,而系统控件无法满足我们的需求,这时候就需要自己定义一个控件。自定义view流程继承View要自定义View首先需要需要继承View或者其子类,

有时候我们需要一些特殊的效果或者功能,而系统控件无法满足我们的需求,这时候就需要自己定义一个控件。

自定义view流程

继承View

要自定义View首先需要需要继承View或者其子类,如果需要实现的效果比较复杂,通常需要继承View,有时候我们需要的是系统的控件再加上一些特殊的效果则可以继承View的子类(如TextView)

如果是要自己设计一种布局或者要组合其他控件,这时候就需要继承ViewGroup或者LinearLayout、FrameLayout等系统自带的布局

重写构造方法

自定义View至少需要重写两个构造方法

  • 在java代码中直接创建控件所用的构造方法

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

  • 在xml文件中定义View所用的构造函数

public CustomView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
}

自定义xml中的属性

首先需要新建res/values/custom_view_attrs.xml,并在里面声明如下







然后就可以在xml布局文件中声明了


xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
tools:cOntext="cn.lkllkllkl.customview.MainActivity">
android:layout_
android:layout_
app:custom_/>

接着我们就可以在自定义View的构造方法中将该属性的值取出使用了

public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mPaint = new Paint();
float customWidth = 0;
TypedArray typeArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
customWidth = typeArray.getDimension(R.styleable.CustomView_custom_width, 100);
// 需要记得回收
typeArray.recycle();
}

onMeasure方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec);

MeasureSpec

这里要明白onMeasure的两个参数首先需要明白MeasureSpec这个类。

MeasureSpec通过将SpecMode(模式)和SpecSize(具体尺寸)打包成一个int(即widthMeasureSpec、heightMeasureSpec),前2位表示SpecMode,后30位表示尺寸。

SpecMode有三类

  • UNSPECIFIED: 父容器对View的大小没有限制,一般我们自定义View用的较少

  • EXACTLY: 在xml文件中设置具体大小为多少dp,或者设置为match_parent则SpecMode为EXACTLY,此模式表示则直接使用SpecSize作为自定义View的尺寸

  • AT_MOST: 对应xml文件中的wrap_content,表示设置的尺寸大小不能超过SpecSize

通常我们需要view支持wrap_content的时候才需要重写onMeasure,如果不重写则wrap_content效果和match_parent一样。一般使用如下代码就够了

@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);
// dp转换成px, 事先定义的默认宽高单位为dp
int defaultWidth = dp2px(getContext(), DEFAULT_WIDTH);
int defaultHeight = dp2px(getContext(), DEFAULT_HEIGHT);
if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {
widthSize = Math.min(defaultWidth, widthSize);
heightSize = Math.min(defaultHeight, heightSize);
} else if (widthMode == MeasureSpec.AT_MOST) {
widthSize = Math.min(defaultWidth, widthSize);
} else if (heightMode == MeasureSpec.AT_MOST){
heightSize = Math.min(defaultHeight, heightSize);
}

setMeasuredDimension(widthSize, heightSize);
}

一般为了适配不同屏幕需要使用dp为单位,而setMeasuredDimension是使用px为单位的,所以上面的代码将我们设置的默认宽高转换成px,转换方法如下

public static int dp2px(Context context, float dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dp, context.getResources().getDisplayMetrics());
}

onLayout方法

一般自定义ViewGroup才需要重写该方法对子View进行排放

onDraw方法

通过onDraw方法我们可以将view绘制到屏幕上,如果要让view支持padding属性则需要在onDraw中做处理

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
/* mPaint为成员变量,画笔Paint的对象,
* 在构造函数中进行初始化,可以设置一些在canvas上绘制的通用属性
*/
mPaint.setColor(Color.RED);
int paddingLeft = getPaddingLeft();
int paddingRight = getPaddingRight();
int paddingTop = getPaddingTop();
int paddingBottom = getPaddingBottom();
//在View所占区域绘制一条对角线
canvas.drawLine(paddingLeft, paddingTop,
getWidth() - paddingRight, getHeight() - paddingBottom, mPaint);
}

注意事项

  • onMeasure、onLayout、onDraw三个方法可能会对此调用,特别是onDraw方法,所以最好不要在这些方法中创建对象避免频繁分配内存造成内存抖动,或者做一些耗时操作导致跳帧

  • View中有提供post系列方法,所以不需要在View中使用Handler

  • View中若有创建线程或者动画需要及时停止,View#onDetachedFromWindow就是一个很好的时机

  • View中若有滑动嵌套的情形,需要处理好滑动冲突

reference

  • 《Android开发艺术探索》

  • 自定义View,有这一篇就够了


推荐阅读
  • 在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板
    本文介绍了在Xamarin XAML语言中如何在页面级别构建ControlTemplate控件模板的方法和步骤,包括将ResourceDictionary添加到页面中以及在ResourceDictionary中实现模板的构建。通过本文的阅读,读者可以了解到在Xamarin XAML语言中构建控件模板的具体操作步骤和语法形式。 ... [详细]
  • 使用nodejs爬取b站番剧数据,计算最佳追番推荐
    本文介绍了如何使用nodejs爬取b站番剧数据,并通过计算得出最佳追番推荐。通过调用相关接口获取番剧数据和评分数据,以及使用相应的算法进行计算。该方法可以帮助用户找到适合自己的番剧进行观看。 ... [详细]
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • Spring源码解密之默认标签的解析方式分析
    本文分析了Spring源码解密中默认标签的解析方式。通过对命名空间的判断,区分默认命名空间和自定义命名空间,并采用不同的解析方式。其中,bean标签的解析最为复杂和重要。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 阿,里,云,物,联网,net,core,客户端,czgl,aliiotclient, ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 本文详细介绍了MySQL表分区的创建、增加和删除方法,包括查看分区数据量和全库数据量的方法。欢迎大家阅读并给予点评。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文讨论了Kotlin中扩展函数的一些惯用用法以及其合理性。作者认为在某些情况下,定义扩展函数没有意义,但官方的编码约定支持这种方式。文章还介绍了在类之外定义扩展函数的具体用法,并讨论了避免使用扩展函数的边缘情况。作者提出了对于扩展函数的合理性的质疑,并给出了自己的反驳。最后,文章强调了在编写Kotlin代码时可以自由地使用扩展函数的重要性。 ... [详细]
author-avatar
朵朵妞er
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有