对于Android自定义控件和动画的知识,我一直是一窍不通。今天趁着“闲暇”时间,在网上搜罗了一些关于自定义控件的资料,现将其中个人觉得比较好的内容整理如下,供读者朋友们参考。
个人觉得如果想真正去实现一个自定义控件,了解其原理和实现步骤还是很有必要的。这哥们在博客里给出的实现开发步骤还是很详细的:
a)了解View的工作原理;
b)编写继承自View的子类;
c)为自定义的View类增加属性;
d)绘制控件;
e)响应用户消息;
f)自定义回调函数。
现在我们就沿着上述步骤来做个简单的说明。
1)Android中View的工作原理
Android系统的View在设计上采用了组合模式,类View是所有View控件的父类,ViewGroup同样继承自View,用于放置其他View控件(含ViewGroup控件,也称为视图容器)。
与View的绘制密切相关的方法主要有三个,即measure()、layout()和draw()方法,其内部又分别调用了onMeasure()、onLayout和onDraw()三个子方法。其具体操作如下:
1.1)measure操作
measure主要用于计算View的大小,即View的width和height值。在view中,该方法是final类型,不允许子类继承该方法。在measure操作内部调用了onMeasure()方法,View的大小最终是通过调用onMeasure()方法确定的,并且通过调用setMeasuredDimention(width, height)方法保存计算结果。
1.2)layout操作
layout操作主要用于控制View在屏幕中显示的位置,该方法同样为final类型,主要用在ViewGroup中。与layout()方法相关的操作主要是setFrame(l, t, r, b)和onLayout()方法。setFrame中的参数指定了子View在视图容器中的位置,通过该方法将位置信息保存。onLayout()方法主要用于ViewGroup对子视图显示位置的控制。
1.3)draw操作
draw操作会根据measure和layout计算到的结果,来完成View在屏幕中的显示。子类也不应该修改该方法,因为其内部定义了绘图的基本操作:
(1)绘制背景;
下面这段内容来自大牛博客,我只是做了排版上的修改:
整个 draw是从根View开始的,ViewGroup向子View发出 draw的请求,然后子View负责自己重画它们的invalid区域。Drawing一个Layout必须通过两个步骤:
- 一个准确的数值。
- ·FILL_PARENT,这意味着视图想和父视图一样大(减掉填充padding)。
- WRAP_CONTENT,这意味着视图只想有刚好包装其内容那么大(加上padding)。
- UNSPECIFIED:父视图来决定其子视图的理想尺寸。比如,一个线性布局可能在它的子视图上调用measure(),通过设置其高度为UNSPECIFIED 以及一个宽度为EXACTLY 240,来找出这个子视图在给定240像素宽度的情况下需要显示多高。
- EXACTLY:父视图用来给子视图强加一个准确的尺寸。子视图必须使用这个大小,并确保其所有的后代将适合这个尺寸。
- AT_MOST:这被父视图用来给子视图强加一个最大尺寸。子视图必须确保它自己以及所有的后代都适合这个尺寸。
<span style="font-family:SimSun;font-size:18px;">public class MyView extends View {
private String mtext;
private int msrc;
public MyView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
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) {
// TODO Auto-generated method stub
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);
}
}</span>
布局文件:
<span style="font-family:SimSun;font-size:18px;"><?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></span>
属性Text, Src在自定义View类的构造方法中读取。
<span style="font-family:SimSun;font-size:18px;">public class MyImageView extends LinearLayout {
public MyImageView(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public MyImageView(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
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();
}
}
</span>
attrs.xml进行属性声明, 文件放在values目录下
<span style="font-family:SimSun;font-size:18px;"><?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>
</span>
<span style="font-family:SimSun;font-size:18px;"><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></span>
4)绘制控件
5)响应用户消息
6)自定义回调函数
参考博文:
1、Android自定义控件:http://blog.163.com/ppy2790@126/blog/static/103242241201382210910473/
2、开发者指南-Android如何绘制View:http://zuiniuwang.blog.51cto.com/3709988/718274
3、 Android应用程序窗口(Activity)的测量(Measure)、布局(Layout)和绘制(Draw)过程分析: http://blog.youkuaiyun.com/luoshengyang/article/details/8372924