web-dev-qa-db-ja.com

マルチフラグメントアクティビティでonContextItemSelectedを処理する方法

現在、「Android v4」の互換性ライブラリを使用するようにアプリケーションを調整して、Android 1.6ユーザー。

コンテキストメニューの実装は難しいようです。

  • アプリケーションの主なアクティビティは、FragmentActivityクラスを拡張することです。
  • フラグメントはすべて、Fragmentクラスを拡張する1つのクラスに基づいています。
  • フラグメントクラスは、onCreateView()メソッドでregisterForContextMenu()を呼び出し、メソッドをオーバーライドしますonCreateContextMenu()およびonContextItemSelected()

onCreateContextMenu()の場合、これは非常にうまく機能します。コンテキストメニューはリソースファイルから拡張され、選択されたアイテム(フラグメントがListFragmentでなくてもlistView ...に基づいています)に基づいてわずかに変更されます。

この問題は、コンテキストメニューエントリが選択されたときに発生します。 onContextItemSelected()は、最初に追加されたフラグメントから始まる現在のすべてのフラグメントに対して呼び出されます。

私の場合、フラグメントはフォルダー構造のコンテンツを表示するために使用されます。サブフォルダーフラグメントのコンテキストメニューが開かれ、メニュー項目が選択されると、最初に上位レベルでonContextItemSelected()が呼び出されます(この瞬間に許可/表示されるフラグメントの数に応じて)。

今、私はonCreateContextMenu()を呼び出す最後のフラグメントのタグを保持するアクティビティレベルのフィールドによる回避策を使用します。このように、保存されたタグがgetTag()と同じでない場合、onContextItemSelected()の先頭で「return super.onContextItemSelected(item)」を呼び出すことができます。しかし、このアプローチは私には少し汚いように見えます。

すべてのフラグメントでonContextItemSelected()が呼び出されるのはなぜですか?呼び出していたものだけではありませんonCreateContextMenu()

これを処理する最もエレガントな方法は何ですか?

67
Lars K.

別の方法を見つけました。上記の私の問題については何も変わりませんが、無意味になります。

アプリケーションからコンテキストメニューを完全に削除しました。代わりに、リストアイテムのロングクリックをキャプチャし、この瞬間にアクションバーの表示ボタンを変更します。ユーザーの観点からは、これはコンテキストメニューのようなはるかにタブレットです。

下位互換性のあるアプリケーションでは、アクションバーは存在しません。そのため、Honeycombより前のデバイス用に独自の(上部にツールバーのようなもの)を作成することにしました。

コンテキストメニューを使用したい場合は、上記の回避策としてより良い解決策は見つかりませんでした。

2
Lars K.

同様の問題に対処したため、回避策を見つけたとしても、回答を投稿します。特定のフラグメントのコンテキストメニューを膨らませたら、各メニュー項目にそのフラグメントに固有のgroupIdを割り当てます。次に、「onContextItemSelected」でgroupIdをテストします。例えば:

public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
    menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_1, 0, R.string.src1);
    menu.add(UNIQUE_FRAGMENT_GROUP_ID, MENU_OPTION_2, 0, R.string.src2);
}
public boolean onContextItemSelected(MenuItem item) {
    //only this fragment's context menus have group ID of -1
    if (item.getGroupId() == UNIQUE_FRAGMENT_GROUP_ID) {
        switch(item.getItemId()) {
        case MENU_OPTION_1: doSomething(); break;
        case MENU_OPTION_2: doSomethingElse(); break;
    }
}

このように、すべてのフラグメントは引き続き「onContextItemSelected」の呼び出しを受信しますが、正しいものだけが応答するため、アクティビティレベルのコードを記述する必要はありません。 'menu.add(...)'を使用していない場合でも、この手法の修正版が機能する可能性があると思います

68
Rich Ehmer

別のソリューション:

@Override
public boolean onContextItemSelected(MenuItem item) {
    if (getUserVisibleHint()) {
        // context menu logic
        return true;
    }
    return false;
}

Jake Whartonの このパッチ に基づいています。

55
Sergey Glotov

私はSergei Gによるシンプルなソリューション(Jake Whartonの修正に基づく)が好きでしたが、いくつかのフラグメントに簡単に追加できるため、逆になりました。

public boolean onContextItemSelected(Android.view.MenuItem item) 
{  
    if( getUserVisibleHint() == false ) 
    {
        return false;
    }

    // The rest of your onConextItemSelect code
    AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
 }

その後、以前と同じコード。

8
azelez

非常に簡単な解決策を見つけました。 ContextMenuが作成されるたびにonCreateContextMenu()が呼び出されるため、ブール変数をtrueに設定します。

public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
    super.onCreateContextMenu(menu, v, menuInfo);
    MenuInflater inflater = getActivity().getMenuInflater();
    inflater.inflate(R.menu.film_menu, menu);
    bMenu=true;
}

