0

TÌM HIỂU ACTION BAR COMPAT TRONG ANDROID – PHẦN 2

Tiếp tục chủ đề trong Phần 1: Tìm hiểu cách tạo ActionBar Compat trong Android

Trong bài này chúng ta đề cập đến các vấn đề sau:

  • Adding an Action View
  • Adding an Action Provider
  • Adding Navigation Tabs
  • Adding Drop-down Navigation

**1, Adding an Action View **

Một action view là 1 widget xuất hiện trong Action Bar (AB) như 1 thay thế cho các nút (button). Một action view cung cấp cho chúng ta 1 cách truy cập nhanh chóng (fast access) tới 1 hành động quan trọng nào đó mà không cần phải thay đổi giữa các Activity hay Fragment.

Ví dụ: Nếu bạn có 1 hành động Search (Tìm kiếm), bạn có thể thêm action view để nhúng 1 SearchView (widget) bên trong AB như hình sau:

ActionView in Action Bar

Để khai báo một action view, sử dụng 1 trong 2 thuộc tính "actionLayout" or "actionViewClass" để mô tả cho các mục (item) trong layout menu.xml. Ví dụ, để thêm 1 SearchView tới menu:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
    <item android:id="@+id/action_search"
          android:title="@string/action_search"
          android:icon="@drawable/ic_action_search"
          yourapp:showAsAction="ifRoom|collapseActionView"
          yourapp:actionViewClass="android.support.v7.widget.SearchView" />
</menu>

Chú ý: Thuộc tính "showAsAction" phải bao gồm giá trị "collapseActionView". Đây là tùy chọn để tuyên bố rằng action view này nên được thu gọn trong 1 button.

Nếu bạn cần cài đặt action view (như là thêm 1 sự kiện để lắng nghe), bạn có thể làm điều này bên trong hàm onCreateOptionMenu().

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_activity_actions, menu);
    MenuItem searchItem = menu.findItem(R.id.action_search);
    SearchView searchView = (SearchView) MenuItemCompat.getActionView(searchItem);
    // Configure the search info and add any event listeners
    ...
    return super.onCreateOptionsMenu(menu);
}

Chú ý: Trong Android API 11 hoặc cao hơn Để lấy về action view từ 1 menu item làm như sau:

SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView()

Xử lý thu gọn Action View

Để tiết kiệm không gian cho AB, bạn có thể thu gọn action view bên trong 1 button. Khi đóng lại, hệ thống có thể đặt các hành động của action view bên trong action overflow (nói trong bài trước), nhưng các action view vẫn xuất hiện bên trong AB khi người dụng lựa chọn chúng. Bạn có thể thực hiện việc thu gọn action view bằng cách thêm giá trị "collapseActionView" tới thuộc tính "showAsAction" trong item menu phía trên.

Bởi vì hệ thống mở rộng các action view khi người dụng lựa chọn nên bạn không cần phải xử lý bên trong onOptionsItemSelected() . Hệ thống vẫn gọi onOptionsItemSelected() , nhưng nếu bạn return true; (làm điều này khi thông báo cho hệ thống biết bạn sẽ tự xử lý hành động tiếp theo), action view sẽ không được mở rộng (expand) ra.

Hệ thống cũng tự động thu gọn action view của bạn khi người dùng nhấn nút Up hoặc nút Back.

Nếu bạn cần cập nhật Activity của bạn dựa trên sự xuất hiện của Action view, bạn có thể nhận về 1 callbacks khi hành động mở rộng và thu gọn diễn ra bằng việc định nghĩa 1 listerner OnActionExpandListener sau đó đưa vào hàm setOnActionExpandListener() của MenuItemCompat. Cho ví dụ:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.options, menu);
    MenuItem menuItem = menu.findItem(R.id.actionItem);
    ...

    // When using the support library, the setOnActionExpandListener() method is
    // static and accepts the MenuItem object as an argument
    MenuItemCompat.setOnActionExpandListener(menuItem, new OnActionExpandListener() {
        @Override
        public boolean onMenuItemActionCollapse(MenuItem item) {
            // Do something when collapsed
            return true;  // Return true to collapse action view
        }

        @Override
        public boolean onMenuItemActionExpand(MenuItem item) {
            // Do something when expanded
            return true;  // Return true to expand action view
        }
    });
}

2, Adding an Action Provider

