Xử lý AppBar

Thanh ứng dụng trên cùng (top app bar) có vị trí cố định trên đầu cửa sổ ứng dụng để hiển thị thông tin và hành động trên màn hình hiện tại.

ví dụ về thanh ứng dụng trên cùng
Hình 1. Ví dụ về thanh ứng dụng trên cùng.

Quyền sở hữu thanh ứng dụng thay đổi tuỳ thuộc vào nhu cầu của ứng dụng. Khi sử dụng các mảnh, thanh ứng dụng có thể được triển khai dưới dạng ActionBar thuộc sở hữu của hoạt động lưu trữ hoặc một thanh công cụ trong bố cục của mảnh.

Nếu tất cả các màn hình đều sử dụng cùng một thanh ứng dụng luôn nằm ở trên cùng và chiếm hết chiều rộng của màn hình, thì hãy sử dụng thanh thao tác do giao diện cung cấp được lưu trữ bởi hoạt động. Việc sử dụng thanh ứng dụng giao diện giúp giữ cho giao diện được nhất quán và cung cấp vị trí để lưu trữ trình đơn tuỳ chọn và nút Mũi tên lên.

Sử dụng thanh công cụ được mảnh lưu trữ nếu bạn muốn kiểm soát nhiều hơn về kích thước, vị trí và ảnh động của thanh ứng dụng trên nhiều màn hình. Ví dụ: có thể bạn cần thu gọn thanh ứng dụng hoặc một thanh chỉ chiếm một nửa chiều rộng của màn hình và được căn giữa theo chiều dọc.

Tình huống khác nhau đòi hỏi phương pháp khác nhau để thực hiện những việc như mở rộng trình đơn và phản hồi tương tác của người dùng. Việc nắm vững các phương pháp khác nhau và sử dụng một phương pháp phù hợp nhất cho ứng dụng sẽ giúp bạn tiết kiệm thời gian và đảm bảo ứng dụng hoạt động đúng cách.

Các ví dụ trong chủ đề này tham chiếu đến ExampleFragment chứa một hồ sơ có thể chỉnh sửa. Mảnh mở rộng trình đơn được XML xác định trên thanh ứng dụng của trình đơn:

<!-- 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>

Trình đơn này có hai tuỳ chọn: một để di chuyển đến màn hình hồ sơ và một để lưu mọi thay đổi đã thực hiện đối với hồ sơ.

Thanh ứng dụng thuộc sở hữu của hoạt động

Thanh ứng dụng thường thuộc sở hữu của hoạt động lưu trữ. Khi thanh ứng dụng thuộc quyền sở hữu của một hoạt động, mảnh có thể tương tác với thanh ứng dụng bằng cách ghi đè phương thức khung được gọi trong quá trình tạo mảnh.

Đăng ký kèm hoạt động

Bạn phải thông báo cho hệ thống rằng mảnh của thanh ứng dụng đang tham gia vào tập hợp của trình đơn tuỳ chọn. Để thực hiện việc này, gọi setHasOptionsMenu(true) trong phương thức onCreate(Bundle) của mảnh như được minh hoạ trong ví dụ sau:

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) cho hệ thống biết rằng mảnh của bạn muốn nhận các lệnh gọi lại liên quan đến trình đơn. Khi một sự kiện liên quan đến trình đơn xảy ra, chẳng hạn như nhấp chuột, phương thức xử lý sự kiện được gọi trước tiên trên hoạt động trước khi được gọi trên mảnh.

Tuy nhiên, đừng dựa vào thứ tự này trong logic ứng dụng. Nếu cùng một hoạt động lưu trữ nhiều mảnh, thì mỗi mảnh có thể cung cấp nhiều tuỳ chọn trình đơn. Trong trường hợp đó, thứ tự lệnh gọi lại phụ thuộc vào thứ tự thêm các mảnh.

Mở rộng trình đơn

Để hợp nhất trình đơn vào trình đơn tuỳ chọn của thanh ứng dụng, ghi đè onCreateOptionsMenu() trong mảnh của bạn. Phương thức này sẽ nhận trình đơn thanh ứng dụng hiện tại và một MenuInflater dưới dạng tham số. Sử dụng trình mở rộng trình đơn để tạo thực thể của trình đơn của mảnh, sau đó tích hợp trình đơn vào trình đơn hiện tại, như trong ví dụ sau:

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);
    }
}

Hình 2 cho thấy trình đơn được cập nhật.

trình đơn tuỳ chọn hiện chứa mảnh trình đơn của bạn
Hình 2. Trình đơn tuỳ chọn hiện chứa mảnh trình đơn của bạn.

Xử lý các sự kiện nhấp chuột

Mọi hoạt động và mảnh tham gia trình đơn tuỳ chọn đều có thể phản hồi với thao tác chạm. onOptionsItemSelected() của mảnh nhận mục trong trình đơn đã chọn dưới dạng tham số và trả về một boolean cho biết đã có thao tác chạm hay chưa. Sau khi một hoạt động hoặc mảnh trả về true từ onOptionsItemSelected(), thì không mảnh tham gia nào khác sẽ nhận được lệnh gọi lại.

