安卓工具欄的使用

工具欄可以提供應(yīng)用導(dǎo)航、放置菜單選項,也可以統(tǒng)一風(fēng)格設(shè)計等,這里有個簡單的例子,如圖所示:

Toolbar 示例

這里我們創(chuàng)建工具欄菜單,分以下幾點介紹:

  • 在 XML 文件中定義菜單
  • 讓菜單顯示出來
  • 響應(yīng)菜單的選擇
  • 子標(biāo)題的顯示
  • 數(shù)據(jù)的同步

在 XML 文件中定義菜單

菜單及菜單選項需要用到一些字符串資源,我們首先添加字符串資源

values/strings.xml

<resources>
    <string name="new_crime">New Crime</string>
    <string name="show_subtitle">Show Subtitle</string>
    <string name="hide_subtitle">Hide Subtitle</string>
    <string name="subtitle_format">%1$d crimes</string>
</resources>

右鍵點擊 res 目錄, 選擇 New->Android resource file 菜單項。在彈出窗口界面,選擇 Menu 資源按鈕,命名資源文件為 fragment_crime_list,點擊 OK 確認(rèn)按鈕。

創(chuàng)建菜單

打開創(chuàng)建的 fragment_crime_list.xml 文件,添加以下代碼

res/menu/fragment_crime_list.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/new_crime"
        android:icon="@drawable/ic_menu_add"
        android:title="@string/new_crime"
        app:showAsAction="ifRoom|withText"/>
</menu>

showAsAction 屬性是指定菜單顯示在工具欄上還是溢出菜單,這里設(shè)置的是 ifRoom 和 withText 的組合值。因此,只要空間足夠,就會菜單項就會顯示圖標(biāo)及其文字。如果空間僅夠顯示菜單項圖標(biāo),文字描述就不會顯示。如果空間大小不夠顯示任何項,菜單就會溢出到溢出菜單中,以三個點為表示。showAsAction 屬性還有兩個可選值:always 和 never。不推薦用 always,盡量使用 ifRoom。對于很少用到的菜單,可選擇 never 屬性。

讓菜單顯示出來

在代碼中,Acrivity 類提供了管理菜單的回調(diào)函數(shù)。需要選項菜單時,Android 會調(diào)用 Activity 的 onCreateOptionsMenu(Menu) 函數(shù)。但是在本例中,與選項菜單相關(guān)的回調(diào)函數(shù)需在 fragment 而非 activity 里實現(xiàn)。不用擔(dān)心,fragment 有一套自己的處理機制。

實例化選項菜單

CrlmeListFragment.java

public class CrimeListFragment extends Fragment {
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.frigment_crime_list,menu);
    }
}

在上面代碼中,我們調(diào)用 MenuInflater.inflate(int,Menu) 方法傳入菜單文件的資源 ID,將布局文件中定義的菜單項目填充到 Menu 實例中。

Fragment.onCreateOptionMenu(Menu,MenuInflater) 是由 FragmentManager 調(diào)用的。因此,當(dāng) activity 接收到操作系統(tǒng)的 onCreateOptionsMenu(...) 方法請求回調(diào)時,我們必須明確告訴 FragmentManager:其管理的 fragment 應(yīng)該接收 onCreateOptionsMenu(...) 方法調(diào)用指令。
需調(diào)用以下方法:

public void setHsaOptionsMenu(boolean hsaMenu)

CrimeListFragment.java

public class CrimeListFragment extends Fragment {
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
    }
}

運行程序就能看到創(chuàng)建的菜單欄了

顯示在工具欄上的菜單欄

在豎屏模式下因為空間有限,默認(rèn)隱藏菜單項標(biāo)題,長按可以看到。在橫屏模式下就可以直接看到。

豎屏

響應(yīng)菜單的選擇

為了響應(yīng)用戶的菜單項,需要向 Crime 列表中添加新的 Crime。還有刪除隨機生成的數(shù)據(jù)。