Tương tự như Action View, một Action provider thay thế một action button với một layout được tùy chỉnh (customized). Tuy nhiên, không giống như action view, một action Provider kiêm soát tất cả các hành vi của action và chúng có thể được hiển thị như 1 menu con (submenu) khi được nhấn vào.

Để khai báo một action provider, cung cấp giá trị cho thuộc tính "actionProviderClass" bên trong thẻ <item> của menu một tên lớp đầy đủ cho 1 ActionProvider.

Bạn có thể xây dựng action provider của riêng bạn bằng việc kế thừa (extending) lớp ActionProvider. Trong Android cũng xây dựng một số action provider dựng sẵn (buil-in class) như ShareActionProvider, nó tạo một hành động "chia sẻ (share)" bằng cách hiển thị 1 danh sách các ứng dụng có thể dùng để chia sẻ trực tiếp qua AB.

Action Provider

Sử dụng ShareActionProvider

Để thêm một action "share" với ShareActionProvider tới AB, định nghĩa giá trị cho thuộc tính "actionProviderClass" trong thẻ <item> trong menu. Ví dụ:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:yourapp="http://schemas.android.com/apk/res-auto" >
    <item android:id="@+id/action_share"
          android:title="@string/share"
          yourapp:showAsAction="ifRoom"
          yourapp:actionProviderClass="android.support.v7.widget.ShareActionProvider"
          />
    ...
</menu>

Bây giờ action provider đưa ra kiểm soát của các action item và xử lý cả việc xuất hiện và hành vi của nó. Nhưng bạn vẫn phải cung cấp một tiêu đề (title) cho các mục được sử dụng khi nó xuất hiện trong phần action overflow (trong bài trước).

Điều duy nhất bạn phải làm là định nghĩa 1 Intent với kiểu nội dung bạn muốn chia sẻ. Để làm điều này, sửa phương thức onCreateOptionsMenu() để gọi MenuItemCompat.getActionProvider() và đưa vào 1 intent với cờ ACTION_SEND với nội dung phù hợp kèm theo.

Bạn nên gọi setShareIntent() một lần trong onCreateOptionsMenu () để khởi tạo các hành động chia sẻ, nhưng vì bối cảnh người dùng có thể thay đổi, bạn phải cập nhật Intent thông qua việc gọi lại setShareIntent().

Ví dụ:

private ShareActionProvider mShareActionProvider;

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    getMenuInflater().inflate(R.menu.main_activity_actions, menu);

    // Set up ShareActionProvider's default share intent
    MenuItem shareItem = menu.findItem(R.id.action_share);
    mShareActionProvider = (ShareActionProvider)
            MenuItemCompat.getActionProvider(shareItem);
    mShareActionProvider.setShareIntent(getDefaultIntent());

    return super.onCreateOptionsMenu(menu);
}

/** Defines a default (dummy) share intent to initialize the action provider.
  * However, as soon as the actual content to be used in the intent
  * is known or changes, you must update the share intent by again calling
  * mShareActionProvider.setShareIntent()
  */
private Intent getDefaultIntent() {
    Intent intent = new Intent(Intent.ACTION_SEND);
    intent.setType("image/*");
    return intent;
}

Tạo 1 Action Provider tùy chỉnh

Tạo Action provider của riêng của bạn cho phép bạn tái sử dụng và quản lý hành vi các item action trong một module khép kín. Như đã trình bày ở phần trước, Android đã cung cấp một class được triển khai ActionProvider cho hành động Share là ShareActionProvider.

Để tạo 1 action provider của riêng bạn cho các hành động khác, đơn giản chỉ cần kế thừa lớp ActionProvider và triển khai các phương thức callbacks của nó cho phù hợp. Quan trọng nhất bạn cần triển khai các phương thức sau đây:

ActionProvider()

Đây là hàm khởi tạo (constructor) với đối số là Context.

onCreateActionView(MenuItem)

Đây là nơi bạn định nghĩa các Action View cho các mục. Sử dụng Context lấy từ hàm khởi tạo để tạo ra 1 view của riêng bạn sử dụng LayoutInflater. Ví dụ:

public View onCreateActionView(MenuItem forItem) {
    // Inflate the action view to be shown on the action bar.
    LayoutInflater layoutInflater = LayoutInflater.from(mContext);
    View view = layoutInflater.inflate(R.layout.action_provider, null);
    ImageButton button = (ImageButton) view.findViewById(R.id.button);
    button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Do something...
        }
    });
    return view;
}

onPerformDefaultAction()

