文章來自:Android程序員日記
作者:賢榆的魚
參考閱讀時間:5 min 15s
導讀語:自定義控件只看這一篇,是不夠的!
前言
在之前我先后寫了"自定義View之擴展式"、"自定義View之復合式"、"自定義View之完全自定義",在這三篇文章中我都分別給出了一個例子。當然了,例子其實是相對比較簡單的,主要原因可能是個人水平有限吧!但盡管如此,我也盡可能把我要講的內容表述的更清晰一些!那么為了更好的在我們的腦海里構建這個知識框架(這句話太專業,讓我寫起來都感到了滿滿的壓力!其實就是讓某些重要的或是會用到的內容,能夠在我們大腦里留下個更深刻的印象,并讓他們之間有更多的聯系。)以便于在我們需要的時候,更容易提取出來用!為此,寫個總結也是一個不錯的方法!這個觀點是來自《暗時間》!
正文
我們暫且將自定義View分為了擴展式、復合式和完全自定義三種類型(當然我們也可以按別的分)!他們看起來確實有一定的從易到難的梯度關系!不管怎樣,我們將這前面三遍中的知識點和自定義View的一些知識點做一個總結!好處就是——可能會有跟多人關注我的“Android程序員日記”的公眾號吧!
都說程序員都是從零開始計數的,那我們開始吧!
[ 0 ]關于自定義View的三個構造方法
- 一個參數的構造方法:在用代碼動態的添加我們的自定義view時調用。
- 兩個參數的構造方法:在使用xml +inflate的方法添加控件時會調用,里面多了一個AttributeSet類型的值
- 三個參數的構造方法:這個構造方法系統是不調用的,需要我們顯示調用并給defStyleAttr傳值,多了一個defStyleAttr參數,這是這個view引用style資源的屬性參數,也就是我們可以在style中為自定義View定義一個默認的屬性樣式然后添加進來!
[ 1 ]關于三種自定義View
-
擴展式:
擴展式自定義View繼承自Android原生特定的View如:TextView,ImageView等等。我們通過重寫onDrow()等回調方法對其進行擴展!使其實現我們想要的更能或樣式!
注:該方法實現的自定義View控件不需要自己支持wrap_content和padding。
-
組合式:
組合式自定義View繼承自ViewGrop的子View如:LinearLayout、RelativieLayout等。當某種效果看起來像幾種View組合在一起的時候,都可以使用這種方式實現。
注:該方式實現自定義View不需要自己處理ViewGroup的測量和布局這兩個過程。
-
完全自定義:
完全自定義View繼承自View(android中所有控件的基類),通常實現一些不方便布局的組合方式來達到的,需要靜態或動態地顯示一些不規則的控件或圖形!注:該方法實現的自定義View控件需要自己支持wrap_content和padding。
[ 2 ]常用的回調方法
- onFinishInflate():加載完XML組件后回調
- onSizeChanged():組件大小改變時回調
- onMeasure():回調該方法來進行測量(在該方法中實現對wrap_content支持的代碼)
- onLayout():回調該方法來顯示位置
- onTouchEvent():監聽到觸摸事件回調,也是實現交互非常重要的回調方法
- onDraw():回調該方法對我們的控件進行繪制
[ 3 ]為自定義View添加并使用自定義屬性的過程
Step 1 : 在Values下創建attrs.xml(當然也可以以別的名字命名,無限制),然后在該文件中添加自定義View的自定義屬性!
Step 2 : 在自定義View的構造方法中獲取自定義屬性值,并將值配予相應的位置!
Step 3 :在xml中使用自定義控件及配置其自定義屬性
注:在xml使用自定義控件時一定要加 :
xmlns:custom="http://schemas.android.com/apk/res-auto"
當然你也可以寫成
xmlns:custom="http://schemas.android.com/apk/res/com.timen4.t3"(com.timen4.t3是應用的報名)
這兩種方式沒有本質上的區別。至于custom隨便你喜歡命名什么都可以。
[ 4 ]完全自定義控件中我們自己支持wrap_content和padding的代碼
-
MeasureSpec
簡介:MeasureSpec代表一個32位int值,高2位代表SpecMode(測量模式),低30位代表SpecSize(指定模式下的規格大小)。
-
三種SpecMode與SpecSize
1.UNSPECIFIED:父容器自容器無限制,要多大給多大,這種情況一般用于系統內部。一般我們不關注蓋模式。
2.EXACTLY:父容器已檢測出了View所需要的精確大小,這時V接我的最終大小就是SpecSize所給定的值。它對應于LayoutParams中的match_parent和具體的數值兩種模式
3.AT_MOST:父容器制定了一個可用大小即SpecSize,View的大小不能大于這個值,具體值要看不同View的具體實現。它對應于LayoutParams中的wrap_content。
MeasureSpec和LayoutParams的對應關系
普通的View(即非頂層View)的MeasureSpec由父容器的MeasureSpec和自身的LyoutParams來共同決定的,MeasureSpec一旦確定后,onMeasure中就可以確定View的測量寬高。但對于頂層View(即)
-
對wrap_content的支持
在onMeasure()方法中實現對wrap_content的支持
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); int widthSpecSize= MeasureSpec.getSize(widthMeasureSpec); int heightSpectMode = MeasureSpec.getMode(heightMeasureSpec); int heightSpecSize=MeasureSpec.getSize(heightMeasureSpec); //這里就是對wrap_content的支持 if(widthSpecMode==MeasureSpec.AT_MOST&&heightSpectMode==MeasureSpec.AT_MOST){ //這里設定的根據你自己自定義View的情況而定 setMeasuredDimension(200,200); }else if(widthSpecMode==MeasureSpec.AT_MOST){ setMeasuredDimension(200,heightSpecSize); }else if (heightSpectMode==MeasureSpec.AT_MOST){ setMeasuredDimension(widthSpecSize,200); } }
-
對padding的支持
在onDraw()方法中實現對padding的支持,其實就是在或控件時考慮到就padding就好了。如果不自己實現那么你對該自定義View設置padding將是無效的! @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //這里是對畫一個圓形的View的padding支持 final int paddingLeft = getPaddingLeft(); final int paddingRight = getPaddingRight(); final int paddingTop = getPaddingTop(); final int paddingBottom=getPaddingBottom(); int width = getWidth()-paddingLeft-paddingRight; int height = getHeight()-paddingBottom-paddingTop; int radius = Math.min(width,height)/2; canvas.drawCircle(paddingLeft+width/2,paddingTop+height/2,radius,mPaint_while); }
[ 5 ]為自定義View定義并實現接口回調的過程
本段示例代碼來自"自定義View之復合式",為了總結的需要,做了一些注釋上的修改,并簡化為實現一個回調方法!
Step 1: 定義回調接口(自定義View類中操作)
public interface TopBarClickListener {
void leftClick();
}
Step 2: 暴露回調接口(自定義View類中操作)
public void setOnTopbarClickListener(TopBarClickListener mListener){
this.mListener=mListener;
}
Step 3: 調用回調接口(自定義View類中操作)
private void bindEvents() {
mLeftButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if(mListener!=null){
mListener.leftClick();
}
}
});
}
Step 4: 實現回調回調接口及方法(在實例化自定義View對象的類中)
mtopBar.setOnTopbarClickListener(new TopBar.TopBarClickListener() {
@Override
public void leftClick() {
Toast.makeText(MainActivity.this,"上一張",Toast.LENGTH_SHORT).show();
if (index<=0){
index=3;
}else{
index--;
}
iv_image.setImageResource(images.get(index));
}
后記
好了,這篇總結就這么多了!我必須再次強調一次,本文最最重要的一句話,就是放在最前面的導讀語——自定義控件只看這一篇,是不夠的!這句話我沒開玩笑,你不信也沒關系,反正我是不會在哪我的美貌做賭注了!
其實,不只是我對自己寫的東西不自信,而是無論是我這一篇,還是某個大牛寫的某一篇。我都覺得自定義View看某一篇是不夠的!對于初學者或新手而言更是如此。因為正如我在我在前言中寫道的一樣,我們都在構造自己的知識架構。我們需要從更多的角度,去理解我們學到的或是要學習的內容!這樣才更有助于我們去理解和記憶!也才更有助于我們去實際應用!
最后總結一句話吧——“我們學習的任何事物”看“某一篇”都是不夠的!
喜歡可以關注我微信公眾號
分享絕不止于Android!