Android 自定义 View 浅析

概括

说到自定义 View ,就一定得说说 android 系统的UI绘制流程。再说这个流程之前,我们先看一下在每一个 activity 页面中我们的布局 ui 所处的位置。

从上图就可以知道,我们平时使用的 setContentView() 这个方法就是用来设置 contentview 的。了解了,这个之后,我们还应该了解一下 android 中 view 的继承关系。

从上面的一张图中,我们可以看出 android 的 UI 控件主要有两种:view 和 viewgroup。那么像我们经常使用的 Button,TextView,ImageView 都属于 view 的范畴!FrameLayout,LinearLayout等都属于 viewgroup 的范畴!了解了这些基本知识之后,我们再来说说如果自定义控件!

如何自定义

我们要自定义控件,无非就是继承 view 或者 viewgroup 亦或者继承已有的控件在上面在进行扩展!在这里继承 view 和 继承 viewgroup 有点区别!我们先来说说相同点吧!

相同点

1. 初始化

这个其实就是构造函数啦,在这里你可以为这个 view 设置特定的属性啊!那么如何自定义属性呢?首先你得在 res -->

values 这个目录下新建 attrs 的资源文件!在这个文件中配置你要的自定义属性!先看一下代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="WeekSelectView">
<attr name="selectColor" format="color"></attr>
<attr name="unSelectColor" format="color"></attr>
<attr name="lineColor" format="color"></attr>
<attr name="textColor" format="color"></attr>
<attr name="textSize" format="dimension"></attr>
<attr name="selectSize" format="dimension"></attr>
<attr name="lineWidth" format="dimension"></attr>
<attr name="lineHeight" format="dimension"></attr>
<attr name="space" format="dimension"></attr>
</declare-styleable>
</resources>

其中的 declare-styleable 标签就是添加自定义属性用的,里面包含的每一个 attr 就代表每一个自定义属性!后天面的 format 属性代表每一个属性的类型!接着就是我该如何使用它们呢!我们只要在布局文件中使用代码就行了:

<com.kidbot.library.widget.weekselect.WeekSelectView
xmlns:weekselect="http://schemas.android.com/apk/res-auto"
android:id="@+id/weekselect"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
weekselect:lineHeight="10dp"
weekselect:lineWidth="50dp"
weekselect:selectSize="10dp"
weekselect:space="30dp"
weekselect:textSize="15sp" />

要注意的是如果要在布局文件中使用这些自定义属性得加这句话: xmlns:weekselect="http://schemas.android.com/apk/res-auto" 其中 weekselect 这个字段属于用户自定义!

好了知道如何使用了,那么在写自定义 view 的时候,我们该怎么获取这些这些值呢?

    TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.WeekSelectView);
selectColor=typedArray.getColor(R.styleable.WeekSelectView_selectColor, Defalut_Select_Color);
unSelectColor=typedArray.getColor(R.styleable.WeekSelectView_unSelectColor, Defalut_UnSelect_Color);
lineColor=typedArray.getColor(R.styleable.WeekSelectView_lineColor, Defalut_Line_Color);
textSize = typedArray.getDimension(R.styleable.WeekSelectView_textSize, Defalut_Text_Size);
textColor = typedArray.getColor(R.styleable.WeekSelectView_textColor, Defalut_Text_Color);
selectSize = typedArray.getDimension(R.styleable.WeekSelectView_selectSize, Defalut_Select_Size);
lineHeight = typedArray.getDimension(R.styleable.WeekSelectView_lineHeight, Defalut_Line_Height);
lineWidth=typedArray.getDimension(R.styleable.WeekSelectView_lineWidth, Defalut_Line_Width);
space=typedArray.getDimension(R.styleable.WeekSelectView_space,Defalut_Space);
typedArray.recycle();

代码比较简单,我就不详细说了,唯一要注意的就是不要忘了释放这个资源

2. 计算大小

好了,当我们获取到了 view 的一些初始化值之后呢,我们得计算我们的 view 的大小了!那怎么计算 view 的大小呢?只要覆写 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 这个方法就行了。但在覆写这个方法之前,我们先得了解几个方法