Hệ thống sẽ gọi hàm này khi menu được lựa chọn từ action overflow và action provider cần thực hiện các hành động (action) mặc định cho menu item.

Tuy nhiên, nếu action provider của bạn cung cấp một menu con (submenu), thông qua onPrepareSubMenu () , sau đó các menu con này sẽ xuất hiện ngay cả khi action provider được đặt trong action overflow. Do đó, phương thức onPerformDefaultAction () là không bao giờ được gọi là khi có một menu con.

3, Adding Tab điều hướng (Navigation Tabs)

Tabs ActionBar

Hình 7. Action bar Tab trên màn hình lớn

Tabs trong tAB làm cho nó trở nên dễ dàng cho người sử dụng để khám phá và chuyển đổi giữa các views khác nhau trong ứng dụng của bạn. Các tab được cung cấp bởi các ActionBar là lý tưởng vì chúng tương thích với nhiều kích thước màn hình khác nhau.

Tabs ActionBar

Hình 8: Action Bar Tabs trên màn hình bình thường

Ví dụ, khi màn hình đủ rộng cho các tab xuất hiện trong AB cùng với các nút (chẳng hạn như khi trên một máy tính bảng, thể hiện trong hình 7), trong khi khi trên màn hình hẹp chúng xuất hiện trong một thanh Tab riêng biệt thể hiện trong hình 8). Trong một số trường hợp, Android sẽ thay cho mục tab của bạn thành một danh sách thả xuống để đảm bảo phù hợp nhất cho AB.

Để bắt đầu, layout của bạn phải bao gồm một ViewGroup để đặt mỗi Fragment liên kết với một tab. Hãy chắc chắn các ViewGroup có một ID , do đó bạn có thể ánh xạ nó từ mã của bạn và trao đổi các tab bên trong nó. Ngoài ra, nếu nội dung tab tràn đầy màn hình, sau đó activity của bạn không cần triển khai layout (thậm chí không cần phải gọi setContentView ()). Thay vào đó, bạn có thể đặt mỗi Fragmebnt bên trong một root view mặc định, bạn có thể ánh xạ nó tới ID android.R.id.content.

Khi bạn đã xác định nơi các Fragment được xuất hiện, các bước cơ bản để bạn thêm Tab tới AB là:

  1. Triển khai interface ActionBar.TabListener . Interface này cung cấp các callbacks sự kiện cho Tab, như khi người dùng nhấn vào 1 trong số các Tab sau đó bạn có thể chuyển đổi giữa các tab.

  2. Mỗi khi bạn muộn tạo thêm 1 tab mới, bạn cần tạo ra thể hiện của ActionBar.Tab và gán ActionBar.TabListener bởi việc gọi setTabListener(). Cũng có thể gán tiêu đề (có thể gán kèm icon) cho tab sử dụng setText().

  3. Sau đó mỗi khi thêm 1 tab tới AB sử dụng phương thức addTab().

Chú ý phương thức callbacks ActionBar.TabListener không chỉ định Fragment được kết hợp với Tab, mà nó chỉ đơn thuần thông báo ActionBar.Tab được lựa chọn. Bạn phải xác định sử kết hợp giữa mỗi ActionBar.Tab và Fragment thích hợp mà nó đại diện. Có một số cách bạn có thể xác định sự liên kết, tùy thuộc vào thiết kế của bạn.

Ví dụ, đây là cách bạn có thể triển khai các ActionBar.TabListener mà mỗi tab sử dụng 1 thể hiện riêng của mình đẻ lắng nghe sự kiện:

public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
    private Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    /** Constructor used each time a new tab is created.
      * @param activity  The host Activity, used to instantiate the fragment
      * @param tag  The identifier tag for the fragment
      * @param clz  The fragment's Class, used to instantiate the fragment
      */
    public TabListener(Activity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    /* The following are each of the ActionBar.TabListener callbacks */

    public void onTabSelected(Tab tab, FragmentTransaction ft) {
        // Check if the fragment is already initialized
        if (mFragment == null) {
            // If not, instantiate and add it to the activity
            mFragment = Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content, mFragment, mTag);
        } else {
            // If it exists, simply attach it in order to show it
            ft.attach(mFragment);
        }
    }

    public void onTabUnselected(Tab tab, FragmentTransaction ft) {
        if (mFragment != null) {
            // Detach the fragment, because another one is being attached
            ft.detach(mFragment);
        }
    }

    public void onTabReselected(Tab tab, FragmentTransaction ft) {
        // User selected the already selected tab. Usually do nothing.
    }
}

