Android學習路線(二十四)ActionBar Fragment運用最佳實務

來源:互聯網
上載者:User

Android學習路線(二十四)ActionBar Fragment運用最佳實務

 

通過前面的幾篇部落格,大家看到了Google是如何解釋action bar和fragment以及推薦的用法。俗話說沒有demo的部落格不是好部落格,下面我會介紹一下action bar和fragment在實戰中的應用,以及相關demo源碼,希望和大家相互交流。

瞭解過fragment的同學們應該都知道,fragment是android 3.0版本才出現的的,因此如果要在支援android 3.0一下版本的工程中使用fragment的話是需要添加Support Library的。具體如何添加我就不再贅述,可以看我前面的部落格Android學習路線(二十一)運用Fragment構建動態UI——建立一個Fragment,下面的項目支援到API Level最低為8,所以項目中也會使用到Support Library。

作為一個有上進心的Android開發人員,我們是希望項目的設計符合Android Design的。Android Design是Google官方推薦的應用設計原則,不瞭解Android Design的同學可以去瞭解下,我這裡有官方翻譯文檔。

我發現“知乎”的App設計是符合Android Design的,那麼我們的項目就來模仿知乎的主介面。首先看下:

我們來分析一下這樣的介面應該怎麼實現,從可以看出“知乎”android端使用了action bar和drawerlayout,同時drawer中item切換主介面應該是fragment。

建立一個工程FakeZhihu:

從可以看到,使用最新的adt外掛程式建立android項目時,如果選擇的Minimum Required SDK為8,而Target SDK大於它的話,系統會自動在項目中匯入Support v4包;在建立項目嚮導最後一步可以選擇Navigation Type,如果選擇了Navigation Drawer,adt工具會在建立項目時自動產生DrawerLayout相關範例程式碼。但由於DrawerLayout是在高版本的API中出現的,因此adt工具會協助匯入Support v7 appcompat包,這樣DrawerLayout就可以相容到Android2.2了。沒有使用最新版的adt工具也沒有關係,我提供的demo裡有Support v4包和Support v7包,大家可以直接使用。

 

下面來看看代碼如何?,android預設的holo主題只提供兩種色調,和官方的action bar比較可以看出“知乎”的action bar的顏色以及action bar上action item的顏色以及title的字型大小都是自訂的,那麼我們來模仿它自訂一下action bar。

 

首先我們開啟res目錄下的style檔案,自訂一個主題和action bar的style,然後在自訂佈景主題中引用自訂的action bar的style:

 

        
這裡要注意的是 無論是在自訂佈景主題還是自訂style時,要根據情況加上parent屬性,如果沒有加上相應的parent屬性的話就不能使用父style中沒有被覆蓋的樣式。具體如何設定action bar的style可以參考 Android學習路線(九)為Action Bar添加Style。

 

完成自訂佈景主題和style後要記得在manifest檔案中應用:

 

                                                                                    
這裡可以讓整個應用都使用自訂的主題,也可以指定單個activity使用,使用android:theme屬性來指定。

 

接下來要給app添加DrawerLayout了,修改MainActivity的布局檔案,添加一個DrawerLayout,內容非常簡單,其中包含一個Drawer和內容布局的Container:

 

    <framelayout android:id="@+id/container" android:layout_height="match_parent" android:layout_width="match_parent">    </framelayout>
注意,下面那個fragment就是app的Drawer, 其中的屬性android:layout_gravity在這裡表示Drawer從哪一側划出,start代表左側,end代表右側;還可以定義兩個fragment,然後一個左側划出一個右側划出,DrawerLayout在之後會詳細講解,這裡先簡單瞭解如何使用。

 

建立完DrawerLayout布局後,我們來為Drawer定義一個fragment,我們可以看到知乎的Drawer中只是包含了一個ListView。這個ListView的第一項和其它項的布局不一樣,我們可以想到用ListView加上headerView來實現,知道這些後,我們來建立一個NavigationDrawerFragment繼承自Fragment,這個fragment的布局包含一個ListView:

 

使用一個ArrayList來存放ListView的資料,定義一個DrawerListItem對象來存放每個Item的title和icon的資源ID:

 

 

    首頁    發現    關注    收藏    草稿    搜尋    提問    設定
