《转载-两篇很好的文章整合》Android中自定义控件
两篇很好的文章,有相互借鉴的地方,整合到一起收藏
分别转载自:http://blog.csdn.net/xu_fu/article/details/7829721
http://www.cnblogs.com/0616--ataozhijia/p/4003380.html
Android系统的视图结构的设计也采用了组合模式,即View作为所有图形的基类,Viewgroup对View继承扩展为视图容器类,由此就得到了视图部分的基本结构--树形结构
View定义了绘图的基本操作
基本操作由三个函数完成:measure()、layout()、draw(),其内部又分别包含了onMeasure()、onLayout()、onDraw()三个子方法。具体操作如下:
1、measure操作
measure操作主要用于计算视图的大小,即视图的宽度和长度。在view中定义为final类型,要求子类不能修改。measure()函数中又会调用下面的函数:
(1)onMeasure(),视图大小的将在这里最终确定,也就是说measure只是对onMeasure的一个包装,子类可以覆写onMeasure()方法实现自己的计算视图大小的方式,并通过setMeasuredDimension(width, height)保存计算结果。
2、layout操作
layout操作用于设置视图在屏幕中显示的位置。在view中定义为final类型,要求子类不能修改。layout()函数中有两个基本操作:
(1)setFrame(l,t,r,b),l,t,r,b即子视图在父视图中的具体位置,该函数用于将这些参数保存起来;
(2)onLayout(),在View中这个函数什么都不会做,提供该函数主要是为viewGroup类型布局子视图用的;
3、draw操作
draw操作利用前两部得到的参数,将视图显示在屏幕上,到这里也就完成了整个的视图绘制工作。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
(1)绘制背景;
(2)如果要视图显示渐变框,这里会做一些准备工作;
(3)绘制视图本身,即调用onDraw()函数。在view中onDraw()是个空函数,也就是说具体的视图都要覆写该函数来实现自己的显示(比如TextView在这里实现了绘制文字的过程)。而对于ViewGroup则不需要实现该函数,因为作为容器是“没有内容“的,其包含了多个子view,而子View已经实现了自己的绘制方法,因此只需要告诉子view绘制自己就可以了,也就是下面的dispatchDraw()方法;
(4)绘制子视图,即dispatchDraw()函数。在view中这是个空函数,具体的视图不需要实现该方法,它是专门为容器类准备的,也就是容器类必须实现该方法;
(5)如果需要(应用程序调用了setVerticalFadingEdge或者setHorizontalFadingEdge),开始绘制渐变框;
(6)绘制滚动条;
从上面可以看出自定义View需要最少覆写onMeasure()和onDraw()两个方法。
ViewGroup中的扩展操作:
首先Viewgroup是一个抽象类。
1、对子视图的measure过程
(1)measureChildren(),内部使用一个for循环对子视图进行遍历,分别调用子视图的measure()方法;
(2)measureChild(),为指定的子视图measure,会被 measureChildren调用;
(3)measureChildWithMargins(),为指定子视图考虑了margin和padding的measure;
以上三个方法是ViewGroup提供的3个对子view进行测量的参考方法,设计者需要在实际中首先覆写onMeasure(),之后再对子view进行遍历measure,这时候就可以使用以上三个方法,当然也可以自定义方法进行遍历。
2、对子视图的layout过程
在ViewGroup中onLayout()被定义为abstract类型,也就是具体的容器必须实现此方法来安排子视图的布局位置,实现中主要考虑的是视图的大小及视图间的相对位置关系,如gravity、layout_gravity。
3、对子视图的draw过程
(1)dispatchDraw(),该方法用于对子视图进行遍历然后分别让子视图分别draw,方法内部会首先处理布局动画(也就是说布局动画是在这里处理的),如果有布局动画则会为每个子视图产生一个绘制时间,之后再有一个for循环对子视图进行遍历,来调用子视图的draw方法(实际为下边的drawChild());
(2)drawChild(),该方法用于具体调用子视图的draw方法,内部首先会处理视图动画(也就是说视图动画是在这里处理的),之后调用子视图的draw()。
从上面分析可以看出自定义viewGroup的时候需要最少覆写onMeasure()和onLayout()方法,其中onMeasure方法中可以直接调用measureChildren等已有的方法,而onLayout方法就需要设计者进行完整的定义;一般不需要覆写以dispatchDraw()和drawChild()这两个方法,因为上面两个方法已经完成了基本的事情。但是可以通过覆写在该基础之上做一些特殊的效果,比如
- @Override
- protected void dispatchDraw(Canvas canvas) {
- // TODO Auto-generated method stub
- //
- //可以在这里先做一些处理,包括对传入的canvas
- //
- super.dispatchDraw(canvas); //这里会调用drawChild绘制子视图
- //
- //所有子视图都绘制完成后这里还可以做一些处理,比如绘制阴影什么的
- //
- }
其他
从以上分析可以看出View树的绘制是一个递归的过程,从ViewGroup一直向下遍历,直到所有的子view都完成绘制,那这一切的源头在什么地方(是谁最发起measure、layout和draw的)?当然就是在View树的源头了——ViewRoot!,ViewRoot中包含了窗口的总容器DecorView,ViewRoot中的performTraversal()方法会依次调用decorView的measure、layout、draw方法,从而完成view树的绘制。
invalidate()方法
invalidate()方法会导致View树的重新绘制,而且view中的状态标志mPrivateFlags中有一个关于当前视图是否需要重绘的标志位DRAWN,也就是说只有标志位DRAWN置位的视图才需要进行重绘。当视图调用invalidate()方法时,首先会将当前视图的DRAWN标志置位,之后有一个循环调用parent.invalidateChildinParent(),这样会导致从当前视图依次向上遍历直到根视图ViewRoot,这个过程会将需要重绘的视图标记DRAWN置位,之后ViewRoot调用performTraversals()方法,完成视图的绘制过程。
《转载!http://blog.csdn.net/xu_fu/article/details/7829721》
参考书籍《Android内核剖析》——柯元旦
public class MyView extends View { private String mtext;
private int msrc; public MyView(Context context) {
super(context);
} public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
int resourceId = 0;
int textId = attrs.getAttributeResourceValue(null, "Text",0);
int srcId = attrs.getAttributeResourceValue(null, "Src", 0);
mtext = context.getResources().getText(textId).toString();
msrc = srcId;
} @Override
protected void onDraw(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.RED);
InputStream is = getResources().openRawResource(msrc);
Bitmap mBitmap = BitmapFactory.decodeStream(is);
int bh = mBitmap.getHeight();
int bw = mBitmap.getWidth();
canvas.drawBitmap(mBitmap, 0,0, paint);
//canvas.drawCircle(40, 90, 15, paint);
canvas.drawText(mtext, bw/2, 30, paint);
}
}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" > <com.example.myimageview2.MyView
android:id="@+id/myView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
Text="@string/hello_world"
Src="@drawable/xh"/> </LinearLayout>
属性Text, Src在自定义View类的构造方法中读取。
public class MyImageView extends LinearLayout { public MyImageView(Context context) {
super(context);
} public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
int resourceId = -1;
TypedArray typedArray = context.obtainStyledAttributes(attrs,
R.styleable.MyImageView);
ImageView iv = new ImageView(context);
TextView tv = new TextView(context);
int N = typedArray.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = typedArray.getIndex(i);
switch (attr) {
case R.styleable.MyImageView_Oriental:
resourceId = typedArray.getInt(
R.styleable.MyImageView_Oriental, 0);
this.setOrientation(resourceId == 1 ? LinearLayout.HORIZONTAL
: LinearLayout.VERTICAL);
break;
case R.styleable.MyImageView_Text:
resourceId = typedArray.getResourceId(
R.styleable.MyImageView_Text, 0);
tv.setText(resourceId > 0 ? typedArray.getResources().getText(
resourceId) : typedArray
.getString(R.styleable.MyImageView_Text));
break;
case R.styleable.MyImageView_Src:
resourceId = typedArray.getResourceId(
R.styleable.MyImageView_Src, 0);
iv.setImageResource(resourceId > 0 ?resourceId:R.drawable.ic_launcher);
break;
}
}
addView(iv);
addView(tv);
typedArray.recycle();
}
}
attrs.xml进行属性声明, 文件放在values目录下
<?xml version="1.0" encoding="utf-8"?>
<resources> <declare-styleable name="MyImageView">
<attr name="Text" format="reference|string"></attr>
<attr name="Oriental" >
<enum name="Horizontal" value="1"></enum>
<enum name="Vertical" value="0"></enum>
</attr>
<attr name="Src" format="reference|integer"></attr>
</declare-styleable> </resources>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:uview="http://schemas.android.com/apk/res/com.example.myimageview2"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" > <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" /> <com.example.myimageview2.MyImageView
android:id="@+id/myImageView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
uview:Text="这是一个图片说明"
uview:Src="@drawable/tw"
uview:Oriental="Vertical">
</com.example.myimageview2.MyImageView> </LinearLayout>
六:自定义View的方法
《转载-两篇很好的文章整合》Android中自定义控件的更多相关文章
- 两篇很好的EPG相关文章
两篇很好的EPG相关文章 原文地址:http://blog.sina.com.cn/s/blog_53220cef0100pi8j.html 1 基于DVB-SI的数字有线电视机顶盒节目指南的设计实现 ...
- 两篇很牛的vim使用技巧
读本文之前请注意: 1. 本文的目标是提供一些vim的使用技巧,利用这些技巧可以提高vim的操作效率.部分技巧在vi上也可以使用,但是现在基本上都是用vim了. 2. 本文是整理和总结使用技巧,而非讲 ...
- 关于ssh的一篇很好的文章
源地址:http://www.w3hacker.com/?p=156 ssh-agent的manual写得倒是挺详细,可看了好几次都没怎么搞明白.08年在网上找到了非常好的一篇文章,An Illu ...
- 转载两篇dedecms的漏洞修复教程
Dedecms安全步骤,安装之后的操作 1 将文件夹dede改名为其他,比如 /256256.com/ 2 搜索ad.dedecms.com,文件D:\WebSite\256256.com\www\g ...
- 关注 硬件 发展, 转载一篇介绍 VHDL 的文章
<VHDL学习笔记> https://www.eefocus.com/hrbeulvcaho/blog/12-11/289109_978e2.html VHDL 和 “可编程逻辑阵列” ...
- 关于singer elt 的几篇很不错的文章
以下是链接来自singer 团队的实践,很不错,值得学习 参考连接 https://www.stitchdata.com/blog/100-billion-records-later-refining ...
- 前两篇转载别人的精彩文章,自己也总结一下python split的用法吧!
前言:前两篇转载别人的精彩文章,自己也总结一下吧! 最近又开始用起py,是为什么呢? 自己要做一个文本相似度匹配程序,大致思路就是两个文档,一个是试题,一个是材料,我将试题按每题分割出来,再将每题的内 ...
- 关于DataTable的两篇基础文章
DataTable有的时候还是很有用的.记录两篇不错的文章,当字典 1.http://blog.csdn.net/imagse/article/details/3085870 2.https://ms ...
- 上一篇括号配对让人联想起catalan数,顺便转载一篇归纳的还不错的文章
转载请注明来自souldak,微博:@evagle 怎么样才是合法的组合? 只要每一时刻保证左括号的数目>=右括号的数目即可. 直接递归就行,每次递归加一个括号,左括号只要还有就能加,右括号要保 ...
随机推荐
- 游戏音频技术备忘 (五)Wwise Unreal Engine 集成代码浅析 二
AkAmbientSound类的实现 Unreal Engine提供了一个基本对象的构造器ObjectInitializer,一般来说用户创建的类总是拥有很多变量,因此 AkAmbientSound ...
- [UWP]依赖属性2:使用依赖属性
5. 完整的自定义依赖属性 5.1 定义 /// <summary> /// 标识 Title 依赖属性. /// </summary> public static reado ...
- 导入AS项目出现类文件全部报红色J 原因
大家可能遇到过这么一个问题 就是用androidStudio 导入一个新的demo的时候会出现下图的字样 看了是不是很懵逼 我当时看了也是一脸懵逼 这是什么玩意啊.也不报错.也不提示哪里错了 后来我 ...
- swift2.0 字符串,数组,字典学习代码
swift 2.0 改变了一些地方,让swift变得更加完善,这里是一些最基本的初学者的代码,里面涉及到swift学习的最基本的字符串,数组,字典和相关的操作.好了直接看代码吧. class View ...
- handlebars使用总结
对自己使用handlebars做一个小总结,以后忘记了,好有地方看一下,不会用的小伙伴也可以借鉴一下,写的不好. 使用 Handlebars的安装是比较简单和方便的;handlebars是一个纯JS库 ...
- Thrift入门初探(2)--thrift基础知识详解
昨天总结了thrift的安装和入门实例,Thrift入门初探--thrift安装及java入门实例,今天开始总结一下thrift的相关基础知识. Thrift使用一种中间语言IDL,来进行接口的定义, ...
- BZOJ 3924: [Zjoi2015]幻想乡战略游戏(动态点分治)
这种动态点分治嘛,GDKOI时听打到了,也有同学讲到了,所以印象比较深刻也就想出来了,然后就在实现方面卡了好久= = 不得不说CLJ说得真的太简单了,实现方面根本没提. 首先我们可以先用树分治构建出这 ...
- BZOJ 3479: [Usaco2014 Mar]Watering the Fields(最小生成树)
这个= =最近刷的都是水题啊QAQ 排除掉不可能的边然后就最小生成树就行了= = CODE: #include<cstdio>#include<iostream>#includ ...
- Matlab将三维变量分割为多个二维变量的方法
最近在处理 Matlab 中的三维矩阵的时候,遇到了一个问题: 假如m 为 5*5*5的矩阵,如果以第三个维度为基础,分割为5个不同的矩阵 m1,m2,m3,m4,应该如何解决? 解决方法:eval函 ...
- 裸机(Bare Metal)安装CoreOS
本文以 1235.9.0-stable 为例,在WMWare中进行安装. 1.获取安装资源 https://coreos.com/releases/ 通过"Browse Images&quo ...