Androidアプリ開発初級編(ActionBar)

今回はActionBarについて触れていきたいと思います。アクションバーを使うことによってアプリの基本的な動作を他のアプリと共通させることで、ユーザビリティ性を高めていくことが出来ます。

■事前準備

前回の記事で作成したアプリを使用します。

追加でbuild.gradle(アプリ側)に下記を追加します。

    implementation "com.google.android.material:material:1.2.1"

■サンプルアプリの動作

■ActionBarの作成

ActionBarを使って、アプリケーションによくあるAbout画面やHelp画面への画面遷移、そしてデータをシェアするようなアイコンボタンの動作を追加で実装していきます。

まず、適当なテキストを表示するAboutFragment、HelpFragmentを作成し、navigationに追加しておきましょう。

次にres ディレクトリを右クリックしてResource TypeにMenuを指定して下記のオーバーフローメニューを作成していきます。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/aboutFragment"
        android:title="@string/about" />
</menu>

今回はitemが一つだけですが、アイテムを複数作成することも可能です。

idにはnavigationに追加しておいたaboutFragmentをiitemのidとします。

同じようにresディレクトリを右クリックしてMenuのリソースファイルを作成し、ドロワーメニューを作ります。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/aboutFragment"
        android:icon="@android:drawable/ic_menu_info_details"
        android:title="@string/about" />
    <item
        android:id="@+id/helpFragment"
        android:icon="@android:drawable/ic_menu_help"
        android:title="@string/help" />
</menu>

今回は各itemに合わせてiconの指定とfragmentのidを付けています。

さらに同じようにMenuリソースファイルを作成し、Fragment D に表示するshareボタンを作成します。

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/share"
        android:enabled="true"
        android:icon="@android:drawable/ic_menu_share"
        android:title="@string/share"
        android:visible="true"
        app:showAsAction="ifRoom" />
</menu>

ここではapp:showAsAction=”ifRoom”とすることでアイコンボタンのように表示が可能となります。

次はドロワーメニューを表示するために、DrawerLayoutとNavigationViewをactivity_main.xmlに追加していきます。

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <androidx.drawerlayout.widget.DrawerLayout
        android:id="@+id/drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context=".MainActivity">

            <fragment
                android:id="@+id/nav_host_fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:defaultNavHost="true"
                app:navGraph="@navigation/navigation" />

        </androidx.constraintlayout.widget.ConstraintLayout>

        <com.google.android.material.navigation.NavigationView
            android:id="@+id/navView"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_gravity="start"
            app:headerLayout="@layout/menu_header"
            app:menu="@menu/drawer" />
    </androidx.drawerlayout.widget.DrawerLayout>
</layout>

headerLayoutにはヘッダーとして表示させたいレイアウトを指定し、menuにはドロワーメニューとして表示するmenuリソースを指定します。

それでは実際の処理コードに移りましょう。

■ActionBarの処理

MainActivityで基本的な設定を行います。

class MainActivity : AppCompatActivity() {
    private lateinit var drawerLayout: DrawerLayout
    private lateinit var navController: NavController
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding =
            DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        drawerLayout = binding.drawerLayout

        navController = this.findNavController(R.id.nav_host_fragment)
        NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout)
        NavigationUI.setupWithNavController(binding.navView, navController)
    }

    override fun onSupportNavigateUp(): Boolean {
        return NavigationUI.navigateUp(navController, drawerLayout)
    }
}

NavigationUIクラスを使用して、setupActionBarWithNavControllerにコンテキストやナビゲーションコントローラ、ドロワーレイアウトを引数に指定し、それぞれが相互作用出来るように紐づけます。

setupWithNavControllerによりNavigationViewとnavControllerを紐づけて画面遷移が出来るようにします。あとは、onSupportNavigationUpでActionBarの戻るボタンを紐づけて全ての設定が完了です。

次はFragment Aを見ていきます。

class FragmentA : Fragment() {
    lateinit var binding: FragmentABinding
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        binding = DataBindingUtil.inflate(layoutInflater, R.layout.fragment_a, container, false)
        binding.nextButton.setOnClickListener {
            it.findNavController().navigate(FragmentADirections.actionFragmentAToFragmentB())
        }
        setHasOptionsMenu(true)
        return binding.root
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.menu, menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return NavigationUI.onNavDestinationSelected(item, requireView().findNavController())
    }
}

setHasOptionsMenu(true)を指定することでこのフラグメントにMenuがあることを設定します。

onCreateOptionsMenuはmenuを実際にinflateする処理を実装し、onOptionsItemSelectedではmenuのitemが選択された場合の処理を実装します。こちらもnavigationに紐づけるようにonNavDestinationSelectedを実行。

最後にFragment Dにshareの処理を実装します。

class FragmentD : Fragment() {
    val args: FragmentCArgs by navArgs()
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment
        val binding: FragmentDBinding =
            DataBindingUtil.inflate(inflater, R.layout.fragment_d, container, false)
        binding.resultText.text = args.destinationAbout
        setHasOptionsMenu(true)
        return binding.root
    }

    private fun getShareIntent() : Intent {
        return ShareCompat.IntentBuilder.from(requireActivity())
            .setText(getString(R.string.share_string, args.destinationAbout))
            .setType("text/plain")
            .intent
    }

    private fun shareSuccess() {
        startActivity(getShareIntent())
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.fragment_d_menu, menu)
        if (null == getShareIntent().resolveActivity(requireActivity().packageManager)) {
            menu.findItem(R.id.share).isVisible = false
        }
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.share -> shareSuccess()
        }
        return super.onOptionsItemSelected(item)
    }
}

同じようにmenuがあることをsetHasOptionsMenu(true)で設定し、onCreateOptionsMenuにてinflate、onOptionsItemSelectedで実際のshareする処理を実装しています。今回は暗黙的IntentをmimeTypeに”text/plain”を設定して他のアプリにシェアするような実装にしました。

onCreateOptionMenuではinflateの他にresolveActivityを使用して作成したintentに有効なActivityが存在するかを確認しています。なかった場合はnullとなるので、menuを非表示にする処理を行います。

もし有効なActivityがなかった場合にShareボタンが押されてしまうと例外でアプリが落ちてしまうので注意しないといけません。

■まとめ

・ActionBarとNavigationを使うことでより画面遷移を楽に制御できる

・ActionBarを有効活用し、マテリアルデザインのような振る舞いをすることでユーザビリティの向上を目指す

今回はActionBarを中心に学習しました。業務ではカスタムデザインでなかなかActionBarを使う機会がなかったので新鮮でした。Navigationと組み合わせることで有効に使えるのが非常に気に入ったので趣味でアプリを作る際には活用していこうと思います。

それでは。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です