70

私は現在、「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()を呼び出していたものだけではありませんか?

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

4

11 に答える 11

71

同様の問題に対処したばかりなので、回避策が見つかりましたが、回答を投稿します。特定のフラグメントのコンテキスト メニューを拡張するときは、各メニュー項目にフラグメントに固有の 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(...)」を使用していなくても、この手法の修正版が機能すると思います

于 2011-11-18T20:58:20.203 に答える
55

別の1つの解決策:

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

JakeWhartonからのこのパッチに基づいています。

于 2012-04-12T14:32:31.720 に答える
5

私は非常に簡単な解決策を見つけました。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);
    }
}

それでおしまい。

于 2011-12-28T14:56:45.173 に答える
3

代替案を見つけました。上記の問題は何も変わりませんが、無意味になります。

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

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

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

于 2011-05-30T11:15:03.553 に答える
1

私の最初のフラグメントでは、すべてのメニューIDを5000より大きいように設定したので、最初のフラグメントのonContextItemSelectedのコードの最初の行として

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

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

于 2011-11-12T16:08:25.493 に答える
1

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

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()) {
        ...
    }
}
于 2014-07-22T14:53:33.473 に答える
0

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

これは、私の親フラグメントの 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

PS onContextItemSelected(...) の呼び出しをトレースすると、 always がsuper.onContextItemSelected(item)返されることを通知できますfalseWITHIN ではなく AFTER で呼び出された有効な onContextItemSelected 。Sosuper.onContextItemSelected(item)は役に立たないので、 に置き換えましたfalse

于 2012-08-01T11:03:53.367 に答える
0

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

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

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

    switch(item.getItemId()) {
        ...
    }
}
于 2015-05-29T17:29:08.860 に答える
0

変えるだけ

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

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

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

于 2012-06-08T11:38:04.590 に答える
0

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

于 2015-05-30T15:27:44.193 に答える