Android CollapsingToolbarLayout collapse Listener

106

Estoy usando CollapsingToolBarLayoutjunto con AppBarLayouty CoordinatorLayout, y están funcionando bien por completo. Configuré mi Toolbarpara que sea fijo cuando me desplazo hacia arriba, quiero saber si hay alguna manera de cambiar el texto del título de la barra de herramientas, cuando CollapsingToolBarLayoutestá contraído.

Para terminar, quiero dos títulos diferentes cuando se desplaza y cuando se expande .

Gracias a todos de antemano

Anaximandro Andrade
fuente

Respuestas:

150

Comparto la implementación completa, basada en @Frodio Beggins y el código @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);
}

Y luego puedes usarlo:

appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() {
    @Override
    public void onStateChanged(AppBarLayout appBarLayout, State state) {
        Log.d("STATE", state.name());
    }
});
rciovati
fuente
21
Eso es correcto. Pero, por favor, tenga en cuenta que al usar Proguard esa enumeración se traducirá en un valor entero.
rciovati
1
Yo no lo sabía. ¡Eso es genial!
tim687
2
Además, las enumeraciones son una forma muy agradable de garantizar la seguridad de los tipos. No puede tener State.IMPLODED porque no existe (el compilador se quejaría) pero con las constantes Integer podría usar un valor que el compilador no tiene idea de que es incorrecto. También son buenos como singletons, pero esa es otra historia.
droppin_science
@droppin_science para enumeraciones de Android echa un vistazo a IntDef
David Darias
1
@DavidDarias Personalmente, encuentro que las enumeraciones tienen un enfoque mucho más limpio incluso con sus gastos generales (comience el argumento aquí ... :-)
droppin_science
95

Esta solución me funciona perfectamente para detectar AppBarLayoutcolapsados ​​o 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 addOnOffsetChangedListeneren AppBarLayout.

Muhamed Riyas M
fuente
36

Enganche a OnOffsetChangedListenera su AppBarLayout. Cuando verticalOffsetllega a 0 o menos que la Toolbaraltura, significa que CollapsingToolbarLayout se ha contraído; de lo contrario, se está expandiendo o expandiendo.

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
fuente
1
No está funcionando para mí. OnCollapse quiero habilitar el botón de inicio y en Expandir escondí el botón de inicio
Liga de Maheshwar
9
Los valores verticalOffset parecen ser cero cuando la barra de herramientas está completamente expandida y luego se vuelve negativo mientras se contrae. Cuando la barra de herramientas está contraída, verticalOffset es igual a la altura negativa de la barra de herramientas (-mToolbar.getHeight ()). Entonces ... la barra de herramientas está parcialmente expandida "if (verticalOffset> -mToolbar.getHeight ())"
Mike
En caso de que alguien se pregunte dónde está el appBarLayout.getVerticalOffset()método, puede llamar appBarLayout.getY()para recuperar el mismo valor que se usa en la devolución de llamada.
Jarett Millard
Desafortunadamente, Jarett Millard no tiene razón. Dependiendo de su configuración de fitSystemWindow y configuración de StatusBar (transparente) appBarLayout.getY(), puede ser queverticalOffset = appBarLayout.getY() + statusBarHeight
Capricornio
1
¿Alguien ha notado si mAppBarLayout.addOnOffsetChangedListener (oyente) se llama repetidamente incluso si no estamos interactuando con la barra de aplicaciones? O es un error en mi diseño / aplicación donde estoy observando este comportamiento. ¡Por favor ayuda!
Rahul Shukla
16

Este código funcionó para mi

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
            }
        }
    });
EFS
fuente
Mejor respuesta que Nikola Despotoski
Vignesh Bala
Parece no ser una solución confiable. Lo probé y los valores en mi dispositivo son los siguientes: mCollapsingToolbarLayout.getHeight () = 1013, mToolbar.getHeight () = 224. Entonces, de acuerdo con su solución, verticalOffset en estado contraído debe ser -789, sin embargo, es 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
fuente
10

Puede obtener el porcentaje alfa de collapsingToolBar usando a continuación:

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 referencia: enlace

Naveen Kumar M
fuente
2
Esta es una gran respuesta, ya que proporciona un desplazamiento normalizado. En mi opinión, la API debería haber proporcionado esto directamente en lugar de la verticalOffsetdistancia de píxeles.
dbm
5

Aquí tienes una solución de Kotlin . Agregue un OnOffsetChangedListeneral AppBarLayout.

Método A:

Agregue AppBarStateChangeListener.kta su proyecto:

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?
    )

}

Agregue el oyente a su 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
fuente
3

Esta solución funciona para mí:

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

Utilice addOnOffsetChangedListener en AppBarLayout.

Nifhel
fuente
¿Puedes compartir tu código completo? ¿Qué es State.EXPANDED, etc.?
Chetna
1

Si está utilizando CollapsingToolBarLayout, puede poner esto

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

Este código funciona perfectamente para mí. Puedes usar la escala de porcentaje como quieras

@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
fuente
0

El valor de compensación de mi barra de herramientas obtiene -582 cuando se contrae, en expandir = 0 Entonces, encuentro el valor estableciendo el valor de compensación en Toast y cambio el código en consecuencia.

 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
fuente