Materialdrawer: É possível usar o mesmo objeto Drawer em diferentes atividades?

Criado em 12 jul. 2016  ·  27Comentários  ·  Fonte: mikepenz/MaterialDrawer

Olá,

Recebo um erro se tento segurar o objeto Drawer e exibi-lo mais de uma vez.
É possível, por questões de desempenho, criar exatamente uma gaveta (estática) e usá-la em outras atividades?
Sei que a melhor prática seria fazer uma atividade com fragmentos, mas não é o caso.

Obrigado
A Costi

question

Comentários muito úteis

@acosti criou uma nova versão v5.3.5 que permite isso.
https://github.com/mikepenz/MaterialDrawer/releases/tag/v5.3.5

Todos 27 comentários

@acosti se você não pode usar uma atividade com fragmentos (que é definitivamente a implementação correta), você terá que usar uma abordagem diferente.

Definitivamente, NÃO há chance de ter a gaveta _stática_.

O que você pode fazer é ter uma classe auxiliar que cria a gaveta para você (com a mesma lógica) e você apenas chama esse auxiliar em todas as suas atividades. Mas a gaveta .build() deve ser chamada em cada uma dessas atividades, caso contrário, a gaveta não é adicionada.

Ok, então minha pergunta seria:
Existe uma maneira de a referida classe auxiliar criar a gaveta apenas uma vez, mas .build () várias vezes em diferentes estágios do aplicativo?

O racional vem do fato de que criar a gaveta não custa nada barato, pois ela é altamente customizada e interage com vários outros componentes do app. Portanto, recriá-lo a cada vez do zero seria suicídio em termos de desempenho.

@acosti sim, isso é possível. Você pode manter DrawerBuilder global uma vez. E chame build() por atividade então, pois isso iniciará o aumento dos layouts em seu aplicativo no final.

Ok Mike, obrigado. Agradeço a ajuda.

com licença, Mike, mas não parece funcionar.

Estou recebendo um erro explícito de tempo de execução:
"você não deve reutilizar um construtor DrawerBuilder" .. tenho que admitir, essa é uma das mensagens de erro mais informativas. Alguma ideia?

@acosti oh, esqueci esse comportamento porque tinha que evitar que as pessoas .build() várias vezes (o que resultaria em várias gavetas na mesma atividade)

A solução mais fácil seria manter as informações "caras" globais e reconstruir a gaveta normalmente. Suponho que obter os itens seja caro para você, então apenas manter esses itens globalmente ou assim?

sim, acho que essa é a solução sensata para o meu problema louco.
Se acontecer de você pensar em um melhor para segurar o objeto Drawer inteiro, eu adoraria saber.

de qualquer forma, obrigado pelas respostas rápidas. sua biblioteca me salvou muito tempo.

@acosti você poderia fazer uma solicitação de pull e adicionar um método reset ao DrawerBuilder que define o campo mUsed volta para falso.

https://github.com/mikepenz/MaterialDrawer/blob/develop/library/src/main/java/com/mikepenz/materialdrawer/DrawerBuilder.java#L71

isso permitiria que você fizesse isso. E ainda evitará a reutilização de DrawerBuilder para pessoas que não têm certeza sobre o que estão fazendo

@acosti criou uma nova versão v5.3.5 que permite isso.
https://github.com/mikepenz/MaterialDrawer/releases/tag/v5.3.5

Olá,

Tentei usar o método .reset () que você forneceu, mas estou enfrentando problemas.
Aqui está o erro que estou recebendo.

Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

O que eu fiz foi:

  1. Crie um DrawerBuilder
  2. Salve o DrawerBuilder como uma variável estática
  3. Construir () o DrawerBuilder na Atividade A
  4. Ao carregar a Atividade B - redefina () o DrawerBuilder
  5. Informe o DrawerBuilder sobre a nova atividade - .withActivity(Activity B)
  6. Construir () o DrawerBuilder na Atividade B

E então caiu.

@acosti Acho que isso pode estar relacionado à reutilização de DrawerLayout :
https://github.com/mikepenz/MaterialDrawer/issues/1399#issuecomment -233190027

Você pode tentar também chamar withDrawerLayout(-1) além de withActivity

Puta merda, isso foi rápido. Obrigado cara, sério.

Mesmo resultado, infelizmente. Deixe-me colar o log inteiro aqui.

