Android CollapsingToolbarLayout collapse Listener

106

Estou usando CollapsingToolBarLayoutjunto com AppBarLayoute CoordinatorLayout, e eles estão funcionando perfeitamente bem. Defino meu Toolbarpara ser corrigido ao rolar para cima, quero saber se há uma maneira de alterar o texto do título da Barra de Ferramentas, quando CollapsingToolBarLayoutela estiver recolhida.

Concluindo, quero dois títulos diferentes quando rolados e quando expandidos .

Obrigado a todos antecipadamente

Anaximandro Andrade
fonte

Respostas:

150

Compartilho a implementação completa, baseada no código @Frodio Beggins e @Nifhel:

public abstract class AppBarStateChangeListener implements AppBarLayout.OnOffsetChangedListener {

    public enum State {
        EXPANDED,
        COLLAPSED,
        IDLE
    }

    private State mCurrentState = State.IDLE;

    @Override
    public final void onOffsetChanged(AppBarLayout appBarLayout, int i) {
        if (i == 0) {
            if (mCurrentState != State.EXPANDED) {
                onStateChanged(appBarLayout, State.EXPANDED);
            }
            mCurrentState = State.EXPANDED;
        } else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
            if (mCurrentState != State.COLLAPSED) {
                onStateChanged(appBarLayout, State.COLLAPSED);
            }
            mCurrentState = State.COLLAPSED;
        } else {
            if (mCurrentState != State.IDLE) {
                onStateChanged(appBarLayout, State.IDLE);
            }
            mCurrentState = State.IDLE;
        }
    }

    public abstract void onStateChanged(AppBarLayout appBarLayout, State state);
}

E então você pode usá-lo:

appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
    @Override
    public void onStateChanged(AppBarLayout appBarLayout, State state) {
        Log.d("STATE", state.name());
    }
});
rciovati
fonte
21
Está correto. Mas, por favor, não que usando Proguard esse enum vai ser traduzido em um valor inteiro.
rciovati de
1
Eu não sabia disso. Isso é ótimo!
tim687 de
2
Além disso, enums são uma maneira muito boa de garantir a segurança de tipo. Você não pode ter State.IMPLODED porque ele não existe (o compilador reclamaria), mas com constantes inteiras você poderia usar um valor que o compilador não tem idéia de estar errado. Eles são bons como solteiros também, mas isso é outra história.
droppin_science
@droppin_science para enums android confira IntDef
David Darias
1
@DavidDarias Pessoalmente, acho enums uma abordagem muito mais limpa, mesmo com sua sobrecarga (comece a discussão aqui ... :-)
droppin_science
95

Esta solução funciona perfeitamente para eu detectar AppBarLayoutcolapsos ou expandidos.

appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {

            if (Math.abs(verticalOffset)-appBarLayout.getTotalScrollRange() == 0)
            {
                //  Collapsed


            }
            else
            {
                //Expanded


            }
        }
    });

Usado addOnOffsetChangedListenerno AppBarLayout.

Muhamed Riyas M
fonte
36

Conecte um OnOffsetChangedListenerao seu AppBarLayout. Quando verticalOffsetatinge 0 ou menos que a Toolbaraltura, significa que CollapsingToolbarLayout foi recolhido, caso contrário, ele está se expandindo ou expandido.

mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
            @Override
            public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
                if(verticalOffset == 0 || verticalOffset <= mToolbar.getHeight() && !mToolbar.getTitle().equals(mCollapsedTitle)){
                    mCollapsingToolbar.setTitle(mCollapsedTitle);
                }else if(!mToolbar.getTitle().equals(mExpandedTitle)){
                    mCollapsingToolbar.setTitle(mExpandedTitle);
                }

            }
        });
Nikola Despotoski
fonte
1
Não está funcionando para mim. No OnCollapse, desejo habilitar o botão home e no Expand escondi o botão home
Maheshwar Ligade
9
Os valores verticalOffset parecem ser zero quando a barra de ferramentas está totalmente expandida e, em seguida, tornam-se negativos durante o recolhimento. Quando a barra de ferramentas é recolhida, verticalOffset é igual à altura negativa da barra de ferramentas (-mToolbar.getHeight ()). Então ... a barra de ferramentas é parcialmente expandida "if (verticalOffset> -mToolbar.getHeight ())"
Mike
Caso alguém esteja se perguntando onde está o appBarLayout.getVerticalOffset()método, você pode chamar appBarLayout.getY()para recuperar o mesmo valor que é usado no retorno de chamada.
Jarett Millard
Infelizmente Jarett Millard não está certo. Dependendo da configuração do fitSystemWindow e da configuração do StatusBar (transparente) appBarLayout.getY(), pode ser queverticalOffset = appBarLayout.getY() + statusBarHeight
Capricórnio
1
Alguém notou se mAppBarLayout.addOnOffsetChangedListener (listener) é chamado repetidamente, mesmo que não estejamos realmente interagindo com o appbar? Ou é um bug no meu layout / aplicativo onde estou observando esse comportamento. Plz help!
Rahul Shukla
16

Este código funcionou para mim

mAppBarLayout.addOnOffsetChangedListener(new   AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            if (verticalOffset == -mCollapsingToolbarLayout.getHeight() + mToolbar.getHeight()) {
                //toolbar is collapsed here
                //write your code here
            }
        }
    });
SAI
fonte
Melhor resposta do que Nikola Despotoski
Vignesh Bala
Parece não ser uma solução confiável. Eu testei e os valores no meu dispositivo são os seguintes: mCollapsingToolbarLayout.getHeight () = 1013, mToolbar.getHeight () = 224. Portanto, de acordo com sua solução, verticalOffset em estado recolhido deve ser -789, porém é igual a -693
Leo Droidcoder,
16
private enum State {
    EXPANDED,
    COLLAPSED,
    IDLE
}

