[转载]Android中自定义View的研究(四) -- 在XML中定义View

mikel阅读(1071)

Android中自定义View的研究(四) — 在XML中定义View

       如果在一直使用SetContentView(new HellwView(this)噶虐总是少了一点东西,少了什么了,失去了Android中使用XML定义组件的便携性,这种感觉让人很不爽,呵呵,在这节里我们会看到一个自定义View报错的解决方法,让我们来看看在XML中定义View
 

一、XML中定义View的一个小错误

 
我们试着直接将错误的那个例子写出来
将上一讲的View例子拿出来,修改main布局:
<!--?xml version="1.0" encoding="utf-8"?-->

修改MainActivity

super.onCreate(savedInstanceState);
setContentView(R.layout.main);// 使用自定义的View
运行:
 
 
 

 
       我们发现,竟然报错了,我们在LogCat里查看下:
 
11-24 10:58:38.993: ERROR/AndroidRuntime(323): Caused by: java.lang.NoSuchMethodException: HelloView(Context,AttributeSet)
 
竟然是没有HelloView(Context,AttributeSet)这个构造器,结局方法呼之欲出了,呵呵。
 

二、解决方法

只需要在HelloView 中添加以下方法就解决了:
/**

* 这个是我们要在XML中初始化用的

* */

public HelloView(Context context,AttributeSet attrs){

super(context, attrs);
}
运行:
 

 
关于这个解决方法网上有类似的问题:为什么非得加上这个方法,其实这个方法是作为系统解析XML中定义的属性时作为回调方法用的。如果想更深入的了解View以及刚才的解决方案的原理的话,可以关注我的博客,我会在以后的《深入解析View原理》中讲解,呵呵,说不定有时候会有一种豁然开朗的感觉。
 

三、另一中在XML中的View布局

我们也可以使用如下的方法在XML中添加View
<!--?xml version="1.0" encoding="utf-8"?-->

&nbsp;
运行结果与上面一样
 
  

本文出自 “皓月繁星” 博客,请务必保留此出处http://lovewf.blog.51cto.com/1723922/724124

[转载]Android自定义View研究(五)--View的大小

mikel阅读(968)

.Android自定义View研究(五)–View的大小

说了这么多,那View的大小是多少呢?这小节我就研究下View的大小。通过LogCat来研究View的大小是怎样确定的。好了,直接切入正题吧.

一、        Activity中直接new HelloViewView的大小。

View的大小获取可以用其中的两种方法获取:
   this.getHeight():获取View的高
this.getWidth():获取View的宽
我们可以做一个猜想,View的大小是在什么时候确定的,是在new一个View的时候还是在onDraw()的时候?还是在其他时候?为了研究这个,我们分别在构造函数和onDraw中打上Log补丁(这个只是个人习惯的称呼,不足为鉴)
— >HelloVew.java
public HelloView(Context context){

super(context);

Log.v("HelloView(Context context)","" + this.getHeight()+ " " + this.getWidth());

}

/**

* 这个是我们要在XML中初始化用的

* */

public HelloView(Context context,AttributeSet attrs){

super(context, attrs);

Log.v("HelloView(Context context,AttributeSet attrs)","" + this.getHeight()+ " " + this.getWidth());

}

/**

* 绘制View

* */

protected void onDraw(Canvas canvas){

Log.v("onDraw(Canvas canvas)","" + this.getHeight()+ " " + this.getWidth());

canvas.drawColor(Color.WHITE);

myUseBitmapFactory(canvas);

myUseBitmapDrawable(canvas);

myUseInputStreamandBitmapDrawable(canvas);
}

 

运行:

 
我们观察可以发现,new View 的时候并没有确定了View的大小,并且系统就没有调用(Context context)这个构造函数。
也就是说View大小是在new View之后OnDraw之前确定的,那onDraw之前的又有那些方法了,呵呵,我们试着override这个方法试试:
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

// TODO Auto-generated method stub

super.onMeasure(widthMeasureSpec, heightMeasureSpec);

Log.v("onMeasure","" + this.getHeight()+ " " + this.getWidth());

}
 
