Android自定義View入門及實戰(zhàn)

前言

在Android應(yīng)用開發(fā)過程中井辜,固定的一些控件和屬性可能滿足不了開發(fā)的需求绎谦,所以在一些特殊情況下,我們需要自定義控件與屬性粥脚,同樣窃肠,這也是面試中面試官問的幾率比較高的問題,也是由初級工程師通向中高級工程師必備的刷允。

實現(xiàn)步驟

  1. 繼承View類或其子類
  2. 復(fù)寫view中的一些函數(shù)
  3. 為自定義View類增加屬性(兩種方式)
  4. 繪制控件(導(dǎo)入布局)
  5. 響應(yīng)用戶事件
  6. 定義回調(diào)函數(shù)(根據(jù)自己需求來選擇)

哪些方法需要被重寫

  • onDraw()
    view中onDraw()是個空函數(shù)冤留,也就是說具體的視圖都要覆寫該函數(shù)來實現(xiàn)自己的繪制。對于ViewGroup則不需要實現(xiàn)該函數(shù)树灶,因為作為容器是“沒有內(nèi)容“的(但必須實現(xiàn)dispatchDraw()函數(shù)纤怒,告訴子view繪制自己)。
  • onLayout()
    主要是為viewGroup類型布局子視圖用的天通,在View中這個函數(shù)為空函數(shù)泊窘。
  • onMeasure()
    用于計算視圖大小(即長和寬)的方式,并通過setMeasuredDimension (width, height) 保存計算結(jié)果烘豹。
  • onTouchEvent()
    定義觸屏事件來響應(yīng)用戶操作瓜贾。

還有一些可以重寫的方法

1.onKeyDown() 當(dāng)按下某個鍵盤時
2.onKeyUp() 當(dāng)松開某個鍵盤時
3.onTrackballEvent() 當(dāng)發(fā)生軌跡球事件時
4.onSizeChange() 當(dāng)該組件的大小被改變時
5.onFinishInflate() 回調(diào)方法,當(dāng)應(yīng)用從XML加載該組件并用它構(gòu)建界面之后調(diào)用的方法 6.onWindowFocusChanged(boolean) 當(dāng)該組件得到携悯、失去焦點(diǎn)時
7.onAttachedToWindow() 當(dāng)把該組件放入到某個窗口時
8.onDetachedFromWindow() 當(dāng)把該組件從某個窗口上分離時觸發(fā)的方法
9.onWindowVisibilityChanged(int) 當(dāng)包含該組件的窗口的可見性發(fā)生改變時觸發(fā)的方法

View的繪制過程

View的繪制過程

自定義控件的三種方式

1.繼承已有的控件 當(dāng)要實現(xiàn)的控件和已有的控件在很多方面比較類似, 通過對已有控件的擴(kuò)展來滿足要求阐虚。
2.繼承一個布局文件 一般用于自定義組合控件,在構(gòu)造函數(shù)中通過inflater和addView()方法加載自定義控件的布局文件形成圖形界面(不需要onDraw方法)蚌卤。
3.繼承view 通過onDraw方法來繪制出組件界面。

自定義屬性的兩種方法

  • 在布局文件中直接加入屬性奥秆,在構(gòu)造函數(shù)中去獲得逊彭。

布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
     <com.lee.demo.initView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" 
         inittext="@string/hello_world"
         />
</RelativeLayout>

在構(gòu)造方法中

public initView(Context context, AttributeSet attrs) {        
    super(context, attrs);        
    // TODO Auto-generated constructor stub
        int textId = attrs.getAttributeResourceValue(null, "inittext", 0);
        String text = context.getResources().getText(textId).toString();
}
  • 在res/values/ 下建立一個attrs.xml 來聲明自定義view的屬性。

可以定義的屬性有:

<declare-styleable name = "名稱"> 
//參考某一資源ID (name可以隨便命名)
<attr name = "background" format = "reference" /> 
//顏色值 
<attr name = "textColor" format = "color" /> 
//布爾值
<attr name = "focusable" format = "boolean" />
 //尺寸值 
<attr name = "layout_width" format = "dimension" /> 
//浮點(diǎn)值 
<attr name = "fromAlpha" format = "float" /> 
//整型值 
<attr name = "frameDuration" format="integer" /> 
//字符串 
<attr name = "text" format = "string" /> 
//百分?jǐn)?shù) 
<attr name = "pivotX" format = "fraction" /> 
//枚舉值 
<attr name="orientation"> <enum name="horizontal" value="0" /> 
<enum name="vertical" value="1" /> 
</attr> 
//位或運(yùn)算 
<attr name="windowSoftInputMode"> 
<flag name = "stateUnspecified" value = "0" /> 
<flag name = "stateUnchanged" value = "1" /> 
</attr> 
//多類型
<attr name = "background" format = "reference|color" /> 
</declare-styleable>
  • attrs.xml進(jìn)行屬性聲明
<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="myView">
        <attr name="text" format="string"/>
        <attr name="textColor" format="color"/>
    </declare-styleable>
</resources>
  • 添加到布局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:myview="http://schemas.android.com/apk/com.example.demo"
    >
     <com.lee.demo.initView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" 
         myview:text = "test"
         myview:textColor ="#ff0000"
         />
