[Android自定義控制元件] Android自定義控制元件
阿新 • • 發佈:2018-11-10
轉載自: http://blog.163.com/[email protected]/blog/static/103242241201382210910473/
開發自定義控制元件的步驟: 1、瞭解View的工作原理 2、 編寫繼承自View的子類 3、 為自定義View類增加屬性 4、 繪製控制元件 5、 響應使用者訊息 6 、自定義回撥函式 一、View結構原理 Android系統的檢視結構的設計也採用了組合模式,即View作為所有圖形的基類,Viewgroup對View繼承擴充套件為檢視容器類。 View定義了繪圖的基本操作 基本操作由三個函式完成:measure()、layout()、draw(),其內部又分別包含了onMeasure()、onLayout()、onDraw()三個子方法。具體操作如下: 1、measure操作 measure操作主要用於計算檢視的大小,即檢視的寬度和長度。在view中定義為final型別,要求子類不能修改。measure()函式中又會呼叫下面的函式: (1)onMeasure(),檢視大小的將在這裡最終確定,也就是說measure只是對onMeasure的一個包裝,子類可以覆寫onMeasure()方法實現自己的計算檢視大小的方式,並通過setMeasuredDimension(width, height)儲存計算結果。 2、layout操作 layout操作用於設定檢視在螢幕中顯示的位置。在view中定義為final型別,要求子類不能修改。layout()函式中有兩個基本操作: (1)setFrame(l,t,r,b),l,t,r,b即子檢視在父檢視中的具體位置,該函式用於將這些引數儲存起來; (2)onLayout(),在View中這個函式什麼都不會做,提供該函式主要是為viewGroup型別佈局子檢視用的; 3、draw操作 draw操作利用前兩部得到的引數,將檢視顯示在螢幕上,到這裡也就完成了整個的檢視繪製工作。子類也不應該修改該方法,因為其內部定義了繪圖的基本操作: (1)繪製背景; (2)如果要檢視顯示漸變框,這裡會做一些準備工作; (3)繪製檢視本身,即呼叫onDraw()函式。在view中onDraw()是個空函式,也就是說具體的檢視都要覆寫該函式來實現自己的顯示(比如TextView在這裡實現了繪製文字的過程)。而對於ViewGroup則不需要實現該函式,因為作為容器是“沒有內容“的,其包含了多個子view,而子View已經實現了自己的繪製方法,因此只需要告訴子view繪製自己就可以了,也就是下面的dispatchDraw()方法; (4)繪製子檢視,即dispatchDraw()函式。在view中這是個空函式,具體的檢視不需要實現該方法,它是專門為容器類準備的,也就是容器類必須實現該方法; (5)如果需要(應用程式呼叫了setVerticalFadingEdge或者setHorizontalFadingEdge),開始繪製漸變框; (6)繪製滾動條; 從上面可以看出自定義View需要最少覆寫onMeasure()和onDraw()兩個方法。 二、View類的構造方法public class MyView extends View { private String mtext; private int msrc; public MyView(Context context) {super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); int resourceId = 0; int textId = attrs.getAttributeResourceValue(null, "Text",0); int srcId = attrs.getAttributeResourceValue(null, "Src", 0); mtext = context.getResources().getText(textId).toString(); msrc = srcId; } @Override protected void onDraw(Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.RED); InputStream is = getResources().openRawResource(msrc); Bitmap mBitmap = BitmapFactory.decodeStream(is); int bh = mBitmap.getHeight(); int bw = mBitmap.getWidth(); canvas.drawBitmap(mBitmap, 0,0, paint); //canvas.drawCircle(40, 90, 15, paint); canvas.drawText(mtext, bw/2, 30, paint); } }
佈局檔案:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <com.example.myimageview2.MyView android:id="@+id/myView1" android:layout_width="wrap_content" android:layout_height="wrap_content" Text="@string/hello_world" Src="@drawable/xh"/> </LinearLayout>
屬性Text, Src在自定義View類的構造方法中讀取。
2)通過XML為View註冊屬性。與Android提供的標準屬性寫法一樣。 案例: 實現一個帶文字說明的ImageView (ImageView+TextView組合,文字說明,可在佈局檔案中設定位置)
public class MyImageView extends LinearLayout { public MyImageView(Context context) { super(context); } public MyImageView(Context context, AttributeSet attrs) { super(context, attrs); int resourceId = -1; TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyImageView); ImageView iv = new ImageView(context); TextView tv = new TextView(context); int N = typedArray.getIndexCount(); for (int i = 0; i < N; i++) { int attr = typedArray.getIndex(i); switch (attr) { case R.styleable.MyImageView_Oriental: resourceId = typedArray.getInt( R.styleable.MyImageView_Oriental, 0); this.setOrientation(resourceId == 1 ? LinearLayout.HORIZONTAL : LinearLayout.VERTICAL); break; case R.styleable.MyImageView_Text: resourceId = typedArray.getResourceId( R.styleable.MyImageView_Text, 0); tv.setText(resourceId > 0 ? typedArray.getResources().getText( resourceId) : typedArray .getString(R.styleable.MyImageView_Text)); break; case R.styleable.MyImageView_Src: resourceId = typedArray.getResourceId( R.styleable.MyImageView_Src, 0); iv.setImageResource(resourceId > 0 ?resourceId:R.drawable.ic_launcher); break; } } addView(iv); addView(tv); typedArray.recycle(); } }
attrs.xml進行屬性宣告, 檔案放在values目錄下
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyImageView"> <attr name="Text" format="reference|string"></attr> <attr name="Oriental" > <enum name="Horizontal" value="1"></enum> <enum name="Vertical" value="0"></enum> </attr> <attr name="Src" format="reference|integer"></attr> </declare-styleable> </resources>MainActivity的佈局檔案:先定義名稱空間 xmlns:uview="http://schemas.android.com/apk/res/com.example.myimageview2" (com.example.myimageview2為你 在manifest中定義的包名) 然後可以像使用系統的屬性一樣使用:uview:Oriental="Vertical"
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:uview="http://schemas.android.com/apk/res/com.example.myimageview2" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".MainActivity" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/hello_world" /> <com.example.myimageview2.MyImageView android:id="@+id/myImageView1" android:layout_width="wrap_content" android:layout_height="wrap_content" uview:Text="這是一個圖片說明" uview:Src="@drawable/tw" uview:Oriental="Vertical"> </com.example.myimageview2.MyImageView> </LinearLayout>四、控制元件繪製 onDraw() 五、
六:自定義View的方法
onFinishInflate() 回撥方法,當應用從XML載入該元件並用它構建介面之後呼叫的方法 onMeasure() 檢測View元件及其子元件的大小 onLayout() 當該元件需要分配其子元件的位置、大小時 onSizeChange() 當該元件的大小被改變時 onDraw() 當元件將要繪製它的內容時 onKeyDown 當按下某個鍵盤時 onKeyUp 當鬆開某個鍵盤時 onTrackballEvent 當發生軌跡球事件時 onTouchEvent 當發生觸屏事件時 onWindowFocusChanged(boolean) 當該元件得到、失去焦點時 onAtrrachedToWindow() 當把該元件放入到某個視窗時 onDetachedFromWindow() 當把該元件從某個視窗上分離時觸發的方法 onWindowVisibilityChanged(int): 當包含該元件的視窗的可見性發生改變時觸發的方法