編輯:關於android開發
說到自定義控件不得不提的就是接口回調,在Android開發中接口回調用的還是蠻多的。在這篇博客開始的時候呢,我想聊一下iOS的自定義控件。在iOS中自定義控件的思路是繼承自UIView, 在UIView的子類中組合一些控件,對外暴漏一些屬性和回調接口,並留有必要的實現方法。在iOS自定義控件中常用的回調有兩種,一是委托代理回調(Delegate),另一種是Block回調。如果你想對這兩者有所了解,請參考我之前的博客《Objective-C中的委托(代理)模式》、《Objective-C中的Block回調模式》、《設計模式(十三):從“FQ”中來認識代理模式(Proxy Pattern)》。
在Android自定義控件時用到的接口回調和iOS開發中使用到的Delegate回調以及Block回調即為相似,就連實現方式都大同小異。今天的內容就自定義一個Android控件,並且以此控件為基礎,聊一下Android中的接口回調(確切的說應該是Java語言中的接口回調)。廢話少說,進入今天的主題。
一.自定義控件的UI實現
上面有提到,iOS開發中,自定義控件一般式繼承自UIView的,然後再UIView的子類中做一些事情。而Android開發中的自定義控件也是繼承自View, 但是今天我們的自定義控件是繼承自FrameLayout, 在此基礎上我們自定義一些東西。因為FrameLayout, LinearLayout等布局方式都是繼承自ViewGroup的,而ViewGroup則繼承自View, 所以在自定義控件時,繼承自FrameLayout等布局方式肯定是可以的。
1. 實現效果分析
接下來我們要自定義一個導航欄,而這個導航欄是模仿iOS系統中的NavigationBar。因為Android開發中沒有這個控件,所以我們需要自定義這個控件供開發者使用。下方是我們要實現的效果。上方的導航欄是我們自定義的NavigationBar,和iOS系統的導航欄類似。點擊左邊的返回按鈕,會退出當前Activity。點擊右邊的借口回調測試,會通過接口回調的形式來在當前Activity中顯示Toast提示。在調用該組件時,可以知道中間的Title.
2. UI布局分析以及Xml布局文件實現
接下來我們將會對UI進行拆分,詳細的看一下上面的NavigationBar是由哪些基礎控件組成的。分析完畢後,在通過一些布局方式將這些基礎控件進行組合,拼裝,最終成為我們想要使用的自定義控件。下方是手動畫的上述自定義控件UI原理圖,如下所示。
最下邊的布局我們采用的時FrameLayout方式,並設置其背景顏色。返回圖標(ImageView)和 返回文字(TextView)放在了一個水平布局的LinearLayout上。這兩者上面放了一個透明的Button, 用來實現返回操作。中間的Title(TextView) 在FrameLayout中設置成居中顯示即可。Call Back是一個Button, 用來測試下面的接口回調。
1 <?xml version="1.0" encoding="utf-8"?> 2 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" 5 android:background="#cccccc"> 6 <LinearLayout 7 android:orientation="horizontal" 8 android:layout_width="match_parent" 9 android:layout_height="20pt" 10 android:background="@drawable/background"> 11 <ImageView 12 android:id="@+id/back_title" 13 android:layout_width="wrap_content" 14 android:layout_height="18pt" 15 android:layout_gravity="center" 16 android:src="@drawable/back"/> 17 <TextView 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:layout_gravity="center" 21 android:textSize="8pt" 22 android:text="返回"/> 23 </LinearLayout> 24 25 <Button 26 android:id="@+id/back_button" 27 android:layout_width="50pt" 28 android:layout_height="20pt" 29 android:layout_gravity="center_vertical" 30 android:alpha="0" /> 31 32 <TextView 33 android:id="@+id/navigation_title" 34 android:layout_width="wrap_content" 35 android:layout_height="wrap_content" 36 android:textSize="10pt" 37 android:layout_gravity="center" 38 android:text="標題"/> 39 40 <Button 41 android:id="@+id/call_back" 42 android:layout_width="wrap_content" 43 android:layout_height="wrap_content" 44 android:layout_gravity="right" 45 android:text="接口回調測試"/> 46 </FrameLayout>
二. 為UI綁定事件以及留出回調接口
UI實現好後,就說明我們自定義組件的殼兒已經做好了,但是其內在的東西還需要實現。也就是說需要為上述實現的UI綁定Java類,並在類中處理控件的一些響應事件,以及在類中留出必要的接口來改變自定義組件的屬性。接下來來實現xml對應的Java類。
因為上述布局中,最外層我們使用的是FrameLayout布局,上面已經粗略的提過,我們可以繼承自FrameLayout來做一些東西,因為FrameLayout的父類是View, 所以我們可以在此基礎上做一些東西。同理,如果上述布局是使用其他布局來實現的,那麼你就可以繼承自其他布局的類來做一些東西。在本篇博客中我們就以FrameLayout為父類來實現我們自定義組件的關聯類。
1. 繼承FrameLayout並實現相應的構造函數,下方是我們要實現的構造函數。在構造函數中,我們需要與上述我們實現的xml布局文件進行關聯,當然,我們使用的是LayoutInflater來實現的,自定義組件的構造函數如下所示。
/* *自定義組件的構造方法 */ public CustomNavigationBar(Context context, AttributeSet attrs) { super(context, attrs); LayoutInflater.from(context).inflate(R.layout.custom_navigation, this); //加載布局文件 }
2. 實現好相應的構造方法並關聯好相應的布局文件後,我們需要對布局文件中的控件進行事件的處理。下方的代碼就是點擊返回按鈕要做的事情,因為點擊返回按鈕要做的事情就是結束當前Activity,所以不需要給調用者留有回調接口,在自定義組件的內部處理即可。下方代碼就是獲取UI中返回按鈕,並處理返回事件的方法。下方的方法需要在構造函數中調用才會起作用,函數不調用怎麼執行呢,對吧~。 下方代碼較為簡單,就是結束當前顯示的Activity,處理返回按鈕的事件如下:
1 /* 2 * 點擊返回按鈕方法 3 */ 4 private void onClickBackButton() { 5 Button button = (Button) findViewById(R.id.back_button); 6 button.setOnClickListener(new OnClickListener() { 7 @Override 8 public void onClick(View v) { 9 ((Activity) getContext()).finish(); 10 } 11 }); 12 }
3.處理好返回事件後,我們需要做的還有就是為標題欄的標題留出設置的方法。也就是說在調用該自定義組件時,我們要能設置該組件的標題。要滿足這一點,我們就需要在自定義組件中留出Title的setter方法了,並且這個Setter方法的訪問權限必須是Public的,不然在外界就沒辦法訪問這個方法了。下方就是這個設置title的Public方法。其實下方的代碼還是比較簡單的,就是通過ID來獲取標題的TextView,並設置相應的title即可,代碼如下:
1 public String navigationTitle = "標題欄"; 2 3 /* 4 * 設置標題欄的標題 5 */ 6 public void setNavigationTitle(String navigationTitle) { 7 this.navigationTitle = navigationTitle; 8 TextView textView = (TextView) findViewById(R.id.navigation_title); 9 textView.setText(navigationTitle); 10 }
4. 上面如果還算簡單的話,下方就是自定義控件中稍稍有點難度的地方了。接下來我們要實現相應按鈕的接口回調,在實現之前我們介紹一下為什麼要實現接口的回調。因為有時候點擊自定義控件中的按鈕時,所做的事情在自定義控件的內部無法獨立完成,需要在調用者中進行事件的處理,在這種情況下,我們就可以使用接口回調來處理。
上面實現的返回事件的處理就沒必要使用接口的回調了,因為在自定義組件內部完全可以該功能。舉個使用接口回調的栗子:比如點擊自定義控件中某個按鈕時,我們需要跳轉到其他Activity,而這個Activity在我們實現自定義控件時是未知的,這時候就要用到我們的接口回調來實現了。在iOS開發中,同樣遇到上述問題,所以iOS開發中也有各種回調比如Block回調,Delegate回調,Target-Action回調等都是iOS開發中常用的回調。雖然實現形式不同,但是其作用和Java中的接口回調是極為相似的。好,說這麼多,接下來我們要為XML布局文件中id為call_back的按鈕的點擊事件通過接口回調的形式傳遞到調用者中。
(1)第一步我們要先實現接口回調的接口,這也是必須的,因為接口回調如果沒有接口怎麼能行呢。該接口是Public類型的,不然在調用者中是無法使用的。我們接口的名字為onClickCallBackListener, 在其中有一個方法,該方法是接口回調時要執行的方法。
1 /* 2 *創建回調接口 3 */ 4 public static interface OnClickCallBackListener { 5 public void OnClickButton(View v); 6 }
(2) 聲明一個私有的接口對象,並為這個私有的對象實現setter方法,該私有的接口對象是用來接受自定義組件調用者傳過來的回調方法的。代碼比較簡單,在此就不做過多贅述了。
1 private OnClickCallBackListener callBackListener; //聲明接口對象 2 public void setCallBackListener(OnClickCallBackListener callBackListener) { 3 this.callBackListener = callBackListener; 4 }
(3) 實現好接口以及接收回調對象的變量後,接下來要做的事情就是獲取自定義組件中相應按鈕點擊的事件,並在此按鈕點擊事件中執行傳過來的接口對象相應的回調方法。下方這個方法,要在構造函數中調用。該方法的功能就是獲取自定義組件的相應按鈕的點擊事件並執行接口對象的回調方法。具體實現如下:
/* *點擊按鈕時執行接口回調 */ private void callBackButton() { Button button = (Button) findViewById(R.id.call_back); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { if (callBackListener != null) { callBackListener.OnClickButton(v); } } }); }
上方是把代碼拆開來講的,下方是整個自定義組件的實現類。具體代碼如下所示。
1 package com.example.lizelu.customnavigationbar; 2 3 import android.annotation.TargetApi; 4 import android.app.Activity; 5 import android.content.Context; 6 import android.os.Build; 7 import android.util.AttributeSet; 8 import android.view.LayoutInflater; 9 import android.view.View; 10 import android.widget.Button; 11 import android.widget.FrameLayout; 12 import android.widget.TextView; 13 14 /** 15 * Created by lizelu on 15/11/29. 16 */ 17 public class CustomNavigationBar extends FrameLayout { 18 19 /* 20 *創建回調接口 21 */ 22 public static interface OnClickCallBackListener { 23 public void OnClickButton(View v); 24 } 25 26 27 private OnClickCallBackListener callBackListener; //聲明接口對象 28 29 public String navigationTitle = "標題欄"; 30 31 /* 32 * 設置標題欄的標題 33 */ 34 public void setNavigationTitle(String navigationTitle) { 35 this.navigationTitle = navigationTitle; 36 TextView textView = (TextView) findViewById(R.id.navigation_title); 37 textView.setText(navigationTitle); 38 } 39 40 public void setCallBackListener(OnClickCallBackListener callBackListener) { 41 this.callBackListener = callBackListener; 42 } 43 44 /* 45 *自定義組件的構造方法 46 */ 47 public CustomNavigationBar(Context context, AttributeSet attrs) { 48 super(context, attrs); 49 LayoutInflater.from(context).inflate(R.layout.custom_navigation, this); //加載布局文件 50 onClickBackButton(); 51 callBackButton(); 52 } 53 /* 54 * 點擊返回按鈕方法 55 */ 56 private void onClickBackButton() { 57 Button button = (Button) findViewById(R.id.back_button); 58 button.setOnClickListener(new OnClickListener() { 59 @Override 60 public void onClick(View v) { 61 ((Activity) getContext()).finish(); 62 } 63 }); 64 } 65 66 /* 67 *點擊按鈕時執行接口回調 68 */ 69 private void callBackButton() { 70 Button button = (Button) findViewById(R.id.call_back); 71 button.setOnClickListener(new OnClickListener() { 72 @Override 73 public void onClick(View v) { 74 if (callBackListener != null) { 75 callBackListener.OnClickButton(v); 76 } 77 } 78 }); 79 } 80 } View Code
三.該自定義組件的調用方式
經過上面的過程,我們自定控件以及實現好了,接下來就是如何使用了。其實自定義組件的使用方式和系統自帶的組件使用起來區別不大,沒有什麼特別之處。下方就讓我們在Activity中使用上述我們自定義的控件吧。
1.首先在我們要使用該組件的Activity所對應的布局文件中加載我們的自定義組件的布局。要注意的一點是自定義組件的標簽我們要使用包的全面才可以,其他的和Android的系統組件使用方法類似,具體代碼如下:
1 <com.example.lizelu.customnavigationbar.CustomNavigationBar 2 android:id="@+id/custom_navigation_bar" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content"/>
2.在Activity的Java類中,通過id獲取我們自定義組件的對象,並實現其相應的回調即可。具體代碼如下:
1 private void setNavigationTitle(String title) { 2 CustomNavigationBar navigationBar = (CustomNavigationBar) findViewById(R.id.custom_navigation_bar); 3 navigationBar.setNavigationTitle(title); 4 5 //實現組件上的按鈕的接口回調 6 navigationBar.setCallBackListener(new CustomNavigationBar.OnClickCallBackListener() { 7 @Override 8 public void OnClickButton(View v) { 9 Toast.makeText(MainActivity.this, "回調執行的方法", Toast.LENGTH_SHORT).show(); 10 } 11 }); 12 }
到此,自定義組件的實現和調用實現完畢。雖然上述自定義控件雖然比較簡單,但是麻雀雖小,五髒俱全。再復雜的自定義控件也是有簡單的東西慢慢的拼裝而成。所以理解自定義控件的實現原理還是比較重要的。今天的博客就先到這兒,下方是上述Demo在GitHub上的分享地址,需要的小伙伴請自行Clone。
github分享地址:https://github.com/lizelu/AndroidCustomNavigationBar
Android官方文檔之Introduction 寫在前面的話:接觸Android的時間也不短了,聽了視頻、看了書、敲了代碼,寫了博客,做了demo。。。
安卓應用程序的簽名 簽名安卓應用程序 Android應用以它的包名作為唯一標識。如果在同一部手機上安裝兩個包名相同的應用,後面安裝的應用就會覆蓋前面安裝的應用。為了避免
Visual Studio Emulator for Android 初體驗,emulatorandroidVisual Studio Emulator for Andr
GreenDao3.0新特性解析(配置、注解、加密),greendao3.0新特性Greendao3.0release與7月6日發布,其中最主要的三大改變就是:1.換包名