Android实现通用的ActivityGroup(效果类似Android微博客户端主界面),...
為什么80%的碼農都做不了架構師?>>> ??
可以說ActivityGroup是Google提供的一個非常優秀的API,但它需要做稍微復雜的重寫才能用起來比較方便,本文擬將實現這個稍微復雜的重寫。TabActivity作為ActivityGroup唯一的子類卻讓人大失所望。
? 首先來說ActivityGroup的優秀之處以及它的必要性,它為開發者提供了一種可能,這種可能不將Activity作為屏幕的頂級元素(Context)呈現,而是嵌入到ActivityGroup當中。這是一種極大的飛躍,它將場景(Context)細分化了,ActivityGroup是一個主場景,而用戶可以通過導航按鈕來切換想要的子場景。如使用微博功能,它是一個相當宏大的場景,具有看最新的廣播信息、自己發微博、修改資料等子場景,用戶可以通過按鈕來切換到想要的子場景,而這個子場景仍活動于主場景之中。讓一個主場景能擁有多個邏輯處理模塊,主場景不再負責子場景邏輯,主場景只負責切換場景的邏輯,即每一個Activity(子場景)擁有一個邏輯處理模塊,一個ActivityGroup有多個Activity,卻不干預Activity的邏輯,這無疑細分化和模塊化了邏輯代碼。ActivityGroup和它將要內嵌的Activity所要實現的功能完全可以用只一個Activity來完成,你可以試想,當你把一個ActivityGroup和它所擁有的Activity的邏輯代碼放在一個Activity中時,那這個Activity會擁有多少行代碼,為維護帶來非常的不便。
? 再來說說TabActivity的不足之處,首先,TabActivity自己獨有的視圖幾乎沒人使用(也就是難看的標簽頁按鈕形式),國內開發者用到的特性幾乎都是從ActivityGroup繼承下來的。還有就是TabActivity的強制依賴關系,它的布局文件必須將TabHost作根標簽,并且id必須為"@android:id/tabhost",必須有TabWidget標簽,且它的id必須是"@android:id/tabs",還有加載Activity的View容器,id必須為@android:id/tabcontent。光是強制依賴關系,我就覺得不是很舒服。不僅僅是TabActivity,在一些特殊的Activity中,如ListActivity都存在這種強制依賴關系,ListActivity必須有id為xxx(想不起來了)的ListView,我想這些弊端應該獲得Google開發者的重視。
? 那么我下面我就將自己實現ActivityGroup,告別強制依賴關系,并隨心所欲的建立視圖。下面這個類是一個抽象類,開發者只需對這個抽象類稍做修改,并加以實現自己的視圖就能告別TabActivity。
package com.chenjun.demo.abstracttabactivity;import android.app.Activity; import android.app.ActivityGroup; import android.app.LocalActivityManager; import android.content.Intent; import android.os.Bundle; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.RadioButton; /** * 自己實現的一個通用ActivityGroup。 * 可以通過簡單的重寫它來制作有導航按鈕和用導航按鈕控制動態加載Activity的ActivityGroup。 * 開發者需要在實現類中實現三個方法: * 1.指定動態加載Activity的容器的對象,getContainer()方法。 * 2.初始化所有的導航按鈕,initRadioBtns()方法,開發者要遍歷所有的導航按鈕并執行initRadioBtn(int id)方法。 * 3.實現導航按鈕動作監聽器的具體方法,onCheckedChanged(...)方法。這個方法將實現某個導航按鈕與要啟動對應的Activity的關聯關系,可以調用setContainerView(...)方法。 * @author zet * */ public abstract class AbstractMyActivityGroup extends ActivityGroup implements CompoundButton.OnCheckedChangeListener{@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);initRadioBtns();} //加載Activity的View容器,容器應該是ViewGroup的子類 private ViewGroup container; private LocalActivityManager localActivityManager; /** * 加載Activity的View容器的id并不是固定的,將命名規則交給開發者 * 開發者可以在布局文件中自定義其id,通過重寫這個方法獲得這個View容器的對象 * @return */ abstract protected ViewGroup getContainer(); /** * 供實現類調用,根據導航按鈕id初始化按鈕 * @param id */ protected void initRadioBtn(int id){RadioButton btn = (RadioButton) findViewById(id);btn.setOnCheckedChangeListener(this);} /** * 開發者必須重寫這個方法,來遍歷并初始化所有的導航按鈕 */ abstract protected void initRadioBtns(); /** * 為啟動Activity初始化Intent信息 * @param cls * @return */ private Intent initIntent(Class<?> cls){ return new Intent(this, cls).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);} /** * 供開發者在實現類中調用,能將Activity容器內的Activity移除,再將指定的某個Activity加入 * @param activityName 加載的Activity在localActivityManager中的名字 * @param activityClassTye 要加載Activity的類型 */ protected void setContainerView(String activityName, Class<?> activityClassTye){ if(null == localActivityManager){localActivityManager = getLocalActivityManager();} if(null == container){container = getContainer();} //移除內容部分全部的View container.removeAllViews();Activity contentActivity = localActivityManager.getActivity(activityName); if (null == contentActivity) {localActivityManager.startActivity(activityName, initIntent(activityClassTye));} //加載Activity container.addView(localActivityManager.getActivity(activityName).getWindow().getDecorView(), new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));}}需要重寫的方法以及為什么需要重寫我都已在原代碼中標明。下面我們來具體的實現這個類,來達到我們想要的預期。
package com.chenjun.demo.abstracttabactivity;import android.os.Bundle; import android.view.ViewGroup; import android.widget.CompoundButton; import android.widget.RadioButton; public class TestMyActivityGroup extends AbstractMyActivityGroup{ //加載的Activity的名字,LocalActivityManager就是通過這些名字來查找對應的Activity的。 private static final String CONTENT_ACTIVITY_NAME_0 = "contentActivity0"; private static final String CONTENT_ACTIVITY_NAME_1 = "contentActivity1"; private static final String CONTENT_ACTIVITY_NAME_2 = "contentActivity2"; private static final String CONTENT_ACTIVITY_NAME_3 = "contentActivity3"; private static final String CONTENT_ACTIVITY_NAME_4 = "contentActivity4";@Override protected void onCreate(Bundle savedInstanceState) {setContentView(R.layout.my_activity_group);super.onCreate(savedInstanceState);((RadioButton)findViewById(R.id.radio_button0)).setChecked(true);} /** * 找到自定義id的加載Activity的View */ @Override protected ViewGroup getContainer() { return (ViewGroup) findViewById(R.id.container);} /** * 初始化按鈕 */ @Override protected void initRadioBtns() {initRadioBtn(R.id.radio_button0);initRadioBtn(R.id.radio_button1);initRadioBtn(R.id.radio_button2);initRadioBtn(R.id.radio_button3);initRadioBtn(R.id.radio_button4);} /** * 導航按鈕被點擊時,具體發生的變化 */ @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { switch (buttonView.getId()) { case R.id.radio_button0:setContainerView(CONTENT_ACTIVITY_NAME_0, ContentActivity0.class); break; case R.id.radio_button1:setContainerView(CONTENT_ACTIVITY_NAME_1, ContentActivity1.class); break; case R.id.radio_button2:setContainerView(CONTENT_ACTIVITY_NAME_2, ContentActivity2.class); break; case R.id.radio_button3:setContainerView(CONTENT_ACTIVITY_NAME_3, ContentActivity3.class); break; case R.id.radio_button4:setContainerView(CONTENT_ACTIVITY_NAME_4, ContentActivity4.class); break; default: break;}}}}布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginTop="0.0px" xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <LinearLayout android:id="@+id/container" android:layout_width="fill_parent" android:layout_height="0.0dip" android:layout_weight="1.0" /> <RadioGroup android:gravity="center_vertical" android:layout_gravity="bottom" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="wrap_content"> <RadioButton android:id="@+id/radio_button0" android:layout_marginTop="2.0dip" android:text="按鈕1" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_call" /> <RadioButton android:id="@+id/radio_button1" android:layout_marginTop="2.0dip" android:text="按鈕2" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_camera" /> <RadioButton android:id="@+id/radio_button2" android:layout_marginTop="2.0dip" android:text="按鈕3" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_agenda" /> <RadioButton android:id="@+id/radio_button3" android:layout_marginTop="2.0dip" android:text="按鈕4" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_delete" /> <RadioButton android:id="@+id/radio_button4" android:layout_marginTop="2.0dip" android:text="按鈕5" style="@style/tab_radio" android:drawableTop="@android:drawable/ic_menu_help" /> </RadioGroup> </LinearLayout> </LinearLayout>具體的實現效果(這里Activity基本沒有內容):
具體的代碼演示就差不多了,這里要做一些說明的:
? 1.開發者在自己的實現類中的onCreate方法中,必須先設置視圖,再調用super.oncreate(...)方法。具體為什么看了抽象類的源代碼我相信讀者應該會明白。
? 2.關于導航按鈕使用RadioButton。Android沒有特意為我們定制適合我們在這種場合下使用的按鈕,也就是上面可以設置簡筆畫,下面有文字說明。解決方案:1)使用ImageButton,將簡筆畫和文字說明P在一張圖片里面,但這樣有一個非常明顯的弊端,文字說明的文字字體是固定的,是P在圖片里的,那么不和系統的文字一樣。如果用戶使用一些比較花哨的系統文字,而導航按鈕卻是宋體,在上面的內容部分是他的系統文字,那么我很難想象他下一次是否還會打開您所開發的應用。2)自己去實現一個View,去代替RadioButton,出于學習目的這是好的。最佳的解決方案我還是認為是用RadioButton,只需對它稍做修改即可,具體可以參照新浪微博的資源文件。
? 缺陷反思:這些代碼都是我從重構得來的,當時開發的時候并沒有設計好開發流程(我是先有那個實現類,才有了那個抽象類的)。自己寫的ActivityGroup與TabActivity相比,優點顯而易見,缺點就是可能不穩定,但暫時沒有發現Bug,動態加載的Activity的邏輯代碼都能正確執行。
轉載于:https://my.oschina.net/mingxv/blog/123583
總結
以上是生活随笔為你收集整理的Android实现通用的ActivityGroup(效果类似Android微博客户端主界面),...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jenkins代理设置
- 下一篇: [Android]关于IntentSer