String[] itemTitle = getResources().getStringArray(R.array.item_title);int[] itemIconRes = {  R.drawable.ic_drawer_home,  R.drawable.ic_drawer_explore,  R.drawable.ic_drawer_follow,  R.drawable.ic_drawer_collect,  R.drawable.ic_drawer_draft,  R.drawable.ic_drawer_search,  R.drawable.ic_drawer_question,  R.drawable.ic_drawer_setting};       for (int i = 0; i < itemTitle.length; i++) {    DrawerListItem item = new DrawerListItem(getResources().getDrawable(itemIconRes[i]), itemTitle[i]);    mData.add(item);}
準備好資料後為該ListView設定Adapter,我們發現這個ListView是Single Choice模式的,並且每個Item被選中後會高亮。那麼如何來實現這個功能呢?

 

實現這樣的效果有兩個步驟:

第一:在ListView中指定android:choiceMode=singleChoice;

第二:給ListView的Item的布局設定一個特殊的背景drawable,這個drawable包含當狀態為activated時的背景和常態下的背景;同時這個item布局中的圖片src和文字顏色也要坐相應的設定;

item的背景:

 

        
圖片的src,這裡以home為例:

 

 

        
文字的顏色:

 

 

            
這樣就能實現ListView點擊Item高亮的效果了。

 

 

考慮到使用者在第一次使用app的時候可能不知道有Drawer的存在,我們可以在app第一次被啟動時讓Drawer處於開啟狀態,之後再預設隱藏,這是實際項目中常用的手段,這裡我們用sharedpreference來實現:

 

// 通過這個flag判斷使用者是否已經知道drawer了,第一次啟動應用顯示出drawer(抽屜),之後啟動應用預設將其
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);
接下來,要實現Drawer的fragment和宿主activity之間的通訊,需要定義一個回調介面,並且在宿主activity中實現:

 

 

/*** 宿主activity要實現的回調介面
* 用於activity與該fragment之間通訊*/public static interface NavigationDrawerCallbacks {    /**     * 當drawer中的某個item被選擇是調用該方法    */    void onNavigationDrawerItemSelected(String title);}
@Overridepublic void onNavigationDrawerItemSelected(String title) {FragmentManager fragmentManager = getSupportFragmentManager();FragmentTransaction ft = fragmentManager.beginTransaction();currentFragment = fragmentManager.findFragmentByTag(title);if(currentFragment == null) {currentFragment = ContentFragment.newInstance(title);ft.add(R.id.container, currentFragment, title);}if(lastFragment != null) {ft.hide(lastFragment);}if(currentFragment.isDetached()){ft.attach(currentFragment);}ft.show(currentFragment);lastFragment = currentFragment;ft.commit();onSectionAttached(title);}
具體如何來建立一個fragment以及如何?fragment和activity之間的通訊,可以參考:Android學習路線(二十一)運用Fragment構建動態UI——建立一個Fragment 和 Android學習路線(二十三)運用Fragment構建動態UI——Fragment間通訊 ;完整的NavigationDrawerFragment代碼如下:

 

 

