自定义view

首先看看api  http://developer.android.com/reference/android/view/View.html

这个是和用户交互的基本组件。一个view占一个矩形框在屏幕上,并且负责绘制和事件响应。ViewGroup这个子类是基本类用于layouts,这些不可见的容器包含了views(或者其他的viewgroup)并且定义了他们的布局属性。

使用Views

1,set Properties,如在xml中的layout属性等。

2,set focus

3,set up listeners 

4, set visibility

要定制我们自己的UI控件,需要重载View类中的一些方法,以下表格列出View提供出来的,供重载的方法,这些方法不必都要重载,但至少要实现onDraw(android.graphics.Canvas)方法。

类别 方法 描述
Creation Constructors  
onFinishInflate() 当View和它的所有子对象从XML中导入之后,调用此方法
Layout onMeasure(int, int) View会调用此方法,来确认自己及所有子对象的大小
onLayout(boolean, int, int, int, int, int, int) 当View要为所有子对象分配大小和位置时,调用此方法
onSizeChanged(int, int, int, int) 当View大小改变时,调用此方法
Drawing onDraw(Canvas) 当View要绘制它的内容时,调用此方法
Event processing onKeyDown(int, KeyEvent) 当一个新的按键事件发生时,调用此方法
onKeyUp(int, KeyEvent) 当一个按键释放事件发生时,调用此方法
onMotionEvent(MotionEvent) 当一个动作事件(如触摸)发生时,调用此方法
Focus onFocusChanged(boolean, int) 当View获得或失去焦点时,调用此方法
Attaching onAttachedToWindow() 当View附加到一个窗体上时,调用此方法
onDetachedFromWindow() 当View离开它的窗体时,调用此方法

当你为一个 activty 添加一个可见的 view, 并且运行这个activty时,android通常情况下会自动按照下列顺序来触发view的相关事件

onAttachedToWindow
onMeasure
onSizeChanged
onLayout
onDraw  



每一个LAYOUT文件必须只有一个根元素,或者是VIEW,或者是VIEWGROUP 

每一个VIEWGROUP都实现了自己的继承自ViewGroup.LayoutParams 的特定类

比如一个LINEARLAYOUT包含了一个RELATIVELAYOUT和其他几个VIEW,RELATIVELAYOUT又包含了几个VIEW

那么定义RELATIVELAYOUT的LAYOUT参数应该用LINEARLAYOUT.LAYOUTPARAMS,而RELATIVELAYOUT中的

孩子应该用RELAVTIVELAYOUT.LAYOUTPARAMS,这里的类别一定要注意,用错了会报类型转换错误的。

 

一个VIEW实际有俩组宽高。

第一组是measured width and measured height这俩个尺寸定了它想在他的父亲里多大。 The measured dimensions can be obtained by calling getMeasuredWidth() and getMeasuredHeight() .

第二组 width and height也被称作 drawing width and drawing height, 这组定义了VIEW在屏幕上的实际尺寸。getWidth() and getHeight() .

这俩组可能不同。

测量VIEW尺寸时,也要考虑他的PADDING Padding can be used to offset the content of the view by a specific amount of pixels. For instance, a left padding of 2 will push the view's content by 2 pixels to the right of the left edge. 

但是VIEW没有提供MARGIN,但是VIEWGROUP提供了。


 

public static class MyEditText extends EditText

This is a cleaner way to create inner classes if they do not need access to state from the outer class, keeps the generated class small, and allows it to be used easily from other classes

(自定义的VIEW如果inflated from an XML layout file,那么应该是一个带参数的构造器(Context context, AttributeSet attrs)被调用

在你的构造函数里要记住先调用SUPER)

怎么用定制的VIEW呢,这里有一个例子

<view
class="com.android.notepad.NoteEditor$MyEditText" 
id="@+id/note"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:drawable/empty"
android:padding="10dip"
android:scrollbars="vertical"
android:fadingEdge="vertical" />


Drawing 绘制要用到invalidate(),而且一定要在UI线程中进行。


 How Android Draws Views

当一个ACT接受到焦点时,将从这个LAYOUT的树层次的根节点开始画

沿着树,从上到下,每一个GROUP都负责请求他的孩子去画他自己(通过DRAW)

父亲总是最先画,然后是兄弟。

 

画LAYOUT是分俩个过程:MEASURE,LAYOUT

MEASURE过程也是一个自顶向下的过程implemented in measure(int, int)

每一个VIEW推动尺寸规范在这个递归过程中,在MEASURE结束时候,每一个VIEW存储了他的MEASUREMENTS。

LAYOUT也同样,自顶向下。在这个过程中,每一个PARENT负责定位他的孩子用在MEASURE过程计算得到的尺寸。

When a View's measure() method returns, its getMeasuredWidth() and getMeasuredHeight() values must be set, along with those for all of that View's descendants

 

MEASURE过程用俩个类去通信和DIMENSIONS,

. The View.MeasureSpec class is used by Views to tell their parents how they want to be measured and positioned. The base LayoutParams class just describes how big the View wants to be for both width and height.