Chú ý: Bạn không cần phải gọi phương thức commit() cho các Fragment transaction trong mỗi callbacks - hệ thống gọi nó cho bạn và nó có thể ném một ngoại lệ nếu bạn gọi nó cho mình. Bạn cũng không thể thêm Fragment transaction tới back stack.

Trong ví dụ trên, sự kiện lắng nghe chỉ đơn giản đính kèm một Fragment tới 1 layout trong activity, khi nó không tạoh ra sử phương thức add() để thêm 1 fragment tới layout (như là con của view group android.R.id.content ). Khi các tab khác được lựa chọn chỉ đơn giản bỏ đính kèm Fragment bằng cách sử dụng phương thức detach().

Tất cả những gì còn lại là tạo ra từng ActionBar.Tab và thêm nó vào ActionBar. Ngoài ra, bạn phải gọi setNavigationMode (NAVIGATION_MODE_TABS) để làm cho các tab có thể nhìn thấy. Ví dụ, đoạn mã sau thêm hai tab bằng cách sử dụng sự kiện lắng nghe tự định nghĩa ở trên:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // Notice that setContentView() is not used, because we use the root
    // android.R.id.content as the container for each fragment

    // setup action bar for tabs
    ActionBar actionBar = getSupportActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    actionBar.setDisplayShowTitleEnabled(false);

    Tab tab = actionBar.newTab()
                       .setText(R.string.artist)
                       .setTabListener(new TabListener<ArtistFragment>(
                               this, "artist", ArtistFragment.class));
    actionBar.addTab(tab);

    tab = actionBar.newTab()
                   .setText(R.string.album)
                   .setTabListener(new TabListener<AlbumFragment>(
                           this, "album", AlbumFragment.class));
    actionBar.addTab(tab);
}

Nếu hoạt động của bạn dừng lại (stop), bạn nên giữ lại các tab đang được chọn sử dung saved instance state lưu lại để bạn có thể mở các tab phù hợp khi người dùng trở về. Bạn có thể truy vấn các tab đang được chọn với getSelectedNavigationIndex (). Điều này trả về vị trí chỉ số của các tab được chọn.

4, Thêm vào điều hướng trượt xuống (Drop-down)

Như một chế độ điều hướng khác (hoặc dùng để lọc - filter) cho activity của bạn, AB cung cấp một danh sách trượt xuống (hay còn được gọi là một "spinner"). Ví dụ, trong danh sách trượt xuống có thể cung cấp các chế độ khác nhau mà nội dung trong activity được sắp xếp.

Sử dụng danh sách thả xuống rất hữu ích trong trường hợp thay đổi các nội dung quan trọng nhưng không thường xuyên xảy ra. Trong trường hợp nội dung được chuyển đổi thường xuyên hơn, bạn nên sử dụng các tab điều hướng thay thế.

Các bước cơ bản để cho phép chuyển hướng trượt xuống (drop-down) là:

  1. Tạo ra 1 SpinnerAdapter cung cấp danh sách các lựa chọn cho Drop-down list với các layout được sử dụng để thêm nội dung cho mỗi mục trong list.

  2. Triển khai ActionBar.OnNavigationListener để xác định hành vi xảy ra khi người sử dụng chọn một mục từ danh sách.

  3. Trong phương thức onCreate() của activity, cho phép AB sử dụng danh sách trượt xuống bằng việc gọi setNavigationMode(NAVIGATION_MODE_LIST).

  4. Gán 1 callback cho danh sách trượt xuống với setListNavigationCallbacks(). Ví dụ:

actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback);

Phương thức này bao gồm SpinnerAdapterActionBar.OnNavigationListener.

Phương thức này là tương đối ngắn, nhưng việc triển khai các SpinnerAdapter và ActionBar.OnNavigationListener là nơi mà hầu hết các công việcđược bạn phải triển khai. Có rất nhiều cách bạn có thể triển khai các chức năng để xác định chuyển hướng trong danh sách trượt xuống và triểnkhai nhiều dạng SpinnerAdapter là vượt ra ngoài phạm vi của tài liệu này (bạn nên tham khảo các tài liệu liên quan tới lớp SpinnerAdapter để biết thêm thông tin).


Mã nguồn: ActionBarCompat

Require: Android Support Libraries V7


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.