package com.sweetvvck.fakezhihu;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.content.SharedPreferences;import android.content.res.Configuration;import android.os.Bundle;import android.preference.PreferenceManager;import android.support.v4.app.ActionBarDrawerToggle;import android.support.v4.app.Fragment;import android.support.v4.view.GravityCompat;import android.support.v4.widget.DrawerLayout;import android.support.v7.app.ActionBar;import android.support.v7.app.ActionBarActivity;import android.view.LayoutInflater;import android.view.Menu;import android.view.MenuInflater;import android.view.MenuItem;import android.view.View;import android.view.ViewGroup;import android.widget.AdapterView;import android.widget.ListView;import android.widget.Toast;/** * 用於管理互動和展示抽屜導航的Fragment。 * 參考 * 設計嚮導  */public class NavigationDrawerFragment extends Fragment {    /**     * 存放選中item的位置     */    private static final String STATE_SELECTED_POSITION = selected_navigation_drawer_position;    /**     * 存放使用者是否需要預設開啟drawer的key     */    private static final String PREF_USER_LEARNED_DRAWER = navigation_drawer_learned;    /**     * 宿主activity實現的回調介面的引用     */    private NavigationDrawerCallbacks mCallbacks;    /**     * 將action bar和drawerlayout綁定的組件     */    private ActionBarDrawerToggle mDrawerToggle;    private DrawerLayout mDrawerLayout;    private ListView mDrawerListView;    private View mFragmentContainerView;    private int mCurrentSelectedPosition = 0;    private boolean mFromSavedInstanceState;    private boolean mUserLearnedDrawer;    private List mData = new ArrayList();    public NavigationDrawerFragment() {    }    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        // 通過這個flag判斷使用者是否已經知道drawer了,第一次啟動應用顯示出drawer(抽屜),之後啟動應用預設將其隱藏        SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());        mUserLearnedDrawer = sp.getBoolean(PREF_USER_LEARNED_DRAWER, false);        if (savedInstanceState != null) {            mCurrentSelectedPosition = savedInstanceState.getInt(STATE_SELECTED_POSITION);            mFromSavedInstanceState = true;        }    }    @Override    public void onActivityCreated (Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        // 設定該fragment擁有自己的actionbar action item        setHasOptionsMenu(true);    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        mDrawerListView = (ListView) inflater.inflate(R.layout.fragment_navigation_drawer, container, false);        View headerView = inflater.inflate(R.layout.list_header, null);        mDrawerListView.addHeaderView(headerView);        mDrawerListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {            @Override            public void onItemClick(AdapterView parent, View view, int position, long id) {                selectItem(position);            }        });        String[] itemTitle = getResources().getStringArray(R.array.item_title);        int[] itemIconRes = {    R.drawable.ic_drawer_home,    R.drawable.ic_drawer_explore,    R.drawable.ic_drawer_follow,    R.drawable.ic_drawer_collect,        R.drawable.ic_drawer_draft,        R.drawable.ic_drawer_search,        R.drawable.ic_drawer_question,        R.drawable.ic_drawer_setting};                for (int i = 0; i < itemTitle.length; i++) {DrawerListItem item = new DrawerListItem(getResources().getDrawable(itemIconRes[i]), itemTitle[i]);mData.add(item);}        selectItem(mCurrentSelectedPosition);        DrawerListAdapter adapter = new DrawerListAdapter(this.getActivity(), mData);        mDrawerListView.setAdapter(adapter);        mDrawerListView.setItemChecked(mCurrentSelectedPosition, true);        return mDrawerListView;    }    public boolean isDrawerOpen() {        return mDrawerLayout != null && mDrawerLayout.isDrawerOpen(mFragmentContainerView);    }    /**     * 設定導航drawer     *     * @param fragmentId   fragmentent的id     * @param drawerLayout fragment的容器     */    public void setUp(int fragmentId, DrawerLayout drawerLayout) {        mFragmentContainerView = getActivity().findViewById(fragmentId);        mDrawerLayout = drawerLayout;        mDrawerLayout.setDrawerShadow(R.drawable.drawer_shadow, GravityCompat.START);        ActionBar actionBar = getActionBar();        actionBar.setDisplayHomeAsUpEnabled(true);        actionBar.setHomeButtonEnabled(true);        //隱藏Action bar上的app icon        actionBar.setDisplayShowHomeEnabled(false);        mDrawerToggle = new ActionBarDrawerToggle(                getActivity(),                    /* 宿主 */                mDrawerLayout,                    /* DrawerLayout 對象 */                R.drawable.ic_drawer,             /* 替換actionbar上的'Up'表徵圖 */                R.string.navigation_drawer_open,                  R.string.navigation_drawer_close        ) {            @Override            public void onDrawerClosed(View drawerView) {                super.onDrawerClosed(drawerView);                if (!isAdded()) {                    return;                }                getActivity().supportInvalidateOptionsMenu(); // 調用 onPrepareOptionsMenu()            }            @Override            public void onDrawerOpened(View drawerView) {                super.onDrawerOpened(drawerView);                if (!isAdded()) {                    return;                }                if (!mUserLearnedDrawer) {                    mUserLearnedDrawer = true;                    SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(getActivity());                    sp.edit().putBoolean(PREF_USER_LEARNED_DRAWER, true).commit();                }                getActivity().supportInvalidateOptionsMenu(); // 調用 onPrepareOptionsMenu()            }        };        // 如果是第一次進入應用,顯示抽屜        if (!mUserLearnedDrawer && !mFromSavedInstanceState) {            mDrawerLayout.openDrawer(mFragmentContainerView);        }        mDrawerLayout.post(new Runnable() {            @Override            public void run() {                mDrawerToggle.syncState();            }        });        mDrawerLayout.setDrawerListener(mDrawerToggle);    }    private void selectItem(int position) {        mCurrentSelectedPosition = position;        if (mDrawerListView != null) {            mDrawerListView.setItemChecked(position, true);        }        if (mDrawerLayout != null) {            mDrawerLayout.closeDrawer(mFragmentContainerView);        }        if (mCallbacks != null) {        if(mCurrentSelectedPosition == 0) {        mCallbacks.onNavigationDrawerItemSelected(getString(R.string.app_name));        return;        }            mCallbacks.onNavigationDrawerItemSelected(mData.get(position - 1).getTitle());        }    }    @Override    public void onAttach(Activity activity) {        super.onAttach(activity);        try {            mCallbacks = (NavigationDrawerCallbacks) activity;        } catch (ClassCastException e) {            throw new ClassCastException(Activity must implement NavigationDrawerCallbacks.);        }    }    @Override    public void onDetach() {        super.onDetach();        mCallbacks = null;    }    @Override    public void onSaveInstanceState(Bundle outState) {        super.onSaveInstanceState(outState);        outState.putInt(STATE_SELECTED_POSITION, mCurrentSelectedPosition);    }    @Override    public void onConfigurationChanged(Configuration newConfig) {        super.onConfigurationChanged(newConfig);        // 當系統配置改變時調用DrawerToggle的改變更配置置方法(例如橫豎屏切換會回調此方法)        mDrawerToggle.onConfigurationChanged(newConfig);    }    @Override    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {        //當抽屜開啟時顯示應用全域的actionbar設定        if (mDrawerLayout != null && isDrawerOpen()) {            inflater.inflate(R.menu.global, menu);            showGlobalContextActionBar();        }        super.onCreateOptionsMenu(menu, inflater);    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        if (mDrawerToggle.onOptionsItemSelected(item)) {            return true;        }        if (item.getItemId() == R.id.action_example) {            Toast.makeText(getActivity(), Example action., Toast.LENGTH_SHORT).show();            return true;        }        return super.onOptionsItemSelected(item);    }    /**     * 當抽屜開啟時顯示應用全域的actionbar設定     */    private void showGlobalContextActionBar() {        ActionBar actionBar = getActionBar();        actionBar.setDisplayShowTitleEnabled(true);        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);        actionBar.setTitle(R.string.app_name);    }    private ActionBar getActionBar() {        return ((ActionBarActivity) getActivity()).getSupportActionBar();    }    /**     * 宿主activity要實現的回調介面     * 用於activity與該fragment之間通訊     */    public static interface NavigationDrawerCallbacks {        /**         * 當drawer中的某個item被選擇是調用該方法         */        void onNavigationDrawerItemSelected(String title);    }    }

 

 

這樣就完成模仿“知乎”主介面的demo的開發啦,來看看效果如何:

 

 

怎麼樣,很像吧,Drawer是不是簡直可以以假亂真了,哈哈。

demo地址:http://download.csdn.net/detail/sweetvvck/7794083

其實demo中還有寫知識點沒有講到,比如drawer劃開時和關閉時action bar上的action item其實是不一樣的,這時如何?的呢?怎麼設定action bar不現實logo/icon?選擇Drawer中listview的item切換fragment可以每選擇一次都replace一次fragment,但是這樣每次都得重新建立一個fragment,如果fragment初始化較複雜就更佔資源,此時可以配合使用add,hide,show來實現切換同時將以載入過的fragment緩衝起來......由於篇幅原因,這些問題都會在之後的部落格中詳細講到的~

 

 

??

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.