Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

“Android programming authority guide” chapter 14, began to touch the application bar, the application look good, very practical. Mature apps on the market should also be designed into a unified style, create a good brand image drop! It could also be called an action bar or a toolbar, but it’s a different name.

The default AppCompat app bar

Android Studio adds a default application bar to any activity that inherits AppCompatActivity when creating a new project.

In the app/build. Gradle file, see depend on the implementation ‘androidx. Appcompat: appcompat: 1.3.0’. AppCompat is short for Application Compatibility. It contains many core classes and resources to keep the UI uniform across Android versions, most of which comply with Material Design guidelines.

All version update please refer to the specific: developer.android.com/jetpack/and… Maybe the component has been updated with a more useful feature, so use it without further ado.

When creating a new project, AS will give you a default theme. Open androidmanifest.xml and find the theme for your application.

    <application
        ...
        android:theme="@style/Theme.AndroidGuideApp">
        ...
    </application>
Copy the code

Pressing CTRL (Windows) or Command (MAC) to jump past will take you to res -> Value /value-night -> themes.xml, which sets some default styles for the theme of the entire application.

<resources xmlns:tools="http://schemas.android.com/tools"> <! -- Base application theme. --> <style name="Theme.AndroidGuideApp" parent="Theme.MaterialComponents.DayNight.DarkActionBar"> <! -- Primary brand color. --> <item name="colorPrimary">@color/purple_500</item> <item name="colorPrimaryVariant">@color/purple_700</item> <item name="colorOnPrimary">@color/white</item> <! -- Secondary brand color. --> <item name="colorSecondary">@color/teal_200</item> <item name="colorSecondaryVariant">@color/teal_700</item> <item name="colorOnSecondary">@color/black</item> <! -- Status bar color. --> <item name="android:statusBarColor" tools:targetApi="l">? attr/colorPrimaryVariant</item> <! -- Customize your theme here. --> </style> </resources>Copy the code

Second, the Application bar menu

The App bar menu consists of menu items (also known as action items). In the upper right area of the App bar, add a new crime menu item.

  • 1. Define menus in XML files

Create a menu folder in res and create fragment_crime_list. XML. Set the resource type to menu.

<? The XML version = "1.0" encoding = "utf-8"? > <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="@android:drawable/ic_menu_add" android:title="@string/new_crime" app:showAsAction="ifRoom|withText" /> </menu>Copy the code

ShowAsAction specifies whether the menu item should be displayed in the application bar or hidden in the Overfow menu.

This combination means that menu item ICONS and their text descriptions are displayed in the app bar whenever there is enough space. If there is only enough space to display the menu item icon, the text description is not displayed; If there is not enough space to show any items, the menu item is hidden in the overflow menu.

The overflow menu is represented by three dots.

ShowAsAction has two other optional values: always and never. Always is not recommended. Instead, use the ifRoom attribute value and let the operating system decide how to display menu items. For menu items that are rarely used, the Never attribute value is a good choice.

For compatibility, the AppCompat library needs to use the APP namespace.

  • 2. Create a menu

The Activity class provides a callback function for managing menus, onCreateOptionsMenu(Menu), and the Fragment has its own set of options Menu callback functions.

class CrimeListFragment : Fragment() {
...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }
...
    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        super.onCreateOptionsMenu(menu, inflater)
        inflater.inflate(R.menu.fragment_crime_list, menu)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return super.onOptionsItemSelected(item)
    }
...

}
Copy the code

Long press the + sign to pop up the New Crime title. In landscape mode, the app bar displays the Menu icon and title.

  • 3. Respond to menu item selection

Add addCrime to crimelistViewModel.kt:

    fun addCrime(crime: Crime) {
        crimeRepository.insertCrimes(crime)
    }
Copy the code

In crimelistfragment. kt, implement the onOptionsItemSelected(MenuItem) function in response to the MenuItem selection event.

override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId){ R.id.new_crime->{ val crime = Crime() crimeListViewModel.addCrime(crime) callBacks? .onCrimeSelected(crime.id) true } else -> return super.onOptionsItemSelected(item) } }Copy the code

OnOptionsItemSelected (MenuItem) returns a Boolean value. Once menu item event processing is complete, the function should return true to indicate that the task is complete. If false is returned, the onOptionsItemSelected(MenuItem) function that hosts the activity is called to continue. (If the hosted activity hosts other fragments, they will also call the onOptionsItemSelected function.) Also, by default, the superclass version function is called if the menu item ID does not exist.

Use Android Asset Studio

ICONS used by the app:

  • System icon: a built-in icon of the Android operating system
  • Project Resource icon

The display style of system ICONS varies greatly between devices or operating system versions. It is not recommended to use the system icon “System icon can be found in the Android SDK installation directory”. Instead, find an appropriate icon and copy it to the drawable resource directory of the project.

Another way to unify the icon style is to use the Android Asset Studio tool built into Android Studio to create or customize images for the app bar.

drawable -> New -> Image Asset

It then generates ICONS for each screen size. Nice! 👏 🏻

Then go to menu there to change the icon path! 🤩

4. In-depth study: Application bar, operation bar and toolbar