private void initViews() {
    final String TAG = "AppBarTest";
    final AppBarLayout mAppBarLayout = findViewById(R.id.appbar);
    mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        private State state;

        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            if (verticalOffset == 0) {
                if (state != State.EXPANDED) {
                    Log.d(TAG,"Expanded");
                }
                state = State.EXPANDED;
            } else if (Math.abs(verticalOffset) >= appBarLayout.getTotalScrollRange()) {
                if (state != State.COLLAPSED) {
                    Log.d(TAG,"Collapsed");
                }
                state = State.COLLAPSED;
            } else {
                if (state != State.IDLE) {
                    Log.d(TAG,"Idle");
                }
                state = State.IDLE;
            }
        }
    });
}
Terrakok
fonte
10

Você pode obter a porcentagem alfa de recolhimento do ToolBar usando a seguir:

appbarLayout.addOnOffsetChangedListener( new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            float percentage = ((float)Math.abs(verticalOffset)/appBarLayout.getTotalScrollRange());
            fadedView.setAlpha(percentage);
    });

Para referência: link

Naveen Kumar M
fonte
2
Esta é uma ótima resposta, pois fornece um deslocamento normalizado. Na minha opinião, a API deveria ter fornecido isso diretamente em vez da verticalOffsetdistância do pixel.
dbm
5

Aqui está uma solução Kotlin . Adicione um OnOffsetChangedListenerao AppBarLayout.

Método A:

Adicione AppBarStateChangeListener.ktao seu projeto:

import com.google.android.material.appbar.AppBarLayout
import kotlin.math.abs

abstract class AppBarStateChangeListener : AppBarLayout.OnOffsetChangedListener {

    enum class State {
        EXPANDED, COLLAPSED, IDLE
    }

    private var mCurrentState = State.IDLE

    override fun onOffsetChanged(appBarLayout: AppBarLayout, i: Int) {
        if (i == 0 && mCurrentState != State.EXPANDED) {
            onStateChanged(appBarLayout, State.EXPANDED)
            mCurrentState = State.EXPANDED
        }
        else if (abs(i) >= appBarLayout.totalScrollRange && mCurrentState != State.COLLAPSED) {
            onStateChanged(appBarLayout, State.COLLAPSED)
            mCurrentState = State.COLLAPSED
        }
        else if (mCurrentState != State.IDLE) {
            onStateChanged(appBarLayout, State.IDLE)
            mCurrentState = State.IDLE
        }
    }

    abstract fun onStateChanged(
        appBarLayout: AppBarLayout?,
        state: State?
    )

}

Adicione o ouvinte ao seu appBarLayout:

appBarLayout.addOnOffsetChangedListener(object: AppBarStateChangeListener() {
        override fun onStateChanged(appBarLayout: AppBarLayout?, state: State?) {
            Log.d("State", state.name)
            when(state) {
                State.COLLAPSED -> { /* Do something */ }
                State.EXPANDED -> { /* Do something */ }
                State.IDLE -> { /* Do something */ }
            }
        }
    }
)

Método B:

appBarLayout.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
        if (abs(verticalOffset) - appBarLayout.totalScrollRange == 0) { 
            // Collapsed
        } else if (verticalOffset == 0) {
            // Expanded
        } else {
            // Idle
        }
    }
)
olearyj234
fonte
3

Esta solução está funcionando para mim:

@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
  if (i == 0) {
    if (onStateChangeListener != null && state != State.EXPANDED) {
      onStateChangeListener.onStateChange(State.EXPANDED);
    }
    state = State.EXPANDED;
  } else if (Math.abs(i) >= appBarLayout.getTotalScrollRange()) {
    if (onStateChangeListener != null && state != State.COLLAPSED) {
      onStateChangeListener.onStateChange(State.COLLAPSED);
    }
    state = State.COLLAPSED;
  } else {
    if (onStateChangeListener != null && state != State.IDLE) {
      onStateChangeListener.onStateChange(State.IDLE);
    }
    state = State.IDLE;
  }
}

Use addOnOffsetChangedListener no AppBarLayout.

Nifhel
fonte
Você pode compartilhar seu código completo? O que é State.EXPANDED etc?
Chetna,
1

Se você estiver usando CollapsingToolBarLayout, você pode colocar este

collapsingToolbar.setExpandedTitleColor(ContextCompat.getColor(activity, android.R.color.transparent));
collapsingToolbar.setTitle(title);
Irving Lóp
fonte
1

Este código está funcionando perfeitamente para mim. Você pode usar a escala de porcentagem como quiser

@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
    double percentage = (double) Math.abs(verticalOffset) / collapsingToolbar.getHeight();
    if (percentage > 0.8) {
        collapsingToolbar.setTitle("Collapsed");
    } else {
        collapsingToolbar.setTitle("Expanded");
    }
}
Artur Gniewowski
fonte
0

O valor de deslocamento da minha barra de ferramentas obtém -582 ao recolher, ao expandir = 0 Então, encontro o valor definindo o valor de deslocamento em Toast e altere o código de acordo.

 mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
            if(verticalOffset == -582) {
            Toast.makeText(MainActivity.this, "collaped" + verticalOffset, Toast.LENGTH_SHORT).show();
            mCollapsingToolbarLayout.setTitle("Collapsed");
            }else if(verticalOffset == 0){
                Toast.makeText(MainActivity.this, "expanded" + verticalOffset, Toast.LENGTH_SHORT).show();
            mCollapsingToolbarLayout.setTitle("expanded");
            }
        }
    });
Mazhar Ali
fonte