运行:
 
 
 
我们观察发现:onMeasure方法运行了两次:第一次宽和高都是0,但是第二次就变了,是不是可以说是在这个方法中确定的,但是实际上不一定会是这么回事,这个我们放在以后研究。这里我们只需要知道不是在new View时确定的就好了。
 
 
二、在XML中定义时View大小
 
这个我们直接上代码:
 
main.xml文件修改:
<!--?xml version="1.0" encoding="utf-8"?-->

&nbsp;

&nbsp;

mainActivity :

/**

* 使用自定义的View

* */

public class MainActivity extends Activity {

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);// 使用自定义的View

}

}

 
 
 
       
 
我们发现,和我们Xml中定义的大小一样,哈哈,有兴趣的可以自己测试测试。
 
 

本文出自 “皓月繁星” 博客,请务必保留此出处http://lovewf.blog.51cto.com/1723922/727714

[转载]Android自定义View研究(六)--View中的原点坐标相关问题

mikel阅读(866)

Android自定义View研究(六)–View中的原点坐标相关问题

我们自定义了View,但是有没想过一个问题,就是View中的(0,0)坐标,也就是原点坐标在哪??我们是不是有时候很困惑,接下来我们就来研究View中的原点坐标相关的问题。

 

 

 

一、new HelloViewView的原点

 

我们通过从View中绘制一条从原点到右下角的线来看看这个View中的原点和这个View的宽和高。这里我们从onDraw(Canvas canvas)中绘制,下面给出核心代码。

HelloView.java

/**

* 初始化绘制线的画笔

* */

public void initLinePaint(){

linePaint = new Paint();

// 设置画笔

linePaint.setColor(Color.GREEN);

linePaint.setAntiAlias(true);

linePaint.setStrokeWidth(5); // 设置线宽

}

/**

* 绘制View

* */

protected void onDraw(Canvas canvas){

Log.v("onDraw(Canvas canvas)","" + this.getHeight()+ " " + this.getWidth());

canvas.drawColor(Color.WHITE);

initLinePaint(); // 初始化画笔

myUseBitmapFactory(canvas);

canvas.drawLine(0, 0, this.getWidth(), this.getHeight(), linePaint);

}

运行:  

 

 

 

通过观察发现,View大小是从使用继承View的那个Label下开始

 

计算的,也就是这个View原点是在Label的左下角开始,到屏幕的右下角结束。

 

自己多试试,呵呵。

 

 

 

 

 

二、使用XML中布局文件时的View原点。

 

   修改布局:

   MainActivity.java

/**

* 使用自定义的View

* */

public class MainActivity extends Activity {

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);// 使用自定义的View

}

}

 

这里直接给出运行结果图:

                      

 

我想不用我说大家就明白了吧,哈哈,不多解释了。

 

本文出自 “皓月繁星” 博客,请务必保留此出处http://lovewf.blog.51cto.com/1723922/728918

[转载] Android自定义View研究(七)--XML中布局自定义View时View触摸原点问题

mikel阅读(818)

.

XML中布局时,我们自定义的View的触摸原点在哪??这又是一个问题啊,学习的过程就是不断发现问题,解决问题的过程。呵呵,相信大家都是深有体会吧,这次我们研究View的触摸原点问题。

下面是核心代码:

/**

* 定义手势识别

* */

GestureDetector mGestureDetector;

/**

* 注册手势识别,当然这个要在构造里初始化一下

* */

public void myEvent(){

mGestureDetector = new GestureDetector(

new GestureDetector.SimpleOnGestureListener(){

public boolean onSingleTapUp(MotionEvent e){

Log.v("onTouchEvent"," e.getX():" + e.getX()+ " e.getY():" + e.getY());

return true;

}

});

}

/**

* 响应触摸屏

* */

public boolean onTouchEvent(MotionEvent event) {

mGestureDetector.onTouchEvent(event);

return true;

}

运行一下:

                     

下面是点击的示意图:

                 

 

点击后查看log

 

通过观察发现,这个和我们定义的View的大小相符,呵呵,结果不言而喻啊,哈哈。

 