The UI design elements of the application bar, toolbar, and action bar are called application bars themselves.

Before Android 5.0 (Lollipop, API level 21), the app bar was implemented using ActionBar classes, and the “ActionBar” and “app bar” were identical concepts.

Starting with Android 5.0, the app bar takes precedence over the new Toolbar class implementation.

Like the Toolbar, the ActionBar is built on top of the ActionBar. In addition to visual tweaks to the UI, the toolbar is more flexible than the action bar.

The action bar has many limitations. For example, there can only be one action bar for the entire application and its location and size must be fixed (at the top of the screen). Toolbars don’t have this limitation, and they can also support inline views and height adjustments, which greatly enriches the application’s interaction mode.

Fifth, in-depth study: AppCompat version application bar

To use the AppCompat version application bar, you need to reference the supportFragmentManager property of AppCompatActivity.

Here’s the code from the book:

        val appCompatActivity = activity as AppCompatActivity
        val appBar = appCompatActivity.supportActionBar as Toolbar
        appBar.setTitle(R.string.some_cool_title)
Copy the code

An error:

The actionBar obtained here is already in androidx, the usage will be a little different, and we will study it again when we have time. If you just want to change the title, you can add it directly:

val appCompatActivity = activity as AppCompatActivity val appBar = appCompatActivity.supportActionBar appBar? .title = appCompatActivity.resources.getString(R.string.some_cool_title)Copy the code

Note that if you need to modify the content of the application bar Menu in the current activity user interface, you can call invalidateOptionsMenu() to trigger the onCreateOptionsMenu(Menu, MenuInfater) callback. In the onCreateOptionsMenu callback, after encoding changes to the menu contents, all changes take effect immediately after the callback ends.

On the Toolbar API reference: developer.android.com/reference/k…

RecyclerView empty view

It is now time to delete the default list database initialized, instead of inserting some data into the database when the application starts. Add an empty view to the list if there is no data.

First imitate the previous code, add a new type of recyclerView itemType is empty layout type

    enum class ITEM_TYPE {
        ITEM_TYPE_NOMAL,
        ITEM_TYPE_POLICE,
        ITEM_TYPE_EMPTY
    }
Copy the code

Add a field to the CrimeAdapter to indicate whether the CrimeAdapter needs to display the empty layout. By default, the field is not displayed. GetItemViewType = 0; getItemViewType = 0; getItemViewType = 0; getItemViewType = 0; getItemViewType = 0 Return the empty layout type. Since the empty layout also has no data to bind, this is also checked in onBindViewHolder. The code looks like this:

private inner class CrimeAdapter(var crimes: List<Crime>) : Recyclerview.adapter < recyclerview.viewholder >() {// whether to display empty layout, Private var showEmptyView = false Override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return when (viewType) { ITEM_TYPE.ITEM_TYPE_POLICE.ordinal -> CrimePoliceHolder( ItemCrimePoliceBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) ITEM_TYPE.ITEM_TYPE_NOMAL.ordinal -> CrimeHolder( ItemCrimeBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) else -> EmptyHolder( ItemEmptyViewBinding.inflate( LayoutInflater.from(parent.context), parent, false ) ) } } override fun getItemViewType(position: Int): Int { return if (position == 0 && isEmptyPosition()) { ITEM_TYPE.ITEM_TYPE_EMPTY.ordinal } else { if (crimes[position].requiresPolice) { ITEM_TYPE.ITEM_TYPE_POLICE.ordinal } else { ITEM_TYPE.ITEM_TYPE_NOMAL.ordinal } } } override fun getItemCount(): Int { val count = crimes.size return if (count > 0) { showEmptyView = false count } else { if (showEmptyView) { 1 } else  { 0 } } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { if (! (position == 0 && isEmptyPosition())) { val crime = crimes[position] if (holder is CrimeHolder) { holder.bind(crime) } Else if (holder is CrimePoliceHolder) {holder.bind(crime)}} /** * Show empty view */ showEmptyView() { ShowEmptyView = true notifyDataSetChanged()} /** * Check whether the layout is empty */ fun isEmptyPosition(): Boolean { val count = if (crimes.isEmpty()) 0 else crimes.size return showEmptyView && count == 0 } } private inner class EmptyHolder(val itemEmptyViewBinding: ItemEmptyViewBinding) : RecyclerView.ViewHolder(itemEmptyViewBinding.root) { init { itemEmptyViewBinding.imgAddCrime.setOnClickListener { val crime = Crime() crimeListViewModel.addCrime(crime) callBacks? .onCrimeSelected(crime.id) } } }Copy the code

The above code removes DiffUtil, because when displaying an empty layout, you also need to return an item, but this item is an empty layout that fills the screen, and unlike other types, Crime would not have used diffUtil.itemCallback to compare data changes the way it used diffUtil.itemCallback, and would have reported the following error.

Therefore, the code has been restored and the empty layout display has been successfully added. DiffUtil needs to be studied carefully.

Seven, other

CriminalIntent project Demo address: github.com/visiongem/A…


🌈 follow me ac~ ❤️

Public account: Ni K Ni K