FATAL EXCEPTION: main
                                                                                               Process: com.example.agombiprototypetest.prototypetest, PID: 20689
                                                                                               java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.agombiprototypetest.prototypetest/com.example.agombiprototypetest.prototypetest.Prototypes.core.aboutUs}: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
                                                                                                   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2416)
                                                                                                   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476)
                                                                                                   at android.app.ActivityThread.-wrap11(ActivityThread.java)
                                                                                                   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344)
                                                                                                   at android.os.Handler.dispatchMessage(Handler.java:102)
                                                                                                   at android.os.Looper.loop(Looper.java:148)
                                                                                                   at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                                                                   at java.lang.reflect.Method.invoke(Native Method)
                                                                                                   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                                                                   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)`

`Caused by: java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
                                                                                                   at android.view.ViewGroup.addViewInner(ViewGroup.java:4309)
                                                                                                   at android.view.ViewGroup.addView(ViewGroup.java:4145)
                                                                                                   at android.view.ViewGroup.addView(ViewGroup.java:4117)
                                                                                                   at com.mikepenz.materialdrawer.DrawerBuilder.createContent(DrawerBuilder.java:1629)
                                                                                                   at com.mikepenz.materialdrawer.DrawerBuilder.buildView(DrawerBuilder.java:1483)
                                                                                                   at com.mikepenz.materialdrawer.DrawerBuilder.build(DrawerBuilder.java:1281)
                                                                                                   at com.example.agombiprototypetest.prototypetest.BL.Navigation.DrawerNavigationModel.BuildDrawerWithToolbar(DrawerNavigationModel.java:119)
                                                                                                   at com.example.agombiprototypetest.prototypetest.BL.Navigation.DrawerNavigationModel.injectView(DrawerNavigationModel.java:96)
                                                                                                   at com.example.agombiprototypetest.prototypetest.BL.Models.AgombiNavActivity.onCreate(AgombiNavActivity.java:37)
                                                                                                   at android.app.Activity.performCreate(Activity.java:6237)
                                                                                                   at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1107)
                                                                                                   at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2369)
                                                                                                   at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2476) 
                                                                                                   at android.app.ActivityThread.-wrap11(ActivityThread.java) 
                                                                                                   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1344) 
                                                                                                   at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                                                                   at android.os.Looper.loop(Looper.java:148) 
                                                                                                   at android.app.ActivityThread.main(ActivityThread.java:5417) 
                                                                                                   at java.lang.reflect.Method.invoke(Native Method) 

Tenho certeza.

Aqui está o processo:

  1. A atividade A precisa de uma gaveta
  2. DrawerBuilder prepara a gaveta
  3. DrawerBuilder é vinculado à Atividade A por meio de .withActivity(Activity A) e também redefine o layout por meio de .withDrawerLayout(-1)
  4. DrawerBuilder é built()
  5. A atividade B precisa de uma gaveta
  6. DrawerBuilder recebe reset()
  7. DrawerBuilder é vinculado à Atividade B por meio de withActivity(Activity B) e também redefine o layout por meio de .withDrawerLayout(-1)
  8. DrawerBuilder é built

você poderia enviar estas etapas como amostra para que eu possa depurá-lo? obrigado

hmm isso vai ser um pouco difícil. deixe-me tentar juntar algo para você ver.

Ok, isso vai ser complicado. Por favor, tenha paciência comigo e terei o maior prazer em responder a qualquer pergunta o mais rápido possível.

Cada atividade que contém uma gaveta usa o seguinte código:

private void initDrawerForActivity(Context activityContext)
    {
        // Get the activity
        AgombiNavActivity activity = (AgombiNavActivity) activityContext;

        if (sSharedDrawer == null) {
            sSharedDrawer =generateDrawer(activityContext);
        }
        else {
            sSharedDrawer.reset();
        }

        sSharedDrawer.withActivity(activity)
                     .withDrawerLayout(-1);
    }

O código de geração de gaveta de generateDrawer(activityContext) é o seguinte:

private DrawerBuilder generateDrawer(Context context) {

        // Get the menu items
        RealmResults<AgombiComponentRealm> menuItems =
                NavigationManager.getInstance().getMenu();

        List<IDrawerItem> drawerItems = new LinkedList<>();

        for (final AgombiComponentRealm menuItem : menuItems) {

        // Get the drawer item externally
            PrimaryDrawerItem item =
                    AgombiViewLoader.getInstance().initializeMenuItem(context, menuItem);
            drawerItems.add(item);
        }

       // Get the footer externally
        ViewGroup footer = AgombiViewLoader.getInstance().initializeDrawerFooter(context);

        DrawerBuilder drawerBuilder = new DrawerBuilder()
                .withTranslucentStatusBar(false)
                .withDelayDrawerClickEvent(300)
                .withDrawerItems(drawerItems)
                .withFooter(footer)
                .withFooterDivider(false);
        // TODO: turn footer to sticky (removing its divider as sticky doesn't work - attempt to solve)

        return drawerBuilder;
    }