本文出自 “皓月繁星” 博客,请务必保留此出处http://lovewf.blog.51cto.com/1723922/729889

[转载]Android自定义View研究(八)--自定义View总结

mikel阅读(901)

.

关于自定义View就讲到这了,自定义View可以说有很大的用途,在这里我们小小的总结一下,哈哈,也算是一个笔记,方便以后学习。
 
一、使用自定义View的两种基本方法
 
1. setContentView(new HelloView(this));
这种方法是在Activity中直接使用View
优点:这样布局的话可以很大程度上的自己控制View,绘图灵活,可以很好的控制View中的坐标。
缺点:这种布局失去了Android平台的特点,使用XML布局,并且布局不太灵活。
 
2. <com.fxhy.stady.HelloView
    Android:layout_width=“fill_parent”
    android:layout_height=“wrap_content”
    />
这种方法是直接在XML文件中布局。
优点:布局灵活,可以很好的使用Android中在XML中布局的特性
缺点:慢慢感觉吧,我感觉还是很好用的,呵呵
 
二、View中的原点坐标及大小
用自定义View时,通常各个组件的位置是相当重要的,下面是两种位置。
1.    setContentView(new HelloView(this))
原点:View的原点就是显示出来的View的左上角,我想看过前面讲解的应该对这个一下就理解了。
大小:这个View的大小取决与你屏幕的大小减去上面的通知栏和标题栏
 
2. <com.fxhy.stady.HelloView
    android:layout_width=“30px”
    android:layout_height=“120px”
    />
原点:View的原点就是显示出来的View的左上角
大小:就是我们指定的View的大小,是不是很有意思。哈哈
 
三、触摸ViewView的坐标原点
 
触摸View时,View的触摸原点就是显示出的这个View大左上角,而不是相对于屏幕的位置,这个要注意一下,以后会经常用到,呵呵
 
好了Android自定义View就到这了,如果有兴趣请关注我的博客。下一个专题是使用Android基础适配器的研究,欢迎大家一起探讨。
 
四、一个需要注意的地方。
 
有一个需要注意的地方,在View中的onDraw方法中,那个Canvas是无限大的,这个我将会在以后的研究中给出答案。
下一个专题是Android自定义ViewGroup研究
 
声明:我一直希望通过简单的例子入手,慢慢研究,找到我们需要的答案,深入去挖掘、去理解这些常见的东西,同时欢迎各位喜欢Android的朋友可以一起研究。
交流群:194802363  andorid研究交流群   添加时请注明:Android深入研究。
博客地址搬迁通知:即日起本博将搬到个人独立博客思享

本文出自 “皓月繁星” 博客,请务必保留此出处http://lovewf.blog.51cto.com/1723922/731524

[转载]Android自定义View的实现(续)-做早起的鸟-ChinaUnix博客

mikel阅读(872)

[转载]Android自定义View的实现(续)-做早起的鸟-ChinaUnix博客.

自定义View会用到TypedArrayattrs.xmlAttributeSet知识

 

 

 

先说一下attrs.xml文件。这个文件定义了自定义View的属性的信息,包括属于哪个控件属性的名称,属性的类型。下面是一个普通的attrs.xml的内容

 

 

 

<resources>

 

<declare-styleable name = “MyView”>

 

    <attr name = “textColor” format = “color”></attr>

 

<attr name = “textSize” format = “dimension”/>

 

</declare-styleable>

 

</resources>

 

 

 

其中标签declare-styleablename属性代表了接下来定义的属性的所属控件(只是用来区分不同declare-styleable的代号而且,不一定非要和属性相关的控件的名称一致)。标签attr就是用来的定义具体的属性,name代表属性名,format代表属性的类型。

 

 

 

Attrs.xml文件中属性类型format值的格式

 

引用型reference

 

定义:

 

<attr name = “background” format = “reference” />

 

使用:

 

Tools:background = “@drawable/图片ID”

 

颜色型color

 

定义:<attr name = “textColor” format = “color” />

 

使用:tools:textColor = “#ffffff”

 