For each dimension, it can specify one of:

  • an exact number
  • FILL_PARENT , which means the View wants to be as big as its parent (minus padding)
  • WRAP_CONTENT , which means that the View wants to be just big enough to enclose its content (plus padding).

MeasureSpecs are used to push requirements down the tree from parent to child. A MeasureSpec can be in one of three modes:

 

当ACT开始呈现他的内容时,首先从根节点VIEWGROUP开始

画VIEWGROUP就是用MEASURE,LAYOUT去测量定位他的子VIEW,这个过程完了

VIEWGROUP会被调用

dispatchDraw,然后轮次调用VIEW的DRAW。

VIEWGROUP:ONMEASURE-->ONLAYOUT-->DISPATCHDRAW-->  VIEW'S ONDRAW.

 

viewgroup设了一个标志位防止自己被画,因为LAYOUT本身不需要画东西,你也能设置这个标志位

所以继承iewgroup实现ONDRAW,没有用。

.

Usually, a ViewGroup calls measure() on its children from its own onMeasure() method. Similarly, you must call the layout() method of the children from onLayout().

Note that for performance reasons, onMeasure() is not necessarily call every time measure() is called. The view system tries to be smart and will invoke onMeasure() only when a View might have changed its bounds. Also, onLayout() is executed by layout() only when the bounds have changed. When onMeasure() is called on a View, onLayout() will also be called.

Last but not least, drawing is a bit different. At drawing time, ViewGroup will enter the draw() method and instead of going through onDraw(), it will invoke dispatchDraw(), which will in turn call draw() on every child.

 


下面就说一下自定义view属性的问题

先看个例子:

 在res/values 文件下定义一个attrs.xml 文件.代码如下:
<?xml version="1.0" encoding="utf-8"?> 
<resources>
    <declare-styleable name="MyView"> 
        <attr name="textColor" format="color" /> //这里format为类别,也可以由format="reference" 引用类型等
       <attr name="textSize" format="dimension" />
    </declare-styleable> 
</resources> 


   
   
   
   
我们在MyView.java 代码修改如下,其中下面的构造方法是重点,我们获取定义的属性我们R.sytleable.MyView_textColor, 获取方法中后面通常设定默认值(float textSize = a.getDimension(R.styleable.MyView_textSize, 36 ); ), 防止我们在xml 文件中没有定义.从而使用默认值!
获取,MyView 就是定义在<declare-styleable name="MyView "></declare-styleable> 里的名字,获取里面属性用 名字_ 属性 连接起来就可以.TypedArray 通常最后调用 .recycle() 方法,为了保持以后使用该属性一致性!
public MyView(Context context,AttributeSet attrs)    
{     
       super(context,attrs);     
       mPaint = new Paint();                 
       TypedArray a = context.obtainStyledAttributes(attrs,    
                R.styleable.MyView);    
       int textColor = a.getColor(R.styleable.MyView_textColor,    
                0XFFFFFFFF);    
       float textSize = a.getDimension(R.styleable.MyView_textSize, 36);     
       mPaint.setTextSize(textSize);    
       mPaint.setColor(textColor);     
       a.recycle();     
} 

三、将我们自定义的MyView 加入布局main.xml 文件中,平且使用自定义属性,自定义属性必须加上:
xmlns:test =" http://schemas.android.com/apk/res/com.android.tutor "蓝色 是自定义属性的前缀,红色 是我们包名,这个命名空间的test是可以随意起的,res/后面是包名
main.xml 全部代码如下:
< ?xml 
version="1.0" encoding="utf-8"?> 
< LinearLayout 
xmlns:android="http://schemas.android.com/apk/res/android" 
xmlns:test="http://schemas.android.com/apk/res/com.android.tutor" 
android:orientation="vertical" 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
> 
< TextView 
android:layout_width="fill_parent" 
android:layout_height="wrap_content" 
android:text="@string/hello" 
/> 
< com.android.tutor.MyView 
android:layout_width="fill_parent" 
android:layout_height="fill_parent" 
test:textSize="20px" 
test:textColor="#fff" 
/> 
< /LinearLayout> 










而自定义layout的复杂度就要高,如果继承了一个LAYOUT,你不需要覆盖onDraw() and onMeasure() 因为他们本身默认行为足够。也可以覆盖,这样可以有更多的自定义空间,Android会对Layou和View嵌套组成的这棵树进行2次遍历,一次是measure调用,用来确定Layout或者View的大小;一次是layout调用,用来确定Layout或者view的位置。

onMeasure(),调用子类的measureChild(),或者child.measure()

onLayout(),调用子类的layout,如child.layout();

dispatchDraw(),调用drawChild(canvas, childxxx, drawingTime);

在事件响应上,先调用事件的分配

onInterceptTouchEvent()

在子类响应剩下的地方再响应 onTouchEvent()



参考:

http://weizhulin.blog.51cto.com/1556324/311453

http://blog.csdn.net/G_rrrr/article/details/4929849

 http://yarin.iteye.com/blog/558123

你可能感兴趣的:(自定义view)