引言
在Android的應用中,經常會見到底部菜單,例如的底部菜單如下所示:
而在企業級的Android應用中,也存在同樣的需求,但與這些福士軟體的區別在於企業級的Android應用由於UI頁面很多,每個頁面都需要有底部菜單,而且每個頁面的底部功能表按鈕還可能完全不一樣,所以,為了使每個頁面保持一致性並為UI頁面製作時提供便利,針對底部菜單進行專門的設計封裝,就顯得特別重要。
設計選型
在設計底部菜單時,有下面兩種思路:
一、單獨定製底部菜單塊,並將菜單塊代碼引入到每一個需要使用到底部菜單的layout.xml檔案中,然後再在代碼中定義需要使用的按鈕。
此方法符合正常思維邏輯,缺點在於每個頁面都要重複去引入,其實這個是可以省掉的。
二、Android中,可以將某塊自訂的UI從layout.xml中執行個體化出來使用,使用的是LayoutInflater,基於該技術,我們可以換一種思路來解決這個問題:寫頁面layout檔案的時候不用關心底部菜單。然後在展示的代碼中,定義一個大的視圖頁面將頁面的layout檔案以及底部菜單的layout包含進來,過程如所示:
詳細設計
1. 設計大的視圖頁面layout:bottom_menu_layout.xml,該頁面用來包含以後自訂的業務頁面,以及底部菜單。
<?xml version="1.0"encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <LinearLayout android:orientation="horizontal"android:layout_width="fill_parent" android:gravity="bottom" android:layout_height="wrap_content" android:id="@+id/bottom_menu_button_group_id"> </LinearLayout> </LinearLayout> |
2. 設計底部功能表按鈕layout:bottom_menu_button_frame.xml,該頁面用來定義每一個按鈕的樣式,一般需要包含一個位於上方的圖片以及一個位於下方的按鈕文字。
<?xml version="1.0"encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="64px" android:layout_height="wrap_content" android:id="@+id/bottom_menu_template_button_id" android:background="@drawable/tab_one_normal"> <ImageView android:id="@+id/bottom_menu_template_img_id" android:paddingTop="5px"android:layout_gravity="center" android:layout_width="wrap_content" android:layout_height="wrap_content"android:src="@drawable/image" /> <TextView android:layout_width="wrap_content"android:id="@+id/bottom_menu_template_text_id" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:textSize="11sp"android:paddingBottom="5px" /> </LinearLayout> |
3. 自訂封裝按鈕的JAVA類:BottomButton,封裝底部功能表按鈕的基本資料:圖片、文字、按鈕事件、是否當前被選中的按鈕等。
public class BottomButton { // 按鈕菜單文字 private String text; // 按鈕菜單圖片 private int backgroundResource; // 點擊事件。 private View.OnClickListener clickListener; // 是否當前已經選中的按鈕,如果是則高亮,並且忽略點擊事件。 private boolean isCurrent = false; } |
4. 自訂底部菜單Layout 類:BottomMenuLayout,該類繼承自LinearLayout,用來處理按鈕的展示,該類負責以下三件事情:
a) 將底部菜單layout加入到整個大視圖頁面中。
b) 綁定每一個功能表按鈕。
c) 重新計算整個布局的大小,將菜單固定在底部。
public class BottomMenuLayout extends LinearLayout { //執行個體化layout使用的類 private LayoutInflater mInflater; //儲存功能表按鈕的集合,每一個集合元素代表一個按鈕,包含了按鈕所需要的資訊:圖片,文字,按鍵處理事件。 private List<BottomButton> bottomButtons; //封裝功能表按鈕的布局。 private View bottomMenuLayout; public void processInitButton() { //初始化布局,將底部菜單layout加入到視圖中。 initLayout(this.getContext()); //綁定每一個功能表按鈕 bindingButton(); //重新計算整個布局的大小,使用整個螢幕的高度減去底部菜單的高度, //得出並設定中間頁面部分的高度,就能夠將菜單固定在底部。 resizeLayout(); } |
註:這裡屏蔽了實現細節,具體參考附件代碼。
5. 當以上每一個零散的部分都完成以後,就需要一個組裝者,來負責將各個部分組裝在一起,使之能夠正常運作起來,這裡定義一個繼承自Activity的類:BaseBottomMenuActivity ,用來充當組裝者的角色,開發業務時需要更改方式,原來直接繼承自Activity的類改為繼承該類。該類主要完成以下工作:
a) 建立出整個大視圖頁面。
b) 建立出中間內容部分頁面,也就是由開發人員定義的實際layout內容。並加到整個大視圖的頁面中來。
c) 建立出底部菜單,並將底部菜單加入到整個大視圖的頁面中來。
d) 需要得知子類繼承該類後,具體使用的layout頁面的ID,定義抽象方法由子類實現以提供該ID。
e) 需要得知子類繼承該類後,具體需要哪些功能表按鈕,定義抽象方法由子類實現以提供按鈕的列表。
f) 需要得知子類繼承該類後,還需要做哪些頁面初始化的工作,定義抽象方法由子類實現以便在頁面初始過程中調用。
public abstract class BaseBottomMenuActivity extends Activity { private LayoutInflater mInflater; //執行個體化layout使用的類 protected BottomMenuLayout bottomMenuLayout; //底部菜單UI部分 protected View contentView; //頁面中間UI部分 final protected void onCreate(Bundle savedInstanceState) { //a) 建立出整個大視圖頁面。 //b) 建立出中間內容部分頁面,也就是由開發人員定義的實際layout內容。並加到整個大視圖的頁面中來。 //c) 建立出底部菜單,並將底部菜單加入到整個大視圖的頁面中來。 } /** * 子類實現後,在原來的onCreate方法中內容移到這裡來操作。 * @paramsavedInstanceState */ protected abstract void onCreatOverride(Bundle savedInstanceState); /** * 返回layout xml的ID * 原本在Activity的onCreate方法中調用的setContentView(R.layout.xxxxLayoutId); 現在從該方法返回。 * @return */ public abstract int getContentViewLayoutResId(); /** * 建立底部菜單,需要子類實現,在此方法中, * 建立多個BottomButton對象並放置到List中返回即可。 * 如果需要哪一個按鈕當前被選中,則設定BottomButton的isCurrent屬性為ture. * @param bottomButtons * @param bottomMenuLayout * @return */ public abstractList<BottomButton> getButtonList(); |
註:這裡屏蔽了實現細節,具體參考附件代碼。
實現及使用
具體實現代碼見附件。
在上述設計的各部分都實現後,就可以進行使用了,使用時,與平常開發正常頁面的步驟一樣,首先畫layout.xml,然後定義activity,進行綁定並配置AndroidManifest.xml檔案,不同的地方在於定義的Activity需要繼承BaseBottomMenuActivity。並且根據以下步驟開發該Activity:
1. 在子類中繼承實現getButtonList方法,在方法中封裝BottomButton對象返回,每一個BottomButton代表一個功能表項目,具體屬性見BottomButton定義。 2. 在子類中繼承實現getContentViewLayoutResId方法,返回layout xml的ID。 3. 在子類中繼承實現onCreatOverride方法,原先在onCreat方法中完成的事情挪到這裡,super.onCreate(savedInstanceState);和setContentView就不需要調用了。 |
測試
讓我們來寫一個簡單的Demo來進行測試,該Demo中,除了底部菜單,只有一個TextView文本,該類繼承了BaseBottomMenuActivity:
public class Demo extends BaseBottomMenuActivity { public List<BottomButton> getButtonList() { Map<String,String> buttonMaps = new HashMap<String,String>(); buttonMaps.put("Menu1", String.valueOf(R.drawable.home)); buttonMaps.put("Menu2", String.valueOf(R.drawable.home)); buttonMaps.put("Menu3", String.valueOf(R.drawable.home)); buttonMaps.put("Menu4", String.valueOf(R.drawable.home)); buttonMaps.put("主菜單", String.valueOf(R.drawable.home)); List<BottomButton>buttons = new ArrayList<BottomButton>(); Iterator<String> itKey =buttonMaps.keySet().iterator(); while(itKey.hasNext()) { Stringkey = itKey.next(); StringvalueRes = buttonMaps.get(key); BottomButtononeBottomButton = new BottomButton(); oneBottomButton.setText(key); oneBottomButton.setBackgroundResource(Integer.parseInt(valueRes)); buttons.add(oneBottomButton); } return buttons; } public int getContentViewLayoutResId() { return R.layout.main; } protected void onCreatOverride(Bundle savedInstanceState) { } } |
在返回按鈕的getButtonList方法中,返回了5個按鈕。
其布局檔案如下:
<?xml version="1.0"encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/hello" /> </LinearLayout> |
結果程式效果如所示:
源碼:http://download.csdn.net/detail/linghu_java/5023136