私がしなければならない唯一の他のことは、その変数OnContextItemSelected()を要求することです

public boolean onContextItemSelected(MenuItem item) {
    if (bMenu) {
        bMenu=false;
        if (item.getItemId() == R.id.filmProperties) {
            ///Your code
            return true;
        } else {
            return super.onContextItemSelected(item);
        }
    } else {
        return super.onContextItemSelected(item);
    }
}

それでおしまい。

4
Rawpal

フラグメント内のリストビューでアダプターを使用している場合、これが役立つ場合があります。

public boolean onContextItemSelected(final MenuItem item) {
    final AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();

    //Check if the context menu call came from the list in this fragment (needed for support for multiple fragments in one screen)
    if (info.targetView.getParent() != getView().findViewById(Android.R.id.list))
        return super.onContextItemSelected(item);

    //Handle context menu item call
    switch (item.getItemId()) {
        ...
    }
}
1
user1103538

最初のフラグメントでは、すべてのメニューIDを5000以上に設定しているため、最初のフラグメントのonContextItemSelectedのコードの最初の行として

if (item.getItemId() < 5000) return false;

2番目のフラグメントが呼び出されます。

1
frusso

ただ変える

 @Override
    public boolean onContextItemSelected(MenuItem item) {
    return true;
 }

@Override
    public boolean onContextItemSelected(MenuItem item) {
    return super.onContextItemSelected(item); 
 }

そして、うまくいきます!!!

0
Amit Hooda

私見では、ターゲットビューがフラグメントリストビューの子であるかどうかを確認するだけです。それは非常にシンプルで、私にとってうまく機能します。古いAPIから移行するときに、すべてのフラグメントに追加しました:if (getListView.getPositionForView(info.targetView) == -1) return false

これは、親フラグメントの1つの例です。これはScalaですが、アイデアが得られれば幸いです。

_@Loggable
override def onContextItemSelected(menuItem: MenuItem): Boolean = {
  for {
    filterBlock <- TabContent.filterBlock
    optionBlock <- TabContent.optionBlock
    environmentBlock <- TabContent.environmentBlock
    componentBlock <- TabContent.componentBlock
  } yield menuItem.getMenuInfo match {
  case info: AdapterContextMenuInfo =>
    if (getListView.getPositionForView(info.targetView) == -1)
      return false
    TabContent.adapter.getItem(info.position) match {
      case item: FilterBlock.Item =>
        filterBlock.onContextItemSelected(menuItem, item)
      case item: OptionBlock.Item =>
        optionBlock.onContextItemSelected(menuItem, item)
      case item: EnvironmentBlock.Item =>
        environmentBlock.onContextItemSelected(menuItem, item)
      case item: ComponentBlock.Item =>
        componentBlock.onContextItemSelected(menuItem, item)
      case item =>
        log.debug("skip unknown context menu item " + info.targetView)
        false
    }
  case info =>
    log.fatal("unsupported menu info " + info)
    false
  }
} getOrElse false
_

追伸onContextItemSelected(...)の呼び出しをトレースする場合、super.onContextItemSelected(item)が常にfalseを返すことを通知できます。有効なonContextItemSelectedが呼び出されたWITHINではなくAFTERsuper.onContextItemSelected(item)は役に立たないので、falseに置き換えました。

0
Ezhik

変更されたメソッドではtrueを返します。 super.onContextItemSelected(item);を返します。 onContextItemSelected()オーバーライドですべてが機能し始めました。

0
Ali Imran

私は公開されているよりも簡単な解決策を見つけました:

public boolean onContextItemSelected(MenuItem item) {
    ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList);

    if (!yourList.hasFocus())
        return false;

    switch(item.getItemId()) {
        ...
    }
}
0