使用 AppBar

頂端應用程式列會在應用程式視窗頂端顯示一個相同的區域,用以顯示目前畫面上的資訊和動作。

頂端應用程式列範例
圖 1.頂端應用程式列範例。

應用程式列的擁有權會因應用程式的需求而不同。使用片段時,應用程式列可實作為 ActionBar,擁有權屬於代管活動或片段版面配置內的工具列。

如果所有畫面都使用相同的應用程式列,且應用程式列始終位於螢幕頂端並與螢幕同寬,則應使用由主題提供的活動代管動作列。使用主題應用程式列有助於維持一致的外觀,並提供可代管選項選單和向上按鈕的區域。

如想要對跨多個畫面的應用程式列大小、刊登位置和動畫擁有更多管控權,請使用片段代管的工具列。例如,您可能需要收合應用程式列,或希望應用程式列僅橫跨半個螢幕寬度且垂直置中。

在加載選單和回應使用者互動時,不同情境可能需要採用不同做法。瞭解不同的做法並採用最適合應用程式的做法,不但能節省時間,還能確保應用程式正常運作。

本主題中的範例參照包含可編輯設定檔的 ExampleFragment。片段會在應用程式列中加載下列 XML 定義的選單

<!-- sample_menu.xml -->
<menu
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@+id/action_settings"
        android:icon="@drawable/ic_settings"
        android:title="@string/settings"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/action_done"
        android:icon="@drawable/ic_done"
        android:title="@string/done"
        app:showAsAction="ifRoom|withText"/>

</menu>

選單包含兩個選項:一個用於前往設定檔畫面,另一個用於儲存所做的設定檔變更。

活動擁有的應用程式列

應用程式列最常由代管活動擁有。當應用程式列由活動擁有時,片段可以與應用程式列互動,其方式為覆寫建立片段期間所呼叫的架構方法。

透過活動註冊

您必須告知系統,您的應用程式列片段正在參與填入選項選單。方法是在片段的 onCreate(Bundle) 方法中呼叫 setHasOptionsMenu(true),如以下範例所示:

Kotlin

class ExampleFragment : Fragment() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }
}

Java

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

setHasOptionsMenu(true) 會向系統告知片段想接收選單相關回呼。發生點擊事件等選單相關事件時,系統會先在活動上呼叫事件處理方法,再到片段上呼叫該方法。

不過,應用程式邏輯不應仰賴此順序。如果同一個活動代管多個片段,則每個片段都可以提供選單選項。在此情況下,回呼順序取決於片段的加入順序。

加載選單

如要將選單合併至應用程式列的選項選單,請覆寫片段中的 onCreateOptionsMenu()。這個方法會接收目前的應用程式列選單和 MenuInflater 做為參數。請使用選單 inflater 來建立片段選單的執行個體,然後合併至目前的選單,如以下範例所示:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.sample_menu, menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onCreateOptionsMenu(@NonNull Menu menu, @NonNull MenuInflater inflater) {
       inflater.inflate(R.menu.sample_menu, menu);
    }
}

圖 2 顯示了更新後的選單。

現在,選項選單包含您的選單片段
圖 2.現在,選項選單包含您的選單片段。

處理點擊事件

參與選項選單的所有活動和片段都可以回應觸控動作。片段的 onOptionsItemSelected() 會以參數的形式接收選定的選單項目,並透過傳回布林值表示是否耗用觸控動作。活動或片段從 onOptionsItemSelected() 傳回 true 後,其他參與的片段不會再收到回呼。

onOptionsItemSelected() 的實作中,使用選單項目的 itemId 上的 switch 陳述式。如果您擁有選定的項目,請妥善處理觸控作業並傳回 true,表示點擊事件已處理。如果您不擁有選定的項目,請呼叫 super 實作方式。根據預設,super 實作方式會傳回 false,方便繼續選單處理作業。

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return when (item.itemId) {
            R.id.action_settings -> {
                // Navigate to settings screen.
                true
            }
            R.id.action_done -> {
                // Save profile changes.
                true
            }
            else -> super.onOptionsItemSelected(item)
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public boolean onOptionsItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.action_settings:  {
                // Navigate to settings screen.
                return true;
            }
            case R.id.action_done: {
                // Save profile changes.
                return true;
            }
            default:
                return super.onOptionsItemSelected(item);
        }

    }

}

動態修改選單

請將隱藏/顯示按鈕或變更圖示的邏輯放在 onPrepareOptionsMenu() 中。系統會在選單顯示前呼叫此方法。

延續上例,「儲存」按鈕應當在使用者開始編輯後才會顯示,且在使用者儲存後便會消失。將這個邏輯新增至 onPrepareOptionsMenu(),即可讓選單正確顯示:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onPrepareOptionsMenu(menu: Menu){
        super.onPrepareOptionsMenu(menu)
        val item = menu.findItem(R.id.action_done)
        item.isVisible = isEditing
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onPrepareOptionsMenu(@NonNull Menu menu) {
        super.onPrepareOptionsMenu(menu);
        MenuItem item = menu.findItem(R.id.action_done);
        item.setVisible(isEditing);
    }
}

