2012/02/03

ActionBarにDrop-downナビゲーションを追加する

ActionBarにCalendarやGmailのようなDrop-downナビゲーションを追加しようと思ったんです。

やり方自体は、SpinnerAdapterをActionBar.setListNavigationCallbacks()の引数に指定すれば良いのですが、公式ドキュメントに書いてあるサンプルは選択できる項目が予め決まっているResourceファイルから作るので、コーディングは最小限で済む代わりに動的に選択項目を作りたい場合などはこの方法は使えません。

で、軽くググって見た限りでは世の中にほとんどサンプルが無くて、ちょっと苦労したのでここに簡単にまとめておこうかと思います。そのうちTechBoosterさんにちゃんとした記事が載るような 気がするので、その時はこんないい加減なblogよりもそちらの方をどうかご参照ください(笑

SpinnerAdapterとして実装しないといけないインターフェースは下記
メソッド名概要
int getCount()選択項目の個数を返す
Object getItem(int position)position位置の要素を返す
long getItemId(int position)position位置の要素のIDを返す
int getItemViewType(int position)getView()で作成されるViewのタイプを返す
View getView(int position, View convertView, ViewGroup parent)position位置のViewを返す
int getViewTypeCount()Viewのタイプ数を返す
boolean hasStableIds()アイテムのIDがデータによらず安定しているか?
boolean isEmpty()リストが空か?
void registerDataSetObserver(DataSetObserver observer)データの変更を検出するオブザーバーを設定
void unregisterDataSetObserver(DataSetObserver observer)データの変更を検出するオブザーバーの設定を解除
View getDropDownView(int position, View convertView, ViewGroup parent)drop-downのpopupに表示されるViewを返す

ListViewなんかでAdapterに馴染みがあればほとんどのメソッドに関してはご存知かと思いますが、ここでの肝はgetView()とgetDropDownView()です。

メソッド名からもわかる?ように、getView()がActionBarに選択項目として描画されるViewを返し、getDropDownView()がdrop-downのポップアップに描画されるViewを返す為のメソッドです。

簡単にテキストだけ描画されるようなメニューならメソッド内でTextViewを作って返してやればいいようなものなんですが、getDropDownView()がどうにもうまく行きませんでした。コンパイルまでは問題無いのですが、ランタイム時にエラーが出てアプリが死にます。

LogCatのログを見てみると、どうもLayoutの型変換でエラーが出ているようなのですが、いろいろ試してもうまく行かないので最終的にはlayoutのxmlファイルを定義して、LayoutInflaterでViewを作成するようにしました。

ということで、最終的に以下のようなコードでうまくいったのですが、何か勘違いしているかも知れませんので、識者の方からアドバイスをいただけると幸いです。

SampleActivity.java
public class SampleActivity extends Activity implements OnNavigationListener {
  private ArrayList menuList = new ArrayList();
  private LayoutInflater mInflater;
  private ActionBar mActionBar;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    mInflater = (LayoutInflater)mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    mActionBar = getActionBar();
    mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
    mActionBar.setListNavigationCallbacks(new mSpinnerAdapter(), this);
 
    // drop-downメニューのデータを自由に作成
    // ここではコード上の文字列を設定してますが
    // 例えば、IntentのBundleで受け取ったデータ
    // をもとに作成してもいいでしょう
    menuList.add("One");
    menuList.add("Two");
    menuList.add("Three");
  }

  private class mSpinnerAdapter implements SpinnerAdapter {

    @Override
    public int getCount() {
      return menuList.size();
    }

    @Override
    public Object getItem(int position) {
      return menuList.get(position);
    }

    @Override
    public long getItemId(int position) {
      // とりあえずpositionをIDとして返す
      return position;
    }

    @Override
    public int getItemViewType(int position) {
      // 何を設定すればよいのかわからないのでとりあえず
      // IGNORE_ITEM_VIEW_TYPEを返す
      return IGNORE_ITEM_VIEW_TYPE;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {   
      if (convertView == null) {
        // getView()はこれでうまく行く
        convertView = new TextView(mContext);
        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        convertView.setLayoutParams(params);
      }
      ((TextView) convertView).setText(menuList.get(position));
      return convertView;   
    }

    @Override
    public int getViewTypeCount() {
      return 1;
    }

    @Override
    public boolean hasStableIds() {
      return false;
    }

    @Override
    public boolean isEmpty() {
      return false;
    }

    @Override
    public void registerDataSetObserver(DataSetObserver observer) {
      // TODO Auto-generated method stub   
    }

    @Override
    public void unregisterDataSetObserver(DataSetObserver observer) {
      // TODO Auto-generated method stub
    }

    @Override
    public View getDropDownView(int position, View convertView, ViewGroup parent) {
      if (convertView == null) {
        // getDropDownView()はうまくいかないので
        // LayoutInflaterでViewを作成
        convertView = mInflater.inflate(R.layout.dropdown_menu, parent, false);    
      }
      ((TextView) convertView.findViewById(R.id.dropdown_menu_item)).setText(menuList.get(position));
      return convertView;
    } 
  }

  @Override
  public boolean onNavigationItemSelected(int itemPosition, long itemId) {
    // itemPositionの選択に応じた処理
    return true;
  }
}

dropdown_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

 <TextView
        android:id="@+id/dropdown_menu_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

0 件のコメント:

コメントを投稿