布尔型boolean

 

定义:<attr name = “focusable” format = “boolean” />

 

使用:tools: focusable = “true”

 

尺寸型dimension

 

定义:<attr name = “layout_width” format = “dimension” />

 

使用:tools: layout_width = “42dip”

 

浮点型float

 

定义:<attr name = “fromAlpha” format = “float” />

 

使用:tools: fromAlpha = “1.0”

 

整型integer

 

定义:<attr name = “frameDuration” format = “integer” />

 

使用:tools: frameDuration = “100”

 

字符串string

 

定义:<attr name = “apiKey” format = “string” />

 

使用:tools: apiKey = “dsegergegasefwg”

 

百分数fraction

 

定义:<attr name = “pivotX” format = “fraction” />

 

使用:tools: pivotx = “200%”

 

枚举型enum

 

< attr name=”orientation”>

 

  < enum name=”horizontal” value=”0″ />

 

  < enum name=”vertical” value=”1″ />

 

< /attr>

 

使用:Android:orientation = “vertical”

 

标志位、位或运算,格式如下:

 

< 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>

 

XML中使用:

 

Android:windowSoftInputMode = “stateUnspecified | stateUnchanged | stateHidden”>

 

 

 

属性定义可以指定多种类型:

 

定义:< attr name = “background” format = “reference|color” />

 

使用:android:background = “@drawable/图片ID|#00FF00″

 

 

 

TypedArray

 

Context类的obtainStyledAttributes方法一起使用,作为一个不同类型的数据的容器使用。使用是如:

 

 

 

TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyView);

 

 

 

这句一般是使用在自定义View的构造方法中的,其中attrs是构造方法的形参,而R.styleable.MyView是和attrs.xml相关的。MyViewattrs.xmldeclare-styleablename属性的值。如果这个自定义Viewattrs.xml文件中对应的declare-styleablename属性值为A,那么这里就写R.styleable.A

 

其中包括很多方法,用来获取这个容器中包含的值

 

 

 

·getColor  获取颜色值

 

·getDimension   获取尺寸值

 

 

 

这些方法一般都有这两个参数int index, int defValue。其中index为用来查找属性的检索值。如果实在attrs.xml文件中定义的属性,就是R.styleable.xxxx_yyyyXxxx代表declare-styleablename值,yyyy代表attrname值。

 

defValue代表默认值,即如果在xml文件中没有设置,可以使用默认值来进行设置。

 

 

AttributeSet是一个属性的集合,与一个在XML文件中的标签相联系。如在自定义View中,构造方法中会有一个AttributeSet类型的参数,这个参数就和XML中定义的自定义View相联系的。一般不需要直接使用它。

[转载]android之自定义view 二 - g.hui - 博客园

mikel阅读(930)

[转载]android之自定义view 二 – g.hui – 博客园.

在  android之自定义view 一 中,我们了解了如何自定义一个View。但我们并没有说明如何为自定义的View设置自定义的属性。这一篇中简单介绍一下:

添加自定义属性

步骤:

  1)在res/values下创建attrs.xml

  2)在相关xml布局文件中添加新的命名空间

  3)重写 public View (Context context, AttributeSet attrs)

实例:

  我们还用 android之自定义view 一 中的例子。我们为CustomView1添加颜色、半径两个属性

最终运行结果图:

代码:

  attrs.xml

<!--?xml version="1.0" encoding="utf-8"?-->






XML布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:my="http://schemas.android.com/apk/res/com.example.customview"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/RLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".CustomViewActivity" >

    <com.example.customview.CustomView1
        android:id="@+id/cs"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        my:paintColor="#FFBB00"
        my:radius="50dip" />

</RelativeLayout>

说明:

添加新的命名空间:xmlns:my=”http://schemas.Android.com/apk/res /com.example.customview” 其中my是新命名空间的名字,res/+我们自定义view的包名。在自定义view中通过my:xxx引用。
自定义View类

