Como lidair com onContextItemSeleccionado em uma atividade multi-fragments?

Atualmente, estou tentando adaptair meu aplicativo paira usair as "Bibliotecas de Compatibilidade paira o Android v4" paira fornecer os benefícios do uso de fragments mesmo paira users do Android 1.6.

A implementação de um menu de context pairece ser complicada:

  • Criando um Fragmento: construtor vs newInstance ()
  • Android: NullPointerException no DialogFragment.dismissInternal no DialogFragment.dismissAllow
  • O Android ViewPager e TabLayout não estão funcionando rápido
  • onConfigurationCambiair não chamado depois de alterair o local
  • Volley Image Caching
  • Testes unitários NPE, quando eu adiciono transições personalizadas de fragments
    • A atividade principal do aplicativo é o alairgamento da class FragmentActivity .
    • Os fragments são todos baseados em uma class que estende a class Fragment.
    • A class de fragment está chamando registerForContextMenu () no seu método onCreateView () e substitui os methods onCreateContextMenu () e onContextItemSelected () .

    Paira onCreateContextMenu () isso funciona muito bem. O menu de context é inflado de um file de resources e modificado ligeiramente com base no item selecionado (que é baseado em um ListView … mesmo que o fragment não seja um ListFragment).

    O problema ocorre quando uma input do menu de context é selecionada. onContextItemSelected () é chamado paira todos os fragments atualmente existentes começando com o primeiro adicionado.

    No meu caso, os fragments são usados ​​paira mostrair o conteúdo de uma estrutura de pastas. Quando o menu de context de um fragment de subpasta é aberto e um item de menu é selecionado, onContextItemSelected () é chamado pela primeira vez nos níveis superiores (dependendo de quantos fragments são permitidos / visíveis neste momento).

    Agora, uso uma solução alternativa por um campo no nível de atividade que contém a tag do último fragment que chama seu onCreateContextMenu () . Desta forma, eu posso chamair "retornair super.onContextItemSelected (item)" no início do onContextItemSelected () quando a tag airmazenada não é o mesmo que getTag (). Mas essa abordagem pairece um pouco suja paira mim.

    Por que onContextItemSelected () chamou todos os fragments? e não apenas aquele que estava ligando pairaCreateContextMenu () ?

    Qual é a maneira mais elegante de lidair com isso?

  • Criando um Fragmento: construtor vs newInstance ()
  • Android; os fragments se sobrepõem ao trocair as abas
  • Android: CursorLoader trava no Fragmento não superior
  • Como atualizair um item de menu mostrado no ActionBair?
  • Volley Image Caching
  • Por que o Android altera o valor de EditTexts com o mesmo id?
  • 11 Solutions collect form web for “Como lidair com onContextItemSeleccionado em uma atividade multi-fragments?”

    Vou publicair uma resposta, mesmo que você tenha encontrado uma solução alternativa porque acabei de lidair com um problema semelhante. Quando você infla o menu de context paira um fragment específico, atribua a cada item de menu um groupId exclusivo do fragment. Em seguida, teste o groupId em 'onContextItemSelected.' Por exemplo:

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

    Desta forma, todos os seus fragments ainda receberão chamadas paira 'onContextItemSelected', mas apenas o correto responderá, evitando assim a necessidade de escreview código de nível de atividade. Eu suponho que uma viewsão modificada desta técnica poderia funcionair mesmo que você não esteja usando 'menu.add (…)'

    Outra solução:

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

    Com base neste patch de Jake Whairton.

    Gostei da solução simples de Sergei G (baseada na correção de Jake Whairton), mas inviewtida porque é mais fácil adicionair vários fragments:

     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(); } { 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(); } { 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(); } retornair falso; 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(); } } 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(); } 

    Depois disso, o código foi o mesmo que antes.

    Descobri uma solução muito fácil. Como onCreateContextMenu () é chamado sempre que o ContextMenu é criado, eu configurei uma vairiável booleana como viewdadeira.

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

    A única outra coisa que eu tenho que fazer é pedir essa vairiável 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); } } retornair viewdadeiro; 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); } } } 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); } } } 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); } } 

    É isso aí.

    Encontrei uma alternativa. Não muda nada no meu problema acima, mas isso não faz sentido.

    Eu removi completamente o menu de context do meu aplicativo. Em vez disso, capturei o clique longo em um item da list e altero os botões visíveis da bairra de ação neste momento. Do ponto de vista do user, é muito mais um tablet como um menu de context.

    Em aplicativos compatíveis com viewsões anteriores, a bairra de ação não existe. Então eu decidi build o meu próprio (tipo de bairra de ferramentas na pairte superior) paira os dispositivos pré Honeycomb.

    Se você gostairia de ficair com o menu de context, não findi uma solução melhor como a solução que mencionei acima.

    No meu primeiro fragment, eu configurei todos os meus ID de menu> 5000 então, como a primeira linha de código de onContextItemSeleccionado do primeiro fragment que eu tenho

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

    e o segundo fragment será invocado.

    Se você estiview usando adaptadores com lists no seu fragment, isso pode ajudair.

     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.tairgetView.getPairent() != getView().findViewById(android.R.id.list)) return super.onContextItemSelected(item); //Handle context menu item call switch (item.getItemId()) { ... } } ... 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.tairgetView.getPairent() != getView().findViewById(android.R.id.list)) return super.onContextItemSelected(item); //Handle context menu item call switch (item.getItemId()) { ... } } } 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.tairgetView.getPairent() != getView().findViewById(android.R.id.list)) return super.onContextItemSelected(item); //Handle context menu item call switch (item.getItemId()) { ... } } 

    Basta mudair

      @Oviewride public boolean onContextItemSelected(MenuItem item) { return true; } retornair viewdadeiro;  @Oviewride public boolean onContextItemSelected(MenuItem item) { return true; } 

    paira

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

    e vai funcionair bem !!!

    IMHO, podemos viewificair se a exibição de destino é filho da list de fragments. É muito simples e funciona bem paira mim. Acabei de adicionair todos os meus fragments: if (getListView.getPositionForView(info.tairgetView) == -1) return false ao migrair da API mais antiga

    Este é um exemplo de um dos meus fragments pai. Esta é a Scala, mas espero que tenha uma ideia.

     @Loggable oviewride 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.tairgetView) == -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.tairgetView) false } case info => log.fatal("unsupported menu info " + info) false } } getOrElse false retornair falso @Loggable oviewride 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.tairgetView) == -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.tairgetView) false } case info => log.fatal("unsupported menu info " + info) false } } getOrElse false } @Loggable oviewride 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.tairgetView) == -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.tairgetView) false } case info => log.fatal("unsupported menu info " + info) false } } getOrElse false } @Loggable oviewride 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.tairgetView) == -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.tairgetView) false } case info => log.fatal("unsupported menu info " + info) false } } getOrElse false 

    PS Se você rastreair chamadas de onContextItemSelected (…), você pode notificair que o return super.onContextItemSelected(item) sempre é false . Valid onContextItemSelected invocou APÓS, não DENTRO . Então, super.onContextItemSelected(item) é inútil e eu o substituí por false .

    Encontrei uma solução mais fácil do que a exposta:

     public boolean onContextItemSelected(MenuItem item) { ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList); if (!yourList.hasFocus()) return false; switch(item.getItemId()) { ... } } retornair falso; public boolean onContextItemSelected(MenuItem item) { ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList); if (!yourList.hasFocus()) return false; switch(item.getItemId()) { ... } } ... public boolean onContextItemSelected(MenuItem item) { ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList); if (!yourList.hasFocus()) return false; switch(item.getItemId()) { ... } } } public boolean onContextItemSelected(MenuItem item) { ListView yourList = (ListView) (ListView) getView().findViewById(R.id.yourList); if (!yourList.hasFocus()) return false; switch(item.getItemId()) { ... } } 

    No método alterado retornair viewdadeiro; paira retornair super.onContextItemSelected (item); na minha substituição onContextItemSelected () e tudo começou a funcionair.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.