前言:

尽管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及其在布局文件中的使用示例的更多相关文章

  1. Android 自定义View及其在布局文件中的使用示例(三):结合Android 4.4.2_r1源码分析onMeasure过程

    转载请注明出处 http://www.cnblogs.com/crashmaker/p/3549365.html From crash_coder linguowu linguowu0622@gami ...

  2. Android 自定义View及其在布局文件中的使用示例(二)

    转载请注明出处 http://www.cnblogs.com/crashmaker/p/3530213.html From crash_coder linguowu linguowu0622@gami ...

  3. Android查缺补漏(View篇)--布局文件中的“@+id”和“@id”有什么区别?

    Android布局文件中的"@+id"和"@id"有什么区别? +id表示为控件指定一个id(新增一个id),如: <cn.codingblock.vie ...

  4. android 布局文件中xmlns:android="http://schemas.android.com/apk/res/android"

    http://blog.163.com/benben_long/blog/static/199458243201411394624170/ xmlns:android="http://sch ...

  5. Android自定义View初步

    经过上一篇的介绍,大家对于自定义View一定有了一定的认识,接下来我们就以实现一个图片下显示文字的自定义View来练习一下.废话不多说,下面进入我们的正题,首先看一下我们的思路,1.我们需要通过在va ...

  6. android自定义view系列:认识activity结构

    标签: android 自定义view activity 开发中虽然我们调用Activity的setContentView(R.layout.activity_main)方法显示View视图,但是vi ...

  7. Android 自定义View修炼-Android中常见的热门标签的流式布局的实现

    一.概述:在日常的app使用中,我们会在android 的app中看见 热门标签等自动换行的流式布局,今天,我们就来看看如何 自定义一个类似热门标签那样的流式布局吧(源码下载在下面最后给出哈) 类似的 ...

  8. Android自定义View研究--View中的原点坐标和XML中布局自定义View时View触摸原点问题

    这里只做个汇总~.~独一无二 文章出处:http://blog.csdn.net/djy1992/article/details/9715047 Android自定义View研究--View中的原点坐 ...

  9. android自定义View&&简单布局&&回调方法

    一.内容描述 根据“慕课网”上的教程,实现一个自定义的View,且该View中使用自定义的属性,同时为该自定义的View定义点击事件的回调方法. 二.定义自定义的属性 在res/valus/ 文件夹下 ...

随机推荐

  1. ASP.NET Aries 入门开发教程8:树型列表及自定义右键菜单

    前言: 前面几篇重点都在讲普通列表的相关操作. 本篇主要讲树型列表的操作. 框架在设计时,已经把树型列表和普通列表全面统一了操作,用法几乎是一致的. 下面介绍一些差距化的内容: 1:树型列表绑定: v ...

  2. C语言 · 数位分离

    问题描述 编写一个程序,输入一个1000 以内的正整数,然后把这个整数的每一位数字都分离出来,并逐一地显示. 输入格式:输入只有一行,即一个1000以内的正整数. 输出格式:输出只有一行,即该整数的每 ...

  3. UIViewController生命周期-完整版

    一.UIViewController 的生命周期 下面带 (NSObject)的方法是NSObject提供的方法.其他的都是UIViewController 提供的方法. load   (NSObje ...

  4. ASP.NET MVC5+EF6+EasyUI 后台管理系统(72)-微信公众平台开发-消息处理

    系列目录 前言 Senparc.Weixin.MP SDK提供了MessageHandler消息处理类 在作者的Wiki中也详细说明了如何定义这个类,下面我们来演示,消息的回复,及效果 了解Messa ...

  5. PHP 获取 特定时间范围 类

    目录  前序 用途 功能及事项 使用方法 代码及注释 前序: 总体来说,我更应该是一个 android 移动开发者,而不是一个 phper,如果说只做移动端的 APP ,我也不会学这么多,这 2年来, ...

  6. 火星坐标、百度坐标、WGS-84坐标相互转换及墨卡托投影坐标转经纬度JavaScript版

    火星坐标 火星坐标是国家测绘局为了国家安全在原始坐标的基础上进行偏移得到的坐标,基本国内的电子地图.导航设备都是采用的这一坐标系或在这一坐标的基础上进行二次加密得到的.火星坐标的真实名称应该是GCJ- ...

  7. jQuery可拖拽3D万花筒旋转特效

    这是一个使用了CSS3立体效果的强大特效,本特效使用jQuery跟CSS3 transform来实现在用户鼠标按下拖动时,环形图片墙可以跟随鼠标进行3D旋转动画. 效果体验:http://hovert ...

  8. 通过squid 禁止访问/只允许访问指定 网址

    安装 squid yum install squid -y 备份squid.conf cp  squid.conf  squid.conf-list vi  squid.conf 输入: acl de ...

  9. python-time 模块

    1.时间戳是以秒为单位的浮点小数,时间戳以自1970年1月1日午夜到现在经过了的时间来表示 2.时间模块引入方式:import time 3.返回时间戳 time.time() 4.返回时间元组:ti ...

  10. [DS] 标记字段

    标记字段 代码中有时候有这种需求:需要一个公共访问的标记字段,以下称为标记字段. 下面是案例: 一个订单详情页面,如果页面在显示中,程序中其它地方需要访问这个"正在查看中"的订单信 ...