package com.example.customview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class CustomView1 extends View {

    public float currentX = 46;
    public float currentY = 57;
    private Paint p;
    private float radius;
    public float getRadius() {
        return radius;
    }

    public void setRadius(float radius) {
        this.radius = radius;
    }

    public int getPaintColor() {
        return paintColor;
    }

    public void setPaintColor(int paintColor) {
        this.paintColor = paintColor;
    }

    private int paintColor;

    private void init() {
        p = new Paint();
        p.setColor(Color.GREEN);
    }

    public CustomView1(Context context) {
        super(context);
        System.out.println("---------1-----------");
        init();
    }

    public CustomView1(Context context, AttributeSet attrs) {
        super(context, attrs);
        System.out.println("---------2-----------");
        init();

        TypedArray array = context.obtainStyledAttributes(attrs,
                R.styleable.MyView);
        paintColor = array
                .getColor(R.styleable.MyView_paintColor, 0X000000); 
        radius = array.getDimension(R.styleable.MyView_radius, 20);
        p.setColor(paintColor);
        array.recycle(); 
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawCircle(currentX, currentY, radius, p);
    }

}

说明:

    1.重写双参构造函数,记着在最后调用array.recycle();

     2.为我们新添加的属性设置新的属性字段及getter\setter方法。如本例的:

private float radius;
public float getRadius() {
return radius;
}

public void setRadius(float radius) {
this.radius = radius;
}

public int getPaintColor() {
return paintColor;
}

public void setPaintColor(int paintColor) {
this.paintColor = paintColor;
}

private int paintColor;

这样的话,我们就可以在代码中也可以改变这些新的属性值了。

推荐后续阅读:

http://blog.chinaunix.net/uid-26885609-id-3479675.html

[转载]android之自定义view 一 - g.hui - 博客园

mikel阅读(902)

[转载]android之自定义view 一 – g.hui – 博客园.

概述

Android系统提供的UI组件不足以满足我们的需求时,我们可以自己继承View来设计自己的View。然后,选择重写部分的方法。通常可以被重写的方法如下:

1)构造函数,View有三个构造函数:

  public View (Context context)  当我们通过代码创建view时需要复写此方法。

  public View (Context context, AttributeSet attrs)  当我们通过xml创建view时需要复写此方法。

public View (Context context, AttributeSet attrs, int defStyle)  通过源码我们可以知道其实public View (Context context, AttributeSet attrs) 调用的也是三个参数的构造函数public View (Context context, AttributeSet attrs, int defStyle)。我们一般不需要复写此构造函数

2)回调函数

  onFinishInflate():当应用从XML布局文件加载该组件并用它构建完界面之后,该方法就会被回调。

  onMeasure(int,int):用来检测View及它所包含的子View的大小

  onLayout(boolean,int,int,int):当此View需要分配其子组件的位置、大小时会被回调。

  onSizeChanged(int,int,int,int):当该组件大小被改变时回调

  onDraw(Canvas):当该组件将要绘制时被回调

  onKeyDown(int,KeyEvent):当此View被按下时被回调

  onKeyUp(int,KeyEvent):当此View被松开时被回调

  onTrackballEvent(MotionEvent):当发生轨迹球事件时

  onTouchEvent(MotionEvent):当发生触摸事件时

  onWindowFocusChanged(boolean):当焦点发生改变时

  onAttachedToWindow():当此组件被添加到某个窗口时

  onDetachedFromWindow():当从某个窗口上被分离时

  onWindowVisibilityChanged(int):当包含该组件的窗口的可见性发生改变时触发

步骤

1)继承View(当然也可以选择它的某些子类,根据自己的实际需求选择)

2)重写两个构造函数:上面说过View有三个构造函数,我们只需要复写前两个即可。

注意:其实,不是我们一定要复写那两个构造函数,如果我们只通过代码的方式添加我们自定义的View的话可以只复写单参的构造函数。若我们只通 过XML布局的方式往Activity中添加我们自定义的View时,可以只复写双参的构造函数。但为了防止出错,建议把单参的、双参的都复写了。

3)选择复写部分回调函数,我们根据自己的需求选择复写一些回调函数。一般都 会onDraw(Canvas)方法。

