Android 自定义View及其在布局文件中的使用示例
前言:
尽管Android已经为我们提供了一套丰富的控件,如:Button,ImageView,TextView,EditText等众多控件,但是,有时候在项目开发过程中,还是需要开发者自定义一些需要重复使用的控件,使之能像Android提供的其它控件一样,使用起来方便,幸好Android为我们自定义控件过程扫除了障碍,提供了一套基础的类(如:View,Canvas等)和XML标签(如下文即将提及的resources标签,declare-styleable标签,attr标签等);
创建流程:
一,在value文件夹新建以"attrs"命名的XML文件:
看一下本例中的attrs.xml文件
attrs.xml文件:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="CustomView">
<attr name="textString" format="string"></attr>
<attr name="colorValue" format="color"></attr>
<attr name="textSize" format = "dimension"></attr>
</declare-styleable>
</resources>
attrs.xml文件中,外层引入了如下标签:
<declare-styleable name="CustomView">
这个标签就是为了让我们自定义的View,拥有自身的属性,从上面的代码中,我们可以看到,该标签内包含定义了三个属性,分别取名为:"textString","colorValue","textSize",这样我们就可以方便地使用该View的这些属性,就像我们在使用系统提供的TextView时,在布局文件中设置TextView的textSize,textColor等属性。
我们给declare-styleable的name字段取名为"CustomView",这是因为,我们将在下文给自定义的View取名为"CustomView",为什么declare-styleable的名字要跟我们自定义的这个View的名字一样呢?翻阅了google文档,找到解释:
The name of the styleable entity is, by convention, the same name as the name of the class that defines the custom view. Although it's not strictly necessary to follow this convention, many popular code editors depend on this naming convention to provide statement completion.
外层的declare-styleable标签就分析到这里,我们再来仔细看一下attr标签:
<attr name="textString" format="string"></attr>
<attr name="colorValue" format="color"></attr>
<attr name="textSize" format = "dimension"></attr>
本例中,给自定义的View制定了三个属性,textString:该View显示的Text内容;colorValue:字体的颜色;textSize:字体的大小。attr标签不仅有name字段,并且给出了format字段(关于format字段都有哪些值,在附录中我们给出其具体的定义及应用示例)
二,编写布局文件,引用自定义的View
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.project.summary"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:background="@color/BgColor"> <com.project.summary.customview.CustomView
android:id="@+id/customView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:colorValue="@color/textRed"
app:textString="This the Custom View!!!"
app:textSize="20sp"
/> </LinearLayout>
这里需要注意的地方有两个:
1,新增布局文件的命名空间
因为我们自定义了View,并且自定义了属性,而这些属性不再属于
http://schemas.android.com/apk/res/android
这个命名空间,而是属于
http://schemas.android.com/apk/res/[your package name].
所以我们需要增加布局文件中的命名空间,改成
xmlns:app="http://schemas.android.com/apk/res/com.project.summary"
2,自定义View在布局文件中的引用
<com.project.summary.customview.CustomView
我们需要把这个自定义类的包名都写完整;
另外:如果CustomView这个类是ParentCustomView类的内部类,那么在布局文件中的引用应该写成
<com.project.summary.customview.ParentCustomView$CustomView
即需要在类和之间增加字符$
3,自定义属性在布局文件中的引用
app:colorValue="@color/textRed"
app:textString="This the Custom View!!!"
app:textSize="20sp"
需要在自定义的属性前面加上app字段(因为app="http://schemas.android.com/apk/res/com.project.summary",在这里app就是指代新的命名空间)。
三,编写自定义View代码
google文档要求该自定义的View中,至少要有以Context和AttributeSet为参数的构造方法,原因有两个:
1.
To allow the Android Developer Tools to interact with your view, at a minimum you must provide a constructor that takes a Context and an AttributeSet object as parameters. This constructor allows the layout editor to create and edit an instance of your view.
2.
When a view is created from an XML layout, all of the attributes in the XML tag are read from the resource bundle and passed into the view's constructor as an AttributeSet. Although it's possible to read values from the AttributeSet directly, doing so has some disadvantages:
A:Resource references within attribute values are not resolved;
B:Styles are not applied;
Instead, pass the AttributeSet to obtainStyledAttributes(). This method passes back a TypedArray array of values that have already been dereferenced and styled. The Android resource compiler does a lot of work for you to make calling obtainStyledAttributes() easier. For each <declare-styleable> resource in the res directory, the generated R.java defines both an array of attribute ids and a set of constants that define the index for each attribute in the array. You use the predefined constants to read the attributes from the TypedArray.
第一个原因:为了让我们的开发工具 layout editor创建和编辑我们自定义的View;
第二个原因:这个也是最主要的原因,当我们从布局文件中创建View的时候,布局文件中的所有标签,标签中的所有属性都被读到资源包里,并且这个资源包被包装成属性集合AttributeSet传递给自定义View的构造方法;
在构造方法中,使用 obtainStyledAttributes()方法将这些属性转化成TypedArray数组,数组里包含我们自定义的属性ID和常量集合,这样,我们就可以用我们定义的常量名称很方便地从TypedArray中读取我们定义的属性。
所以我们至少先编写包含Context和AttributeSet为参数的构造方法
public class CustomView extends View {
private int color;
private String mText;
private int textSize; public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CustomView);
try {
mText = a.getString(R.styleable.CustomView_textString);
color = a.getColor(R.styleable.CustomView_colorValue,
R.color.textRed);
textSize = a.getDimensionPixelOffset(
R.styleable.CustomView_textSize, 20);
} finally {
a.recycle();
}
}
从代码中可以看出,我们可以用TypedArray提供的相关方法,来取出我们在布局文件中设置的相关属性,此处还需要注意TypedArray的回收!
四,本例中,我们自定义了一个View用来实现显示文字,类似于TextView
由于本文只是讲述如何自定义View,以及其使用,自定义View的功能部分不在本文范畴,将在下一篇中具体讲述;所以,下面只贴代码,不再具体讲述。
public class CustomView extends View {
private int color;
private Paint mTextPaint;
private String mText;
private int textSize;
private int mAscent; public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
initLabelView();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.CustomView);
try {
mText = a.getString(R.styleable.CustomView_textString);
color = a.getColor(R.styleable.CustomView_colorValue,
R.color.textRed);
if (mText != null) {
setCustomText(mText);
}
setTextColor(color);
textSize = a.getDimensionPixelOffset(
R.styleable.CustomView_textSize, 20);
if (textSize > 0) {
setTextSize(textSize);
}
} finally {
a.recycle();
}
} /**
* Sets the text to display in this label
*
* @param text
* The text to display. This will be drawn as one line.
*/
private void setCustomText(String text) {
// TODO Auto-generated method stub
mText = text;
requestLayout();
invalidate();
} private final void initLabelView() {
mTextPaint = new Paint();
mTextPaint.setAntiAlias(true);
// Must manually scale the desired text size to match screen density
mTextPaint.setTextSize(16 * getResources().getDisplayMetrics().density);
mTextPaint.setColor(0xFF000000);
setPadding(3, 3, 3, 3);
} @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(measureWidth(widthMeasureSpec),
measureHeight(heightMeasureSpec));
} /**
* Determines the width of this view
*
* @param measureSpec
* A measureSpec packed into an int
* @return The width of the view, honoring constraints from measureSpec
*/
private int measureWidth(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec); if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text
result = (int) mTextPaint.measureText(mText) + getPaddingLeft()
+ getPaddingRight();
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by
// measureSpec
result = Math.min(result, specSize);
}
} return result;
} /**
* Determines the height of this view
*
* @param measureSpec
* A measureSpec packed into an int
* @return The height of the view, honoring constraints from measureSpec
*/
private int measureHeight(int measureSpec) {
int result = 0;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec); mAscent = (int) mTextPaint.ascent();
if (specMode == MeasureSpec.EXACTLY) {
// We were told how big to be
result = specSize;
} else {
// Measure the text (beware: ascent is a negative number)
result = (int) (-mAscent + mTextPaint.descent()) + getPaddingTop()
+ getPaddingBottom();
if (specMode == MeasureSpec.AT_MOST) {
// Respect AT_MOST value if that was what is called for by
// measureSpec
result = Math.min(result, specSize);
}
}
return result;
} /**
* Sets the text size for this label
*
* @param size
* Font size
*/
public void setTextSize(int size) {
// This text size has been pre-scaled by the getDimensionPixelOffset
// method
mTextPaint.setTextSize(size);
requestLayout();
invalidate();
} /**
* Sets the text color for this label.
*
* @param color
* ARGB value for the text
*/
public void setTextColor(int color) {
mTextPaint.setColor(color);
invalidate();
} /**
* Render the text
*
* @see android.view.View#onDraw(android.graphics.Canvas)
*/
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent,
mTextPaint);
}
// @Override
// protected void onMeasure(final int widthMeasureSpec,
// final int heightMeasureSpec) {
// int width = MeasureSpec.getSize(widthMeasureSpec);
// // int height = (int) (width * heightScale / widthScale);
// int height = MeasureSpec.getSize(heightMeasureSpec);
// if (height == 0) {
// super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// } else {
// super.onMeasure(
// MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
// MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
// }
// }
}
附录:format的定义及应用示例:
1. reference:资源引用。
属性定义:
<attr name = "background" format = "reference" />
属性使用:
<com.lin.gw.CustomView
android:layout_width = "42dip"
android:layout_height = "42dip"
app:background = "@drawable/图片ID"
/>
2. color:颜色值。
属性定义:
<attr name = "textColor" format = "color" />
属性使用:
<com.lin.gw.CustomView
android:layout_width = "42dip"
android:layout_height = "42dip"
app:textColor = "#fff000"
/>
3. boolean:布尔值。
属性定义:
<attr name = "focusable" format = "boolean" />
属性使用:
<com.lin.gw.CustomView
android:layout_width = "42dip"
android:layout_height = "42dip"
app:focusable = "true"
/>
4. dimension:尺寸值。
属性定义:
<attr name = "customWidth" format = "dimension" />
属性使用:
<com.lin.gw.CustomView
app:customWidth = "42dip"
android:layout_height = "wrap_content"
/>
5. float:浮点值。
属性定义:
<attr name = "fromAlpha" format = "float" />
属性使用:
<com.lin.gw.CustomView
app:fromAlpha = "2.0"
/>
6. integer:整型值。
属性定义:
<attr name = "frameDuration" format="integer" />
属性使用:
<com.lin.gw.CustomView
app:frameDuration = "20"
/>
7. string:字符串。
属性定义:
<attr name="textString" format="string"></attr>
属性使用:
<com.lin.gw.CustomView
app:textString = "hello lingling!"
/>
8. fraction:百分数。
属性定义:
<attr name = "pivotX" format = "fraction" />
属性使用:
<com.lin.gw.CustomView
app:pivotX = "30%"
/>
9. enum:枚举值。
属性定义:
<attr name="orientation">
<enum name="horizontal" value="0" />
<enum name="vertical" value="1" />
</attr>
属性使用:
<com.lin.gw.CustomView
app:orientation = "vertical"
/>
10. flag:位或运算。
属性定义:
<declare-styleable name="CustomView">
<attr name="windowSoftInputMode">
<flag name = "stateUnspecified" value = "0" />
<flag name = "stateUnchanged" value = "1" />
<flag name = "stateHidden" value = "2" />
<flag name = "stateAlwaysHidden" value = "3" />
<flag name = "stateVisible" value = "4" />
<flag name = "stateAlwaysVisible" value = "5" />
<flag name = "adjustUnspecified" value = "0x00" />
<flag name = "adjustResize" value = "0x10" />
<flag name = "adjustPan" value = "0x20" />
<flag name = "adjustNothing" value = "0x30" />
</attr>
</declare-styleable>
属性使用:
app:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
转载请注明出处
http://www.cnblogs.com/crashmaker/p/3521310.html
From crash_coder linguowu
linguowu0622@gamil.com
Android 自定义View及其在布局文件中的使用示例的更多相关文章
- Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程
转载请注明出处 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu linguowu0622@gami ...
- Android 自定义View及其在布局文件中的使用示例(二)
转载请注明出处 http://www.cnblogs.com/crashmaker/p/3530213.html From crash_coder linguowu linguowu0622@gami ...
- Android查缺补漏(View篇)--布局文件中的“@+id”和“@id”有什么区别?
Android布局文件中的"@+id"和"@id"有什么区别? +id表示为控件指定一个id(新增一个id),如: <cn.codingblock.vie ...
- android 布局文件中xmlns:android="http://schemas.android.com/apk/res/android"
http://blog.163.com/benben_long/blog/static/199458243201411394624170/ xmlns:android="http://sch ...
- Android自定义View初步
经过上一篇的介绍,大家对于自定义View一定有了一定的认识,接下来我们就以实现一个图片下显示文字的自定义View来练习一下.废话不多说,下面进入我们的正题,首先看一下我们的思路,1.我们需要通过在va ...
- android自定义view系列:认识activity结构
标签: android 自定义view activity 开发中虽然我们调用Activity的setContentView(R.layout.activity_main)方法显示View视图,但是vi ...
- Android 自定义View修炼-Android中常见的热门标签的流式布局的实现
一.概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出哈) 类似的 ...
- Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题
这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...
- android自定义View&&简单布局&&回调方法
一.内容描述 根据“慕课网”上的教程,实现一个自定义的View,且该View中使用自定义的属性,同时为该自定义的View定义点击事件的回调方法. 二.定义自定义的属性 在res/valus/ 文件夹下 ...
随机推荐
- 我的MYSQL学习心得(一) 简单语法
我的MYSQL学习心得(一) 简单语法 我的MYSQL学习心得(二) 数据类型宽度 我的MYSQL学习心得(三) 查看字段长度 我的MYSQL学习心得(四) 数据类型 我的MYSQL学习心得(五) 运 ...
- 恋爱虽易,相处不易:当EntityFramework爱上AutoMapper
剧情开始 为何相爱? 相处的问题? 女人的伟大? 剧情收尾? 有时候相识即是一种缘分,相爱也不需要太多的理由,一个眼神足矣,当EntityFramework遇上AutoMapper,就是如此,恋爱虽易 ...
- ExtJS 4.2 介绍
本篇介绍ExtJS相关知识,是以ExtJS4.2.1版本为基础进行说明,包括:ExtJS的特点.MVC模式.4.2.1GPL版本资源的下载和说明以及4种主题的演示. 目录 1. 介绍 1.1 说明 1 ...
- python黑魔法 -- 内置方法使用
很多pythonic的代码都会用到内置方法,根据自己的经验,罗列一下自己知道的内置方法. __getitem__ __setitem__ __delitem__ 这三个方法是字典类的内置方法,分别对应 ...
- 快速搭建springmvc+spring data jpa工程
一.前言 这里简单讲述一下如何快速使用springmvc和spring data jpa搭建后台开发工程,并提供了一个简单的demo作为参考. 二.创建maven工程 http://www.cnblo ...
- JQuery 复制粘贴上传图片插件(textarea 和 tinyMCE)
开源地址:https://github.com/yuezhongxin/paste-upload-image.js 支持 Ctrl+C/Ctrl+V 上传,支持拖拽上传,也支持 QQ/微信截图上传. ...
- android studio你可能忽视的细节——启动白屏?drawable和mipmap出现的意义?这里都有!!!
android studio用了很久了,也不知道各位小伙伴有没有还在用eclipse的,如果还有,楼主真心推荐转到android studio来吧,毕竟亲儿子,你会知道除了启动速度稍微慢些,你找不到一 ...
- IteratorPattern(迭代子模式)
/** * 迭代子模式 * @author TMAC-J * 聚合:某一类对象的集合 * 迭代:行为方式,用来处理聚合 * 是一种行为模式,用于将聚合本身和操作聚合的行为分离 * Java中的COLL ...
- Android手机相册的布局
实现类似下面的这种布局的方法
- Android中Activity处理返回结果的实现方式
大家在网上购物时都有这样一个体验,在确认订单选择收货人以及地址时,会跳转页面到我们存入网站内的所有收货信息(包含收货地址,收货人)的界面供我们选择,一旦我们点击其中某一条信息,则会自动跳转到订单提交界 ...