Trong khi triển khai onOptionsItemSelected(), sử dụng câu lệnh switch trên itemId của mục trong trình đơn. Nếu mục được chọn là của bạn, hãy xử lý thao tác chạm thích hợp và trả về true để biểu thị sự kiện nhấp chuột đã được xử lý. Nếu mục được chọn không phải là của bạn, hãy gọi thao tác triển khai super. Theo mặc định, việc triển khai super trả về false để cho phép quá trình xử lý trình đơn tiếp tục.

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);
        }

    }

}

Sửa đổi trình đơn một cách linh động

Đặt logic để ẩn/hiện một nút hoặc thay đổi biểu tượng trong onPrepareOptionsMenu(). Phương thức này được gọi ngay trước khi hiển thị trình đơn.

Tiếp tục với ví dụ trước, nút Lưu chỉ hiển thị khi người dùng bắt đầu chỉnh sửa và nút sẽ biến mất sau khi người dùng lưu. Việc thêm logic này vào onPrepareOptionsMenu() giúp trình đơn hiển thị đúng cách:

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);
    }
}

Khi bạn cần cập nhật trình đơn, chẳng hạn như khi người dùng nhấn nút Chỉnh sửa để chỉnh sửa thông tin trong hồ sơ, hãy gọi invalidateOptionsMenu() trên hoạt động của máy chủ để yêu cầu hệ thống gọi onCreateOptionsMenu(). Sau khi vô hiệu hoá, bạn có thể thực hiện cập nhật trong onCreateOptionsMenu(). Sau khi trình đơn được mở rộng, hệ thống sẽ gọi onPrepareOptionsMenu() và cập nhật trình đơn để phản ánh trạng thái hiện tại của mảnh.

Kotlin

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

Java

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

Thanh ứng dụng thuộc sở hữu của mảnh

Nếu hầu hết màn hình trong ứng dụng của bạn không cần thanh ứng dụng hoặc nếu một màn hình cần một thanh ứng dụng khác với các thanh khác, bạn có thể thêm Toolbar vào bố cục mảnh. Mặc dù bạn có thể thêm Toolbar vào bất kỳ đâu trong hệ phân cấp khung hiển thị của mảnh, nhưng thường thì bạn nên giữ thanh công cụ ở đầu màn hình. Để sử dụng Toolbar trong mảnh, cung cấp mã nhận dạng và nhận tham chiếu đến mã đó trong mảnh của bạn, giống như cách bạn thực hiện với bất kỳ khung hiển thị nào khác. Bạn cũng có thể cân nhắc tạo ảnh động cho thanh công cụ bằng các hành vi CoordinatorLayout.

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

Khi sử dụng thanh ứng dụng thuộc sở hữu của mảnh, bạn nên trực tiếp sử dụng các API Toolbar. Không sử dụng setSupportActionBar() và API trình đơn Fragment (chỉ phù hợp với thanh ứng dụng thuộc sở hữu của hoạt động).

Mở rộng trình đơn

Toolbar phương thức tiện lợi inflateMenu(int) lấy mã nhận dạng của một tài nguyên trình đơn làm tham số. Để gia tăng tài nguyên của trình đơn XML vào thanh công cụ, chuyển resId vào phương thức này, như trong ví dụ sau:

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);
    }

}

Để gia tăng một tài nguyên khác của trình đơn XML, gọi lại phương thức này bằng resId của trình đơn mới. Các mục mới của trình đơn được thêm vào trình đơn và các mục của trình đơn hiện thời không bị sửa đổi hoặc xoá.

Nếu bạn muốn thay thế tập hợp trình đơn hiện có, hãy xoá trình đơn trước khi gọi inflateMenu(int) bằng mã nhận dạng trình đơn mới, như trong ví dụ sau:

Kotlin

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

Java

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

        viewBinding.myToolbar.getMenu().clear()

    }

}

Xử lý các sự kiện nhấp chuột

Bạn có thể trực tiếp chuyển OnMenuItemClickListener đến thanh công cụ bằng phương thức setOnMenuItemClickListener(). Trình nghe này được gọi khi người dùng chọn một mục trong trình đơn từ các nút hành động hiển thị ở cuối thanh công cụ hoặc trong trình đơn mục bổ sung liên quan. Phương thức đã chọn MenuItem được chuyển đến phương thức onMenuItemClick() của trình nghe và có thể được dùng để thực hiện hành động, như cho thấy trong ví dụ sau:

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;
            }
        });
    }
}

Sửa đổi trình đơn một cách linh động

Khi mảnh sở hữu thanh ứng dụng, bạn có thể sửa đổi Toolbar tại thời gian chạy giống hệt như khi bạn thực hiện với bất kỳ khung hiển thị nào khác.

Tiếp tục với ví dụ trước, tuỳ chọn trình đơn Save (Lưu) sẽ chỉ hiển thị khi người dùng bắt đầu chỉnh sửa và tuỳ chọn sẽ biến mất lần nữa khi được nhấn:

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);
    }

}

Nếu có, nút điều hướng sẽ xuất hiện ở đầu thanh công cụ. Khi đặt một biểu tượng điều hướng trên thanh công cụ, biểu tượng đó sẽ hiển thị. Bạn cũng có thể đặt một onClickListener() điều hướng cụ thể mà sẽ được gọi mỗi khi người dùng nhấp vào nút điều hướng, như trong ví dụ sau:

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.
            }
        });
    }
}