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
@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.
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:
.withActivity(Activity 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)
@acosti hmm ele falha aqui: https://github.com/mikepenz/MaterialDrawer/blob/develop/library/src/main/java/com/mikepenz/materialdrawer/DrawerBuilder.java#L1629
Estranho o suficiente, pois este foi inflado recentemente durante .build()
aqui
https://github.com/mikepenz/MaterialDrawer/blob/develop/library/src/main/java/com/mikepenz/materialdrawer/DrawerBuilder.java#L1469
tem certeza de que ligou?
Tenho certeza.
Aqui está o processo:
.withActivity(Activity A)
e também redefine o layout por meio de .withDrawerLayout(-1)
built()
reset()
withActivity(Activity B)
e também redefine o layout por meio de .withDrawerLayout(-1)
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.
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