4)当我们完成了上面的步骤后,我们就完成了自定义View。我们可以通过两种方式将它添加到我们的Activity中

a.通过代码方式

b.通过XML方式

具体请看下面的例子代码:

实例

模拟一个跟随手机移动的小球。

package com.example.customview;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;

public class CustomView1 extends View {

public float currentX = 46;
public float currentY = 57;
Paint p;

private void init() {
p = new Paint();
p.setColor(Color.GREEN);
}

public CustomView1(Context context) {
super(context);
System.out.println("---------1-----------");
init();
}

public CustomView1(Context context, AttributeSet attrs) {
super(context, attrs);
System.out.println("---------2-----------");
init();
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawCircle(currentX, currentY, 20, p);
}

}

1)通过代码引入CustomView

package com.example.customview;

import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.RelativeLayout;
import android.app.Activity;

public class CustomViewActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_view);
final CustomView1 cs = new CustomView1(this);
RelativeLayout Rlayout = (RelativeLayout) findViewById(R.id.RLayout);
Rlayout.addView(cs);
cs.setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
cs.currentX = event.getX();
cs.currentY = event.getY();
cs.invalidate();
return true;
}
});
}
}

此时我们的xml布局文件如下:


<!--
<com.example.customview.CustomView1         android:id="@+id/cs"         android:layout_width="fill_parent"         android:layout_height="fill_parent" />
-->

2)通过XML引入CustomView

package com.example.customview;

import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.app.Activity;

public class CustomViewActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_view);
final CustomView1 cs = (CustomView1) findViewById(R.id.cs);
cs.setOnTouchListener(new OnTouchListener() {

@Override
public boolean onTouch(View v, MotionEvent event) {
cs.currentX = event.getX();
cs.currentY = event.getY();
cs.invalidate();
return true;
}
});
}
}

此时我们的xml布局文件如下:


&nbsp;

大家可以注意一下Logcat的输出,对比一下,当我们选择这两种不同的引入自定义View方式时构造函数的调用情况。

PS:我们知道View的子类有许多独特的属性,比如TextView的text属性,color属性等。那么我们能不能给我们自定义的View也添加一些自定义的属性呢?答案当然是可以的,我们放到下一讲中来说。

android之自定义view 二

[转载]Android onMeasure方法介绍-Android新手入门-eoe Android开发者社区_Android开发论坛 - Powered by Discuz!

mikel阅读(817)

[转载]Android onMeasure方法介绍-Android新手入门-eoe Android开发者社区_Android开发论坛 – Powered by Discuz!.

onMeasure方法在控件的父元素正要放置它的子控件时调用.它会问一个问题,“你想要用多大地方啊?”,然后传入两个参数——widthMeasureSpec和heightMeasureSpec.

它们指明控件可获得的空间以及关于这个空间描述的元数据.
比返回一个结果要好的方法是你传递View的高度和宽度到setMeasuredDimension方法里.

接下来的代码片段给出了如何重写onMeasure.注意,调用的本地空方法是来计算高度和宽度的.它们会译解widthHeightSpec和heightMeasureSpec值,并计算出合适的高度和宽度值.


@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int measuredHeight = measureHeight(heightMeasureSpec);
int measuredWidth = measureWidth(widthMeasureSpec);
setMeasuredDimension(measuredHeight, measuredWidth);
}

private int measureHeight(int measureSpec) {

 
// Return measured widget height.
}

private int measureWidth(int measureSpec) {

// Return measured widget width.
}

边界参数——widthMeasureSpec和heightMeasureSpec ,效率的原因以整数的方式传入。在它们使用之前,首先要做的是使用MeasureSpec类的静态方法getMode和getSize来译解,如下面的片段所示:

int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

