电竞比分网-中国电竞赛事及体育赛事平台

分享

android中

 點點滴滴 2012-09-14

android中-自定義View對象(二)

熱度 2已有 815 次閱讀2011-8-24 23:08 |個人分類:android 學(xué)習(xí)篇

上一篇文 章我們了解android.view.View的原理而且制作了一個功能簡單的效果:點擊屏幕隨機在點擊的位置生成一個半徑是1-10的圓圈。我們是使用 一個自定義View對象完成的,但是它對View所做的修改有限,只是修改了onDraw和onTouchEvent兩個方法,這樣不能涵蓋整個自定義 View所能完成的功能。所以這里我們再補充下自定義View相關(guān)的一些內(nèi)容。

在SDK文檔中描述了View創(chuàng)建的幾個主要過程:

Category Methods Description
Creation Constructors There is a form of the constructor that are called when the view is created from code and a form that is called when the view is inflated from a layout file. The second form should parse and apply any attributes defined in the layout file.
onFinishInflate() Called after a view and all of its children has been inflated from XML.
Layout onMeasure(int, int) Called to determine the size requirements for this view and all of its children.
onLayout(boolean, int, int, int, int) Called when this view should assign a size and position to all of its children.
onSizeChanged(int, int, int, int) Called when the size of this view has changed.
Drawing onDraw(Canvas) Called when the view should render its content.
Event processing onKeyDown(int, KeyEvent) Called when a new key event occurs.
onKeyUp(int, KeyEvent) Called when a key up event occurs.
onTrackballEvent(MotionEvent) Called when a trackball motion event occurs.
onTouchEvent(MotionEvent) Called when a touch screen motion event occurs.
Focus onFocusChanged(boolean, int, Rect) Called when the view gains or loses focus.
onWindowFocusChanged(boolean) Called when the window containing the view gains or loses focus.
Attaching onAttachedToWindow() Called when the view is attached to a window.
onDetachedFromWindow() Called when the view is detached from its window.
onWindowVisibilityChanged(int) Called when the visibility of the window containing the view has changed.

上面的列表很詳細(xì)的將View視圖從被創(chuàng)建(Creation)、構(gòu)建布局(layout)執(zhí)行繪圖(drawing)、事件處理(Event Processing)、焦點處理(Focus)被加載到Activity容器(Attaching)的 幾個過程所執(zhí)行的生命周期方法都列出來了。我們可以很清晰的看到它的整個處理過程,當(dāng)你需要自定義一個View的時候,你可以根據(jù)你自定義的View所要 完成的功能選擇性的重寫上面的幾個方法。以上一篇的畫實心圓為例,我們重寫了它的被創(chuàng)建、執(zhí)行繪圖和事件處理三個方法。

下面我們編寫一個自定義View對象,讓其可以通過xml文件配置一些自定義屬性,可手動設(shè)置大小,并在繪圖時添加一些自定義的圖形。

首先我們編寫一個類,繼承android.view.View對象,重寫里面的構(gòu)造方法(在構(gòu)造方法里獲取從xml布局文件中設(shè)置的自定義屬性并執(zhí)行初始化)、onMeasure方法(實現(xiàn)可以在xml配置文件中定義大小)和onDraw方法(繪制視圖內(nèi)容,同時在視圖對象的中間添加一條橫線),CustomLabelView.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
package com.android777.demo.uicontroller.view;
  
import com.android777.demo.uicontroller.R;
  
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 CustomLabelView extends View {
  
    Paint paint;
    String mText;
  
    public CustomLabelView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }
  
    //在xml布局文件中定義這個View時會調(diào)用這個構(gòu)造方法
    public CustomLabelView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
  
        //獲取xml文件里定義的View的所有屬性
        TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.CustomLabelView);
  
        //獲取text屬性
        CharSequence s = a.getString(R.styleable.CustomLabelView_text);
        //獲取backgroundColor屬性
        int color = a.getColor(R.styleable.CustomLabelView_backgroundColor, Color.BLACK);
        //填充text屬性值
        if(s != null){
            setText(s.toString());
        }
        //設(shè)置背景顏色為 xml文件中定義的backgroundColor
        setBackgroundColor(color);
  
        //回收資源
        a.recycle();
  
    }
  
    public CustomLabelView(Context context) {
        super(context);
        init();
    }
  
    /**
     *
     * @param s 設(shè)置顯示的文本內(nèi)容
     *
     * 當(dāng)設(shè)置顯示文本內(nèi)容后,我們將要顯示的內(nèi)容保存到View對象中,然后執(zhí)行
     * invalidate讓View視圖更新顯示畫面。
     *
     */
    public void setText(String s){
        mText = s;
        requestLayout(); //計算尺寸
        invalidate(); //重新繪制視圖對象
    }
  
    private void init(){
  
        paint = new Paint();
        paint.setColor(Color.WHITE);
        paint.setTextSize(16);
        paint.setAntiAlias(true);
  
    }
  
    //重寫這個方法實現(xiàn)能在xml文件中定義視圖大小
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
  
        //定義最終確定的寬度和高度
        int width,height;
  
        //如果在xml文件中是明確定義視圖大小,那么就使用明確定義的值
        if(widthSpecMode == MeasureSpec.EXACTLY){
            width = widthSpecSize;
        }else{
            //否則測量需要顯示的內(nèi)容所占的寬度(就是分配視圖占用的寬度)
            width = (int) paint.measureText(mText);
  
            //如果有定義一個最大寬度, 那么視圖分配的寬度不得超過最大寬度
            if(widthSpecMode == MeasureSpec.AT_MOST){
                width = Math.min(width, widthSpecSize);
            }
        }
  
        //下面測量高度的方式跟寬度差不多
        if(heightSpecMode == MeasureSpec.EXACTLY){
            height = heightSpecSize;
        }else{
            height = (int) (-paint.ascent() + paint.descent());
  
            if(heightSpecMode == MeasureSpec.AT_MOST){
                height = Math.min(height, heightSpecSize);
            }
        }
  
        //這個一定要調(diào)用,  告訴系統(tǒng)測量的最終結(jié)果 需要的寬度是width 高度是height
        setMeasuredDimension(width, height);
  
    }
  
    @Override
    protected void onDraw(Canvas canvas) {
  
        //這個是自定義添加的操作,在視圖繪制的區(qū)域中間畫一條橫線
        canvas.drawLine(0, getMeasuredHeight()/2, getMeasuredWidth(), getMeasuredHeight() /2, paint);
        //繪制視圖的內(nèi)容
        canvas.drawText(mText, 0, -paint.ascent(), paint);
  
    }
  
}