Agora, a atividade chama a função de construção. Mas não é tão simples.
Cada atividade em meu projeto é responsável por sua própria navegação, então uma atividade pode ser com uma gaveta, mas sem uma barra de ferramentas. Ou com uma barra de ferramentas. Então eu tenho que verificar isso primeiro.
A seguir está o código que verifica se uma barra de ferramentas é necessária ou não e, portanto, invoca o build () do DrawerBuilder.

public void injectView(ViewGroup activityLayout) {

        Drawer projectedDrawer = null;
        Toolbar potentialToolbar = getToolbar();
        // Check whether a toolbar is required. If so, build a combined Drawer + Toolbar
        if (potentialToolbar == null)
        {
            projectedDrawer = sSharedDrawer.build();
        }
        else
        {
            projectedDrawer = BuildDrawerWithToolbar(sSharedDrawer, activityLayout, getToolbar());
        }
    }

E a seguir está o código extra que constrói a gaveta com uma barra de ferramentas ...

private Drawer BuildDrawerWithToolbar(DrawerBuilder drawerBuilder, ViewGroup activityLayout, Toolbar optionalToolbar) {

        AttachToolbarToLayout(activityLayout, optionalToolbar);

        // Create the drawer
        return drawerBuilder
                .withToolbar(optionalToolbar)
                .withActionBarDrawerToggle(true)
                .withActionBarDrawerToggleAnimated(true)
                .build();
    }

Droga.
Espero que isso esteja suficientemente claro.
Desculpe, meu código é um pouco complexo. Tenho uma tarefa enorme a cumprir, mas meu conhecimento em Android é um WIP, haha. Espero que o código esteja bom :)

Avise-me se conseguir pegar alguma coisa, Mike.
Agradeço a ajuda e as respostas rápidas, de verdade.

Atualizada a formatação para facilitar o acesso

@mikepenz Como eu usaria fragmentos para fazer isso? Não estou familiarizado com fragmentos.

@acosti desculpe pela resposta tardia. É bastante complexo o acima exposto. Talvez postar um "zip" contendo o código de amostra simples seja mais fácil

@ caralin3 se você tiver uma atividade com fragmentos. Basta usar o ouvinte e alterar os fragmentos se o usuário selecionar um novo

@mikepenz Você tem um link que pode me ajudar a criar um fragmento?

@ caralin3 a documentação oficial do Android mostra como enviar fragmentos para uma visualização de contêiner
https://developer.android.com/guide/components/fragments.html

basta usar o ouvinte para detectar as seleções do usuário e trocar o fragmento

ei @mikepenz ,

Estou tentando uma nova abordagem: em vez de redesenhar a gaveta em cada atividade e em vez de ter a implementação "1 atividade - muitos fragmentos":

Vou fazer 1 atividade - muitas atividades.
isto é - há uma atividade de gaveta, responsável por segurar a gaveta e desenhá-la UMA VEZ.
e durante a navegação - o setContentView tem uma substituição que obtém o layout de destino, aumenta-o e o coloca no layout da atividade de uma gaveta em um "quadro de conteúdo".

Então, basicamente, a mesma implementação que você teria com fragmentos, exceto com atividades e autogerenciada.

Minha pergunta é: como você abordaria essa solução?

Eu estava pensando em simplesmente (espero que seja simples) substituir o layout da gaveta para adicionar o "quadro de conteúdo". a partir daí, deve ser fácil fazer a inflação do layout na substituição de setContentView. seus pensamentos?

Soneone tentou com Dagger2?

@acosti Lembro que havia uma biblioteca que fazia exatamente isso. Manter uma atividade, mas preenchendo-a com diferentes visões dependendo do contexto.

Não tenho certeza se esta é uma boa ou a melhor abordagem. O Android vem com um conceito muito bom e rico em recursos de Fragment s, que fará exatamente o que você precisa.

Também é bastante simples dividir as coisas em fragmentos e lidar com fragmentos e fazer a troca com Drawer pois você só precisa confirmar o fragmento correto quando um item é selecionado. Você pode até mesmo mover a lógica de comprometimento do fragmento inteiro para Listener da gaveta, já que você pode acioná-lo após o início, e também quando o onCreate é chamado com onSavedInstanceState configurando o fragmento correto novamente.

Ok, para finalmente encerrar este problema. Depois de mais escavações, depuração e tentativas. Não é realmente possível reutilizar o mesmo DrawerBuilder , visto que RecyclerView não fica feliz se Adapter for reutilizado.

A solução ideal para esse caso de uso é lembrar os dados complexos que você precisa buscar e, em seguida, apenas construir a gaveta com os itens, com base nos dados já carregados.

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

GutoMartins019 picture GutoMartins019  ·  4Comentários

oleynikd picture oleynikd  ·  4Comentários

ghost picture ghost  ·  3Comentários

Meeks91 picture Meeks91  ·  3Comentários

jd-alexander picture jd-alexander  ·  4Comentários