MeasureSpec.getSize(int value) 这个方法是用来获取对应的宽高的!

MeasureSpec.getMode(int value) 这个方法是用来获取对应的宽高的模式的!这了模式有这个三个:

  • MeasureSpec.EXACTLY

    精确值模式,当layout_width或layout_height指定为具体数值,或者为match_parent时,系统使用EXACTLY

  • MeasureSpec.AT_MOST

    最大值模式,指定为wrap_content时,控件的尺寸不能超过父控件允许的最大尺寸

  • MeasureSpec.UNSPECIFIED

    不指定测量模式,View想多大就多大,一般不太使用

这个给出常用的覆写这个方法的代码:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize=MeasureSpec.getSize(widthMeasureSpec);
int widthMode=MeasureSpec.getMode(widthMeasureSpec);
int heightSize=MeasureSpec.getSize(heightMeasureSpec);
int heightMode=MeasureSpec.getMode(heightMeasureSpec);
if (widthMode==MeasureSpec.EXACTLY){
width=widthSize;
lineWidth=(width-itemlength*selectSize-getPaddingRight()-getPaddingLeft()- textRect.width())/(itemlength-1);
}else{
width= (int) (selectSize*(itemlength)+lineWidth*(itemlength-1)+textRect.width());
} if (heightMode==MeasureSpec.EXACTLY){
height=heightSize;
}else{
height= (int) (selectSize+space+textRect.height()+paddingTop);
}
setMeasuredDimension(width, height);
}

一般而言,当用户设置了具体的宽高的时候,我们使用设置的,如果没有,我们自己计算对应的宽高,最后设置!当完成这一步之后,我么就可以使用 getMeasuredWidth(),getMeasuredHeight() 这两个方法来获取 view 对应的高度了!

3. 绘制

好了,到了这一步,我们只要覆写 onDraw(Canvas canvas) 这个方法就行了,至于要绘制什么,这个是由开发者自己决定的。这里大家住一个问题,就是不要在方法中创建类,要不然会非常的耗资源!!如果大家想绘制出非常炫酷的图形或者效果来,那就得去学学 Canvas 这个类了!!这里给个链接 学习 Canvas

不同点

这里我要说的不同点就是如果继承了 viewgroup 之后,要多覆写一个方法 onLayout() 这个方法就是用来设置子view的位置的!

现在只有你覆写了这个方法之后 ,getHeight()getWidth() 才能获取到对应的值!现在可能有读者要问了,这两个方法和上面的 getMeasuredWidth(),getMeasuredHeight() 这两个方法有什么区别?却别就是

  • getHeight : 就是当前view中的可视高度
  • getMeasuredHeight:就是view的内容高度,包括不可见的地方

其他要到说的

自定义view 还有一个比较重要的问题,就是触摸事件,就是 touch 事件 在这个要涉及的问题就是 android 中触摸事件的传递机制了,这个也给个链接 android 触摸事件传递机制学习

如果触摸相关的已经学习好了,就可以在学习一下 android 动画的相关知识了,毕竟动态的事物比静态的要吸引人啊!这个在给出 android 动画学习

最后

希望大家都能学会 android 自定义 view 这个看似高大上的技能,因为这个在 android 开发中太常用了!!!!