</RelativeLayout>
  • 這里注意命名空間:
    xmlns:前綴="http://schemas.android.com/apk/res/包名(或res-auto)"构订,一般AS會自動生成

  • 在構(gòu)造函數(shù)中獲取屬性值

public initView(Context context, AttributeSet attrs) {        
        super(context, attrs);        
        // TODO Auto-generated constructor stub
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView); 
        String text = a.getString(R.styleable.myView_text); 
        int textColor = a.getColor(R.styleable.myView_textColor, Color.WHITE); 
        a.recycle();
    }

或者:

    public initView(Context context, AttributeSet attrs) {        
        super(context, attrs);        
        // TODO Auto-generated constructor stub
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.myView); 
        int n = a.getIndexCount();        
        for(int i=0;i<n;i++){            
            int attr = a.getIndex(i);            
            switch (attr) {            
            case R.styleable.myView_text:                
                break;            
            case R.styleable.myView_textColor:                
                break;
            }
        }
       a.recycle();
    }

自定義隨手指移動的小球來學(xué)習(xí)自定義View

0.gif

實現(xiàn)上面的效果我們大致需要分成這幾步:
1.在res/values/ 下建立一個attrs.xml 來聲明自定義view的屬性
2.一個繼承View并復(fù)寫部分函數(shù)的自定義view的類
3.一個展示自定義view 的容器界面

1.自定義view命名為cicleview侮叮,它有一個屬性值,格式為color:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="cicleview">
        <attr name="TextColor" format="color"/>
    </declare-styleable>        
</resources>

2.在構(gòu)造函數(shù)獲取獲得view的屬性配置和復(fù)寫onDraw和onTouchEvent函數(shù)實現(xiàn)繪制界面和用戶事件響應(yīng)悼瘾。

public class cicleview extends View{    
    //定義畫筆和初始位置
    Paint p = new Paint();    
    public float currentX = 50;    
    public float currentY = 50;    
    public int textColor;    
    public cicleview(Context context, AttributeSet attrs) {        
        super(context, attrs);        
        //獲取資源文件里面的屬性囊榜,由于這里只有一個屬性值,不用遍歷數(shù)組亥宿,直接通過R文件拿出color值
        //把屬性放在資源文件里卸勺,方便設(shè)置和復(fù)用
        TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.myView);
        textColor = array.getColor(R.styleable.myView_TextColor,Color.BLACK);
        array.recycle();
    }    
    @Override
    protected void onDraw(Canvas canvas) {        
        super.onDraw(canvas);        
        //畫一個藍(lán)色的圓形
        p.setColor(Color.BLUE);
        canvas.drawCircle(currentX,currentY,30,p);        
        //設(shè)置文字和顏色,這里的顏色是資源文件values里面的值
        p.setColor(textColor);
        canvas.drawText("BY finch",currentX-30,currentY+50,p);
    }   
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        currentX = event.getX();
        currentY = event.getY();
        invalidate();//重新繪制圖形
        return true;
    }
}

這里通過不斷的更新當(dāng)前位置坐標(biāo)和重新繪制圖形實現(xiàn)效果烫扼,要注意的是使用TypedArray后一定要記得recycle(). 否則會對下次調(diào)用產(chǎn)生影響曙求。

3.把cicleview加入到activity_main.xml布局里面

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:myview="http://schemas.android.com/apk/res-auto"
    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="finch.scu.cn.myview.MainActivity">

    <com.lee.demo.cicleview
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        myview:TextColor="#ff0000"
        />
</RelativeLayout>

結(jié)束!!!

全體起立,奏國歌.行注目禮!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市映企,隨后出現(xiàn)的幾起案子悟狱,更是在濱河造成了極大的恐慌,老刑警劉巖堰氓,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挤渐,死亡現(xiàn)場離奇詭異,居然都是意外死亡双絮,警方通過查閱死者的電腦和手機(jī)浴麻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來掷邦,“玉大人白胀,你說我怎么就攤上這事「Ц冢” “怎么了或杠?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長宣蔚。 經(jīng)常有香客問我向抢,道長认境,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任挟鸠,我火速辦了婚禮叉信,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘艘希。我一直安慰自己硼身,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布覆享。 她就那樣靜靜地躺著佳遂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪撒顿。 梳的紋絲不亂的頭發(fā)上丑罪,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機(jī)與錄音凤壁,去河邊找鬼吩屹。 笑死,一個胖子當(dāng)著我的面吹牛拧抖,可吹牛的內(nèi)容都是我干的煤搜。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼唧席,長吁一口氣:“原來是場噩夢啊……” “哼宅楞!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起袱吆,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤厌衙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绞绒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體婶希,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年蓬衡,在試婚紗的時候發(fā)現(xiàn)自己被綠了喻杈。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡狰晚,死狀恐怖筒饰,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情壁晒,我是刑警寧澤瓷们,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響谬晕,放射性物質(zhì)發(fā)生泄漏碘裕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一攒钳、第九天 我趴在偏房一處隱蔽的房頂上張望帮孔。 院中可真熱鬧,春花似錦不撑、人聲如沸文兢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽禽作。三九已至,卻和暖如春揩页,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背烹俗。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工爆侣, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人幢妄。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓兔仰,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蕉鸳。 傳聞我的和親對象是個殘疾皇子乎赴,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,446評論 2 348

推薦閱讀更多精彩內(nèi)容