如果是需要更新選單的情況,例如在使用者按下「編輯」按鈕來編輯設定檔資訊時,請在代管活動上呼叫 invalidateOptionsMenu(),藉此要求系統呼叫 onCreateOptionsMenu()。失效後,即可在 onCreateOptionsMenu() 中進行更新。選單加載時,系統會呼叫 onPrepareOptionsMenu() 並更新選單,反映片段的目前狀態。

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateOptionsMenu() {
        isEditing = !isEditing
        requireActivity().invalidateOptionsMenu()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateOptionsMenu() {
        isEditing = !isEditing;
        requireActivity().invalidateOptionsMenu();
    }
}

片段擁有的應用程式列

如果應用程式中大多數畫面都不需要應用程式列,或者其中一個畫面需要不同的應用程式列,您可以在片段版面配置中新增 Toolbar。雖然 Toolbar 可以新增至片段檢視區塊階層中的任何位置,但一般做法是保持在螢幕頂端。如要在片段中使用 Toolbar,請提供 ID 並在片段中取得該 ID 的參照,就像使用其他檢視畫面一樣。您也可以考慮使用 CoordinatorLayout 行為,製作工具列動畫。

<androidx.appcompat.widget.Toolbar
    android:id="@+id/myToolbar"
    ... />

使用片段擁有的應用程式列時,Google 會建議直接使用 Toolbar API。請勿使用 setSupportActionBar()Fragment 選單 API,這些僅適用於活動擁有的應用程式列。

加載選單

Toolbar 便利方法 inflateMenu(int) 會使用選單資源的 ID 做為參數。如要將 XML 選單資源加入工具列,請將 resId 傳遞至此方法,如以下範例所示:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu)
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.inflateMenu(R.menu.sample_menu);
    }

}

如要加載另一個 XML 選單資源,請使用新選單的 resId 再次呼叫此方法。新的選單項目會新增至選單,但不會修改或移除現有選單項目。

如要取代現有選單集,請在使用新選單 ID 呼叫 inflateMenu(int) 前清除選單,如以下範例所示:

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun clearToolbarMenu() {
        viewBinding.myToolbar.menu.clear()
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void clearToolbarMenu() {

        viewBinding.myToolbar.getMenu().clear()

    }

}

處理點擊事件

您可以使用 setOnMenuItemClickListener() 方法將 OnMenuItemClickListener 直接傳遞到工具列。使用者從工具列底部或相關溢位選單的動作按鈕中選取選單項目時,系統就會叫用此事件監聽器。選定 MenuItem 會傳遞到事件監聽器的 onMenuItemClick() 方法,並可用於消耗動作,如以下範例所示:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener {
            when (it.itemId) {
                R.id.action_settings -> {
                    // Navigate to settings screen.
                    true
                }
                R.id.action_done -> {
                    // Save profile changes.
                    true
                }
                else -> false
            }
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setOnMenuItemClickListener(item -> {
            switch (item.getItemId()) {
                case R.id.action_settings:
                    // Navigate to settings screen.
                    return true;
                case R.id.action_done:
                    // Save profile changes.
                    return true;
                default:
                    return false;
            }
        });
    }
}

動態修改選單

片段擁有應用程式列後,您可以在執行階段修改 Toolbar,方法和修改任何其他檢視畫面一樣。

延續上例,「儲存」選單選項應在使用者開始編輯後才顯示,輕觸後就會再次消失。

Kotlin

class ExampleFragment : Fragment() {
    ...
    fun updateToolbar() {
        isEditing = !isEditing

        val saveItem = viewBinding.myToolbar.menu.findItem(R.id.action_done)
        saveItem.isVisible = isEditing

    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    public void updateToolbar() {
        isEditing = !isEditing;

        MenuItem saveItem = viewBinding.myToolbar.getMenu().findItem(R.id.action_done);
        saveItem.setVisible(isEditing);
    }

}

如果存在該圖示,導覽按鈕會顯示在工具列的開頭。在工具列上設定導覽圖示會使其公開可見。您也可以設定導覽專用 onClickListener(),每當使用者點選導覽按鈕時即會呼叫它,如以下範例所示:

Kotlin

class ExampleFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...
        myToolbar.setNavigationIcon(R.drawable.ic_back)

        myToolbar.setNavigationOnClickListener { view ->
            // Navigate somewhere.
        }
    }
}

Java

public class ExampleFragment extends Fragment {
    ...
    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        ...
        viewBinding.myToolbar.setNavigationIcon(R.drawable.ic_back);
        viewBinding.myToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Navigate somewhere.
            }
        });
    }
}