Android CollapsingToolbarLayout 折叠监听器

Posted

技术标签:

【中文标题】Android CollapsingToolbarLayout 折叠监听器【英文标题】:Android CollapsingToolbarLayout collapse Listener 【发布时间】:2015-10-19 08:43:09 【问题描述】:

我正在使用CollapsingToolBarLayout 以及AppBarLayoutCoordinatorLayout,它们完全可以正常工作。我将我的Toolbar 设置为在向上滚动时固定,我想知道是否有办法更改工具栏的标题文本,当 CollapsingToolBarLayout 它被折叠时。

总结一下,当滚动展开时,我想要两个不同的标题

提前谢谢大家

【问题讨论】:

【参考方案1】:

OnOffsetChangedListener 连接到您的AppBarLayout。当verticalOffset达到0或小于Toolbar高度时,说明CollapsingToolbarLayout已经折叠,否则正在展开或展开。

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

            
        );

【讨论】:

它对我不起作用。 OnCollapse 我想启用主页按钮并在展开时隐藏主页按钮 工具栏完全展开时,verticalOffset 值似乎为零,然后在折叠时变为负数。当工具栏收起时,verticalOffset 等于工具栏高度的负数 (-mToolbar.getHeight())。所以...工具栏部分展开“if (verticalOffset > -mToolbar.getHeight())” 如果有人想知道appBarLayout.getVerticalOffset() 方法在哪里,您可以调用appBarLayout.getY() 来检索回调中使用的相同值。 不幸的是,Jarett Millard 不对。根据您的 fitSystemWindow 配置和 StatusBar 配置(透明)appBarLayout.getY() 可能是 verticalOffset = appBarLayout.getY() + statusBarHeight 有没有人注意到即使我们实际上并没有与 appbar 交互,是否会重复调用 mAppBarLayout.addOnOffsetChangedListener(listener)?或者这是我观察到这种行为的布局/应用程序中的一个错误。请帮忙!【参考方案2】:

这个解决方案对我有用:

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

在 AppBarLayout 上使用 addOnOffsetChangedListener。

【讨论】:

你能分享你的完整代码吗?什么是 State.EXPANDED 等?【参考方案3】:
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;
            
        
    );

【讨论】:

【参考方案4】:

我分享基于@Frodio Beggins 和@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);

然后你就可以使用它了:

appBarLayout.addOnOffsetChangedListener(new AppBarStateChangeListener() 
    @Override
    public void onStateChanged(AppBarLayout appBarLayout, State state) 
        Log.d("STATE", state.name());
    
);

【讨论】:

没错。但请不要使用 Proguard 将枚举转换为整数值。 我不知道。太好了! 枚举也是确保类型安全的一种非常好的方法。您不能拥有 State.IMPLODED 因为它不存在(编译器会抱怨),但是使用整数常量,您可以使用编译器不知道是错误的值。他们也很适合单身人士,但那是另一回事了。 @droppin_science for android enums check out IntDef @DavidDarias 就我个人而言,我发现枚举是一种更简洁的方法,即使它们的开销也是如此(此处开始参数... :-)【参考方案5】:

这段代码对我有用

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

【讨论】:

比 Nikola Despotoski 更好的答案 似乎不是一个可靠的解决方案。我对其进行了测试,我的设备上的值如下:mCollapsingToolbarLayout.getHeight() = 1013, mToolbar.getHeight() = 224。因此根据您的解决方案,折叠状态下的垂直偏移必须为 -789,但它等于 -693 【参考方案6】:

这个解决方案非常适合我检测AppBarLayout 折叠或展开。

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

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


            
            else
            
                //Expanded


            
        
    );

AppBarLayout 上使用addOnOffsetChangedListener

【讨论】:

这起火灾在多个场合崩溃了,在展开时也是如此。【参考方案7】:

如果你使用的是 CollapsingToolBarLayout 你可以放这个

collapsingToolbar.setExpandedTitleColor(ContextCompat.getColor(activity, android.R.color.transparent));
collapsingToolbar.setTitle(title);

【讨论】:

【参考方案8】:

您可以使用以下方法获取 collapsingToolBar alpha 百分比:

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

供参考:link

【讨论】:

这是一个很好的答案,因为它提供了一个标准化的偏移量。在我看来,API 应该直接提供这个而不是 verticalOffset 像素距离。 我想当 AppBarLayout 折叠时隐藏视图并显示展开视图。所以我使用val percentage = 1 - abs(verticalOffset).toFloat() / appBarLayout.totalScrollRange。对我来说效果很好。【参考方案9】:

这段代码非常适合我。您可以使用百分比比例您喜欢的方式

@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");
    

【讨论】:

【参考方案10】:

我的工具栏偏移值在折叠时得到 -582,在展开时=0 所以我通过在 Toast 中设置 offsetvalue 来找到价值并相应地更改代码。

 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");
            
        
    );

【讨论】:

【参考方案11】:

这是一个 Kotlin 解决方案。将OnOffsetChangedListener 添加到AppBarLayout

方法一:

AppBarStateChangeListener.kt 添加到您的项目中:

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


将监听器添加到您的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 */ 
            
        
    
)

方法B:

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

【讨论】:

【参考方案12】:

这是 Kotlin 中的解决方案:

abstract class AppBarStateChangeListener : OnOffsetChangedListener 
    enum class State 
        EXPANDED, COLLAPSED, IDLE
    

    private var mCurrentState =
        State.IDLE

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

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

这里是监听器:

  appbar.addOnOffsetChangedListener(object : AppBarStateChangeListener() 
        override fun onStateChanged(
            appBarLayout: AppBarLayout?,
            state: State?
        ) 
            if(state == State.COLLAPSED)
                LayoutBottom.visibility = View.GONE
            else if(state == State.EXPANDED)
                LayoutBottom.visibility = View.VISIBLE
            
        
    )

【讨论】:

以上是关于Android CollapsingToolbarLayout 折叠监听器的主要内容,如果未能解决你的问题,请参考以下文章

CollapsingToolbar, Viewpager 有 recyclerview 和 NestedScrollview

如何确定 CollapsingToolbar 已折叠?

使用 CollapsingToolbar 滚动时如何更新工具栏

如何在CollapsingToolbar中使用Map的点击事件

AppBarLayout 上的 EdgeEffect

带有标签的Flutter SliverAppBar覆盖内容