Android 自定义 View 浅析的更多相关文章

  1. 【Android - 自定义View】之自定义View浅析

    1.概述 Android自定义View / ViewGroup的步骤大致如下: 1) 自定义属性: 2) 选择和设置构造方法: 3) 重写onMeasure()方法: 4) 重写onDraw()方法: ...

  2. Android自定义View 画弧形,文字,并增加动画效果

    一个简单的Android自定义View的demo,画弧形,文字,开启一个多线程更新ui界面,在子线程更新ui是不允许的,但是View提供了方法,让我们来了解下吧. 1.封装一个抽象的View类   B ...

  3. (转)[原] Android 自定义View 密码框 例子

    遵从准则 暴露您view中所有影响可见外观的属性或者行为. 通过XML添加和设置样式 通过元素的属性来控制其外观和行为,支持和重要事件交流的事件监听器 详细步骤见:Android 自定义View步骤 ...

  4. Android 自定义View合集

    自定义控件学习 https://github.com/GcsSloop/AndroidNote/tree/master/CustomView 小良自定义控件合集 https://github.com/ ...

  5. Android 自定义View (五)——实践

    前言: 前面已经介绍了<Android 自定义 view(四)-- onMeasure 方法理解>,那么这次我们就来小实践下吧 任务: 公司现有两个任务需要我完成 (1)监测液化天然气液压 ...

  6. Android 自定义 view(四)—— onMeasure 方法理解

    前言: 前面我们已经学过<Android 自定义 view(三)-- onDraw 方法理解>,那么接下我们还需要继续去理解自定义view里面的onMeasure 方法 推荐文章: htt ...

  7. Android 自定义 view(三)—— onDraw 方法理解

    前言: 上一篇已经介绍了用自己定义的属性怎么简单定义一个view<Android 自定义view(二) -- attr 使用>,那么接下来我们继续深究自定义view,下一步将要去简单理解自 ...

  8. Android 自定义view(二) —— attr 使用

    前言: attr 在前一篇文章<Android 自定义view -- attr理解>已经简单的进行了介绍和创建,那么这篇文章就来一步步说说attr的简单使用吧 自定义view简单实现步骤 ...

  9. Android 自定义View

    Android 自定义View流程中的几个方法解析: onFinishInflate():从布局文件.xml加载完组件后回调 onMeasure() :调用该方法负责测量组件大小 onSizeChan ...

随机推荐

  1. django学习2 视图和模板

    1 编写更多的视图 polls/views.py def detail(request, question_id): return HttpResponse("You're looking ...

  2. Android多任务切换与Activity启动模式SingleTask之间关系的分析

    这里会以多个场景列子进行分析,在分析之前先了解一下基本的概念. Task任务:一系列Activity的集合,这些Activity以栈的形式进行排列(后进先出). 那在什么时候系统会新建一个Task任务 ...

  3. js 四级联动

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head ...

  4. LigerUI编辑表格组件单元格校验问题

    这几天在使用LigerUI(版本为1.2.2)编辑表格组件的时候,遇到几个小问题,从官方demo和api中没有找到解决的办法 问题1.从数据库查询出来的主键单元格不可编辑问题 主键单元格已经保存之前编 ...

  5. Java虚拟机性能管理神器 - VisualVM(3) 插件安装与更新路径配置

    Java虚拟机性能管理神器 - VisualVM(3)  插件安装与更新路径配置 插件路径地址配置方法: VisualVM打开后,会发现功能比较单一,只有概述.监视.线程.抽样器.Profiler五个 ...

  6. 【苏勇老师Linux 入门笔记】网络基础

    IP 地址 IP 编制时一个双层编制方案,一个 IP 地址标示一个主机 (或一个网卡接口). 一个 IP 地址分为两个部分:网络部分(所属区域)和主机部分(标示区域中的哪个主机).IPv4 共32位, ...

  7. C# 指定ip段生成ip地址

    private void button1_Click(object sender, EventArgs e) { string StartIp = ""; string EndIp ...

  8. thinkphp中的类库与引用import引入机制

    ThinkPHP的类库包括基类库和应用类库 控制器类 模块名+Action 例如 UserAction.InfoAction 模型类 模型名+Model 例如 UserModel.InfoModel ...

  9. Weblogic在Linux下启动特别慢及进入控制台慢的解决方法

    实际是JVM在Linux下的bug 他想调用一个随机函数 但取不到 暂时的解决办法是 1)较好的解决办法: 在Weblogic启动参数里添加 “- Djava.security.egd=file:/d ...

  10. JS性能细节学习初步总结

    1,声明变量要赋初值2,尽量避免声明全局变量,可以减少与系统的重名3,当编写大量js代码时,难免会遇到命名冲突,这是可以通过模拟命名空间方式     来避免冲突4,尽量避免使用全局变量,搜索全局变量是 ...