上面代碼的一個難點是在于如何測量文字所占的尺寸,文字占用的寬度比較容易計算,但是高度就比較復(fù)雜,因為各個字體使用的間距不一樣。我們在所有三 個構(gòu)造方法中執(zhí)行初始化代碼,初始化代碼其實沒做什么,只是定義了一個畫筆對象,它的顏色是白色的,文字大小是16。這邊有三個構(gòu)造方法,它們分別對應(yīng)的 功能是不一樣的:

CustomLabelView(Context) :這個構(gòu)造方法是當(dāng)程序中動態(tài)創(chuàng)建CustomLabelView這個對象時調(diào)用的。

CustomLabelView(Context,AttributeSet):這個構(gòu)造方法是當(dāng)你使用xml布局文件加載視圖對象時系統(tǒng)會調(diào)用的構(gòu)造方法,所以你可以在這個構(gòu)造方法里通過AttributeSet這個參數(shù)獲得在xml中對這個視圖對象定義的全部xml屬性。

CustomLabelView(Context,AttributeSet,int):這個構(gòu)造方法用得比較少,詳細(xì)用法我也不是很清楚。

我們在第二個構(gòu)造方法中獲取自定義的屬性,然后對View對象配置這些屬性,在這個步驟前記得要先定義這些可配置的屬性,否則就會報錯,具體是定義在res\values\attrs.xml文件中:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<resources>
  
    <declare-styleable name="CustomLabelView">
        <attr name="text" format="string" />
        <attr name="backgroundColor" format="color" />
    </declare-styleable>
</resources>

這樣你就可以在代碼中引 用:R.styleable.CustomLabelView,R.styleable.CustomLabelView_text和 R.styleable.CustomLabelView_backgroundColor屬性了。然后我們重寫了onMeasure方法,在里面做了一 些根據(jù)xml的配置測量View內(nèi)部內(nèi)容所占的尺寸的測量工作,最后調(diào)用setMeasureDimension通知系統(tǒng)測量的最終結(jié)果。

然后重寫onDraw方法,在這里我們添加了一個自己定義的步驟:在視圖所占區(qū)域的中間畫一條線,最后打出視圖中的內(nèi)容。

布局xml文件,res\layout\custom_view.xml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas./apk/res/android"
  xmlns:app="http://schemas./apk/res/com.android777.demo.uicontroller"
  android:orientation="vertical"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  
  <com.android777.demo.uicontroller.view.CustomLabelView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        app:text="第一個LabelView"
        app:backgroundColor="#7f00"
  />
  
  <com.android777.demo.uicontroller.view.CustomLabelView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:text="第二個LabelView"
        app:backgroundColor="#77ffff00"
  />
  
  <com.android777.demo.uicontroller.view.CustomLabelView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        app:text="第三個LabelView"
        app:backgroundColor="#2ff0"
  />
  
</LinearLayout>

在布局文件中我們創(chuàng)建一個LinearLayout容器,按照縱向排列放了3個自定義的視圖對象,這邊使用自定義視圖對象的方式是將xml的節(jié)點名 字換成你自定義View的類全路徑(包名+類名),然后就可以在節(jié)點的屬性中配置View的屬性。因為上面我們有用到自定義的屬性,所以需要自己創(chuàng)建一個 xml名稱空間,這個名稱空間的規(guī)范是:

1
xmlns:xxx="http://schemas./apk/res/yyy"

其中xxx就是下面要引用到的名稱空間,yyy一定要是項目的包名(剛創(chuàng)建Android項目時填入的包名,也可以查看 AndroidMenifest.xml文件中menifest根節(jié)點中的package屬性,就是這個屬性值)。因為我項目的包名 是:com.android777.demo.uicontroller,所以我的xml名稱空間設(shè)置為:

1
xmlns:app="http://schemas./apk/res/com.android777.demo.uicontroller"

然后我在自定義的View節(jié)點中,使用這個名稱空間配置在res\values\attrs.xml中定義的text和 backgroundColor屬性。這樣整個自定義View步驟就完成了,最后只需要一個Activity來查看效 果,CustomLabelViewActivity.java:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.android777.demo.uicontroller.view;
  
import com.android777.demo.uicontroller.R;
  
import android.app.Activity;
import android.os.Bundle;
  
public class CustomLabelViewActivity extends Activity {
  
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
  
        setContentView(R.layout.custom_view);
  
    }
  
}

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多