CrimeLab.java

public void addCrime(Crime c){
    mCrimes.add(c);
}
private CrimeLab(Context context) {
    mCrimes = new ArrayList<>();
   /*for (int i = 0; i < 100; i++) {
        Crime crime = new Crime();
        crime.setTitle("Crime #" + i);
        crime.setSolved(i % 2 == 0);
        mCrimes.add(crime);
    }*/
}

當(dāng)用戶點擊菜單中的菜單項時,fragment 會收到 onOptionsItemSelectde(MenuItem) 方法的回調(diào)請求。傳入該方法的是一個描述用戶選擇的 MenuItem 實例。

CrimeListFragment.java

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()){
        case R.id.new_crime:
            Crime c = new Crime();
            CrimeLab.get(getActivity()).addCrime(c);
            Intent intent = CrimePagerActivity.newIntent(getActivity(),c.getId());
            startActivity(intent);
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

目前應(yīng)用主要是靠后退鍵導(dǎo)航,現(xiàn)在向應(yīng)用添加層級式導(dǎo)航。
AndroidManifest.xml

<activity
    android:name=".CrimePagerActivity"
    android:parentActivityName=".CrimeListActivity">
</activity>

現(xiàn)在運行就能看到向上按鈕了

CrimePagerActivity 界面的向上按鈕

雖然后退鍵導(dǎo)航和向上按鈕導(dǎo)航執(zhí)行的操作是一樣的,但是各自實現(xiàn)的機制大不相同。向上按鈕時,會在回退棧中尋找指定的 activity,如果實例存在,則彈出棧內(nèi)所有的其他 activity,讓啟動的目標(biāo) activity 出現(xiàn)在棧頂。

子標(biāo)題的顯示

這里我們添加一個菜單項來顯示或者隱藏 CrimelistActivity 工具欄的子標(biāo)題。先來添加視圖的代碼

res/menu/fragment_crime_list.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/new_crime"
        android:icon="@drawable/ic_menu_add"
        android:title="@string/new_crime"
        app:showAsAction="ifRoom|withText"/>
    <item
        android:id="@+id/show_subtitle"
        android:title="@string/show_subtitle"
        app:showAsAction="ifRoom"/>
</menu>

創(chuàng)建方法實現(xiàn)這個功能

CrimeListFragment.java

private void updateSubtitle(){
    CrimeLab crimeLab = CrimeLab.get(getActivity());
    int crimeCount = crimeLab.getCrimes().size();
    String subtitle = getString(R.string.subtitle_format,crimeCount);

    AppCompatActivity activity = (AppCompatActivity) getActivity();
    activity.getSupportActionBar().setSubtitle(subtitle);
}

getString(...) 方法接收字符串資源中的占位符的替換值,然后在 onOptionsItemSelected (...) 點擊響應(yīng)此方法。

響應(yīng) SHOW SHBTITLE 的點擊

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()){
        case R.id.new_crime:
            Crime c = new Crime();
            CrimeLab.get(getActivity()).addCrime(c);
            Intent intent = CrimePagerActivity.newIntent(getActivity(),c.getId());
            startActivity(intent);
            return true;
        case R.id.show_subtitle:
            updateSubtitle();
            return true;
        default:
            return super.onOptionsItemSelected(item);
    }
}

現(xiàn)在點擊 SHOW SHBTITLE 按鈕,就可以顯示出來了。但是菜單項標(biāo)題依然顯示為 SHOW SHBTITLE,顯然,菜單項標(biāo)題的切換與子標(biāo)題的顯示或隱藏需要聯(lián)動。

調(diào)用 onOptionsItemSelected(...) 方法時,可以更新 SHOW SUBTITLE 的文字,但是設(shè)備旋轉(zhuǎn)時,子標(biāo)題的變化就會丟失,比較好的解決方法是在 onCreateOptionsMenu(...) 方法內(nèi)更新 SHOW SUBTITLE 菜單項,并在用戶點擊的時候重建工具欄。

