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/ 文件夹下 ...
随机推荐
- 【.net 深呼吸】细说CodeDom(7):索引器
在开始正题之前,先补充一点前面的内容. 在方法中,如果要引用方法参数,前面的示例中,老周使用的是 CodeVariableReferenceExpression 类,它用于引用变量,也适用于引用方法参 ...
- Fis3的前端工程化之路[三大特性篇之资源定位]
Fis3版本:v3.4.22 Fis3的三大特性 资源定位:获取任何开发中所使用资源的线上路径 内容嵌入:把一个文件的内容(文本)或者base64编码(图片)嵌入到另一个文件中 依赖声明:在一个文本文 ...
- [C#] async 的三大返回类型
async 的三大返回类型 序 博主简单数了下自己发布过的异步文章,已经断断续续 8 篇了,这次我想以 async 的返回类型为例,单独谈谈. 异步方法具有三个可让开发人员选择的返回类型:Task&l ...
- C++中的事件分发
本文意在展现一个C++实现的通用事件分发系统,能够灵活的处理各种事件.对于事件处理函数的注册,希望既能注册到普通函数,注册到事件处理类,也能注册到任意类的成员函数.这样在游戏客户端的逻辑处理中,可以非 ...
- 使用AWS亚马逊云搭建Gmail转发服务(三)
title: 使用AWS亚马逊云搭建Gmail转发服务(三) author:青南 date: 2015-01-02 15:42:22 categories: [Python] tags: [log,G ...
- 设置line-height:1.5和line-height:150%或者line-height:150px的区别
直接正题: 看一下line-height可能的值: 其实可以分为两类: (1)不带单位的(如line-height:1.5),这种是推荐使用的: (2)带单位的(如line-heigth:30px/1 ...
- 【HanLP】资料链接汇总
Java中调用HanLP配置 HanLP自然语言处理包开源官方文档 了解HanLP的全部 自然语言处理HanLP 开源自由的汉语言处理包主页 GitHub源码 基于hanLP的中文分词详解-MapRe ...
- 使用RequireJS并实现一个自己的模块加载器 (一)
RequireJS & SeaJS 在 模块化开发 开发以前,都是直接在页面上引入 script 标签来引用脚本的,当项目变得比较复杂,就会带来很多问题. JS项目中的依赖只有通过引入JS的顺 ...
- C#制作简易屏保
前言:前段时间,有个网友问我C#制作屏保的问题,我瞬间懵逼了(C#还可以制作屏保!).于是我去查阅相关资料,下面把C#如何制作屏保的过程及我学习过程的心得也记录下来,希望对需要的人能有帮助. 基本思路 ...
- .NET设计模式访问者模式
一.访问者模式的定义: 表示一个作用于某对象结构中的各元素的操作.它使你可以在不改变各元素类的前提下定义作用于这些元素的新操作. 二.访问者模式的结构和角色: 1.Visitor 抽象访问者角色,为该 ...