依据specMode的值,如果是AT_MOST,specSize 代表的是最大可获得的空间;如果是EXACTLY,specSize 代表的是精确的尺寸;如果是UNSPECIFIED,对于控件尺寸来说,没有任何参考意义。
  当以EXACT方式标记测量尺寸,父元素会坚持在一个指定的精确尺寸区域放置View。在父元素问子元素要多大空间时,AT_MOST指示者会说给我最大的范围。在很多情况下,你得到的值都是相同的。
  在两种情况下,你必须绝对的处理这些限制。在一些情况下,它可能会返回超出这些限制的尺寸,在这种情况下,你可以让父元素选择如何对待超出的View,使用裁剪还是滚动等技术。

  接下来的框架代码给出了处理View测量的典型实现:

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int measuredHeight = measureHeight(heightMeasureSpec);

int measuredWidth = measureWidth(widthMeasureSpec);

setMeasuredDimension(measuredHeight, measuredWidth);

}

private int measureHeight(int measureSpec) {

int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

// Default size if no limits are specified.

int result = 500;
if (specMode == MeasureSpec.AT_MOST){

// Calculate the ideal size of your
// control within this maximum size.
// If your control fills the available
// space return the outer bound.

result = specSize;
} 
else if (specMode == MeasureSpec.EXACTLY){

// If your control can fit within these bounds return that value.
result = specSize;
}

return result;
}

private int measureWidth(int measureSpec) {
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);

// Default size if no limits are specified.
int result = 500;
if (specMode == MeasureSpec.AT_MOST){
// Calculate the ideal size of your control
// within this maximum size.
// If your control fills the available space
// return the outer bound.
result = specSize;
} 

else if (specMode == MeasureSpec.EXACTLY){
// If your control can fit within these bounds return that value.

result = specSize;
}

return result;
}

[转载]Android Developers:创建自定义视图类 - 小彭 的技术专栏 - 博客频道 - CSDN.NET

mikel阅读(796)

[转载]Android Developers:创建自定义视图类 – 小彭 的技术专栏 – 博客频道 – CSDN.NET.

设计良好的自定义视图就像其它任何精心设计的类。它通过一个容易使用的接口封装了一个组特定的方法。它高效的使用CPU和内存,等等。除了是一个精心设计的类,然而,自定义视图应该: 

  • 遵守Android规范 

  • 提供在Android XML布局中工作的自定义属性。 

  • 发送可访问的事件。 

  • 兼容多个Android平台。 

 

Android框架提供了一组基本类和XML标签,来帮助你创建符合所有要求的视图。这节课程讨论如何使用Android框架来创建视图类的核心功能。 

 

子类化视图 

——————————————————————————————————————————————————————————————

在Android框架中被定义的所有视图类继承View。你的自定义视图也可以直接继承View,或者你能通过继承一个已经存在的视图子类来节省时间,例如Button。 

 

为了允许Android Developer Tool和你的视图交互,你必须至少提供一个构造方法,它使用Context和AttributeSet对象作为参数。这个构造方法允许布局编辑器来创造和编辑一个你的视图实例。 

class PieChart extends View {
public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
}
}

定义自定义属性 

——————————————————————————————————————————————————————————————

向你的用户界面添加一个内嵌的视图,你使用一个XML元素中指定它,并且使用元素属性控制它的显示和行为。完全自定义视图也能通过XML添加和样式化。为了在你的自定义视图中使用这种方式,你必须: 

  • 在<declare-styleable>资源元素中定义你的视图的自定义属性。 

  • 在你的XML布局中指定属性的值。 

  • 在运行时获取属性值。 

  • 将获取的属性值应用到你的视图中。 

 

这节讨论如何定义自定义属性和指定它们的值。下一节是关于在运行时获取和应用这些值。 

 

为定义自定义属性,向你的项目中添加<declare-styleable>资源,通常将这些资源放入/res/values/attrs.xml文件中。这里是attrs.xml文件的一个例子: 


这 段代码声明两个自定义属性,shotText和labelPosition,它属于一个被命名为PieChart的可样式化实体。按照惯例,这个可样式化 的实体的名字,和定义自定义视图类的名字相同。虽然不是非得遵守此约定,大多数程序员基于这种命名约定提供完整的描述。 

 