CrimeListFragment.java

public class CrimeListFragment extends Fragment {
    private boolean mSubtitleVisible;
    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        super.onCreateOptionsMenu(menu, inflater);
        inflater.inflate(R.menu.frigment_crime_list,menu);

        MenuItem subtitleItem = menu.findItem(R.id.show_subtitle);
        if (mSubtitleVisible){
            subtitleItem.setTitle(R.string.hide_subtitle);
        }else {
            subtitleItem.setTitle(R.string.show_subtitle);
        }
    }
    
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()){
            case R.id.new_crime:
                Crime c = new Crime();
                CrimeLab.get(getActivity()).addCrime(c);
                Intent intent = CrimePagerActivity.newIntent(getActivity(),c.getId());
                startActivity(intent);
                return true;
            case R.id.show_subtitle:
                mSubtitleVisible  = !mSubtitleVisible;
                getActivity().invalidateOptionsMenu();
                updateSubtitle();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
}

最后,根據(jù) mSubtitleVisible 變量值,聯(lián)動菜單項標(biāo)題與子標(biāo)題

CrimeListFragment.java

private void updateSubtitle(){
    CrimeLab crimeLab = CrimeLab.get(getActivity());
    int crimeCount = crimeLab.getCrimes().size();
    String subtitle = getString(R.string.subtitle_format,crimeCount);

    if (!mSubtitleVisible){
        subtitle = null;
    }
    AppCompatActivity activity = (AppCompatActivity) getActivity();
    activity.getSupportActionBar().setSubtitle(subtitle);
}

數(shù)據(jù)的同步

接下來解決數(shù)據(jù)同步的問題,新建 Crime 后,使用后退鍵回到 CrimeListActivity,總次數(shù)不會更新,在 onResume() 方法中調(diào)用 updateSubtitle() 就能解決這個問題。因為 onResume() 和 onCreatView() 都會調(diào)用 updateUI() 方法,那就在 updateUI() 中調(diào)用 updateSubtitle().

CrimeListFragment.java

private void updateUI() {
    CrimeLab crimeLab = CrimeLab.get(getActivity());
    List<Crime> crimes = crimeLab.getCrimes();
    if (mAdapter==null){
        mAdapter = new CrimeAdapter(crimes);
        mCrimeRecyclerView.setAdapter(mAdapter);
    }else {
        mAdapter.notifyItemChanged(mPosition);
    }
    updateSubtitle();
}

但是點擊向上按鈕,子標(biāo)題被重置了,這又是什么情況呢?這是 Android 實現(xiàn)層級導(dǎo)航帶來的問題:導(dǎo)航回退到的目標(biāo) activity 會被完全重建。既然父 activity 是全新的。實例變量值以及保存的狀態(tài)顯示會徹底丟失。有兩種解決方案,在本例中,調(diào)用 CrimePagerActivity 的 finlsh() 方法直接回退到前一個 activity 的界面。但是這樣智能回退一個層級,而實際中絕大多數(shù)都需要多層級導(dǎo)航。第二種是在啟動 CrimePagerActivity 時,把子標(biāo)題狀態(tài)作為 extra 信息傳給它,然后在 CrimePagerActivity 中覆蓋 getParentActivityIntent() 方法,用附帶的 extra 信息的 intent 重建 CrimeListActivity。

還有一個問題,旋轉(zhuǎn)設(shè)備后,子標(biāo)題會消失,只要用實例狀態(tài)保存機制,就能解決問題。

CrimeListFragment.java

public class CrimeListFragment extends Fragment {
    
    private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_crime_list, container, false);

        mCrimeRecyclerView = (RecyclerView) view
                .findViewById(R.id.crime_recycler_view);
        mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
        if (savedInstanceState != null){
            mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
        }
        updateUI();

        return view;
    }
    
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(SAVED_SUBTITLE_VISIBLE,mSubtitleVisible);
    }
    
}

GitHub地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容