一 旦你定义了自定义属性,你也能和内置属性一样,在XML布局文件中使用它们。唯一的不同是你的自定义属性属于一个不同的命名空间。代替属于 http://schemas.android.com/apk/res/android命名空间,它们属于http: //schemas.android.com/apk/res/android/[your package name],例如,这里演示了如何使用为PieChar定义的属性: 

<!--?xml version="1.0" encoding="utf-8"?-->

为 了避免重复长命名空间URI,这个例子使用xmlns指令。这个指令分配别名custom给http://schemas.android.com /apk/res/com.example.customviews命名空间。你可以使用任何你想分配给你的命名空间的别名。 

 

注 意XML标识的名称将自定义视图添加到布局中,它是自定义视图类的完整描述名。如果你的视图类是一个内部类,你必须使用外部类的名称进一步限定它。例如 PieChar类有一个被命名为PiaView的内部类。为了使用这个类的属性,你应该使用 com.example.customviews.charting.PiaChar$PiaView标签。 

 

使用自定义属性 

——————————————————————————————————————

当一个视图在一个XML布局中被创建,在这个XML标签中的所有属性都从资源束中读取,并作一个AttributeSet被传递到这个视图的构造方法。虽然可以直接从AttributeSet中读取值,但是这样做有一些缺点: 

  • 在属性值内的资源引用没有被解析。 

  • 类型没有被应用。 

 

相反,传递AttributeSet给obtainStyledAttribute()方法,这个方法返回一个值数组TypedArray,它已经被引用和类型化。 

 

Android 资源编译器为了你更方便的调用obtainStyledAttribute()方法,做了许多工作。对于在res目录下的每个<declare- styleable>资源,在生成的R.java文件定义了一个属性id数组和一个常量集合,它定义了在这个数组中的每个属性的索引。你使用这个预 定义的常量来从TypedArray中读取对应属性。下面是PieChar类如何读取它的属性: 

public PieChart(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.getTheme().obtainStyledAttributes(
attrs,
R.styleable.PieChart,
0, 0);

try {
mShowText = a.getBoolean(R.styleable.PieChart_showText, false);
mTextPos = a.getInteger(R.styleable.PieChart_labelPosition, 0);
} finally {
a.recycle();
}
}

注意TypedArray对象是一个共享资源,并且在使用后必须被回收。 

 

添加属性和事件 

———————————————————————————————————————— 

属性是控制视图的行为和显示的一种强有力的方式,但是它们仅仅在视图被初始化的时候被读取。为了提供动态行为,为每个自定义属性提供属性的一对getter和setter方法。下面的代码段显示PieChart如何暴露被称为showText的属性。 

public boolean isShowText() {
return mShowText;
}

public void setShowText(boolean showText) {
mShowText = showText;
invalidate();
requestLayout();
}

注 意setShowText调用invalidate()和requestLayout()。这些调用对于确保视图的行为可靠是至关重要的。在改变任何可能 改它的显示的属性之后,你必须使视图无效,以至于系统知道它需要被重绘。另一方面,如果一个可能影响这个视图的大小或者形状的属性改变,你需要请求一个新 的布局。忘记了调用这个方法会导致难以寻找的Bug。 

 

自定义视图也应该支持事件监听来沟通重要事件。例如,PieChar暴露一个被称为onCurrentItemChanged的自定义事件来通知来通知监听器,用户已经旋转了PieChar类聚焦一个新的Pie Slice。 

 

忘记暴露属性和事件是很容易的,尤其是当你这个自定义视图的唯一用户的时候。花一些事件细心定义你的视图界面来减少将来维护的开销。遵循好的做法就是,总是暴露任何影响你的自定义视图的外表和行为可见性的属性。 

 

可访问性设计 

———————————————————————————————————————— 

你的自定义视图应该支持最广泛的用户,这包括无法看见或使用触摸屏的残障用户。为了支持残障用户,你应该: 

  • 使用android:contentDescription属性标注你的输入框。 

  • 在适当的时候,通过调用setAccessibilityEvent()发送可访问事件。 

  • 支持备用控制器,例如D-pad和轨迹球。 

 

更多关于创建可访问视图的信息,查阅在Android Developers Guide中Making Application Accessible。