如何检测Android滚动视图是否处于空闲状态?

How to detect whether Android Scroll view is in idle state?

我试图在滚动视图处于空闲状态时隐藏按钮 state.the 按钮应该仅在滚动期间不可见。(其余时间按钮应该可见)

scrollView.setOnScrollChangeListener(new ScrollView.OnScrollChangeListener() {
    @Override
    public void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
        if (scrollX == scrollY)  {
            Button.setVisibility(View.VISIBLE);
        } else if ((scrollY > oldScrollY)) {
            Button.setVisibility(View.INVISIBLE);
        }
    }
});

试试下面的代码

@Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if(scrollState == SCROLL_STATE_IDLE){
            //do your stuff here
        }
}

找到解决方案

    scrollView.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch( View v, MotionEvent event ) {
            switch ( event.getAction( ) ) {
                case MotionEvent.ACTION_SCROLL:
                case MotionEvent.ACTION_MOVE:
                    Log.e( "SCROLL", "ACTION_SCROLL" );
                    break;
                case MotionEvent.ACTION_DOWN:
                    Log.e( "SCROLL", "ACTION_DOWN" );
                    break;
                case MotionEvent.ACTION_CANCEL:
                case MotionEvent.ACTION_UP:
                    Log.e( "SCROLL", "SCROLL_STOP" );
                    break;
            }
            return false;
        }
    });

尽管这个问题有点老,而且 ScrollView 没有像以前那样使用,但我不得不实现与此相关的东西,我根据建议和 Kotlin 使用了 NestedScrollView

如果您使用的是 Java,这将不可能或作为 'clean',您将需要创建静态 util 方法,使用 Kotlin,您可以执行扩展功能,如图所示。 如果您使用的是 ScrollView,只需将您在此处看到的内容从 NestedScrollView 更改为 ScrollView

我将展示两种实现方法:

首先,创建您的 NestedScrollViewExtensions.kt 文件:

1.

如果您想要一个简化的(我喜欢的)版本,只需将此片段添加到您的 NestedScrollViewExtensions.kt 文件中:


fun NestedScrollView.onScrollStateChanged(startDelay: Long = 100, stopDelay: Long = 400, listener: (Boolean) -> Unit) {
    setOnTouchListener { _, event ->
        when (event.action) {
            MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_MOVE -> {
                handler.postDelayed({
                    listener.invoke(true)
                }, startDelay)
            }

            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
                handler.postDelayed({
                    listener.invoke(false)
                }, stopDelay)
            }
        }
        false // Do not consume events
    }
}

这样实现更简洁:


        scroll_view.onScrollStateChanged { isScrolling -> 
            if(isScrolling) fab.shrink() else fab.extend()
        }

2

而且不太干净的方式


interface NestedViewScrollChangedListener {
    fun onScrollStart()

    fun onScrollStop()
}

@SuppressLint("ClickableViewAccessibility")
fun NestedScrollView.onScrollStateChanged(listener: NestedViewScrollChangedListener) {
    this.setOnTouchListener { _, event ->
        when(event.action) {
            MotionEvent.ACTION_SCROLL, MotionEvent.ACTION_MOVE -> {
                handler.postDelayed({
                    listener.onScrollStart()
                }, 100)
            }

            MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_UP -> {
                handler.postDelayed({
                    listener.onScrollStop()
                }, 400)
            }
        }
        false // Don't consume touch events
    }
}

在这里我使用了一个适合我的简单延迟,因为我需要让动画发生,随意删除所有 handler.postDelay() 并直接调用侦听器。

NestedViewScrollChangedListener上面的接口需要这样实现:


scroll_view.onScrollStateChanged(object : NestedViewScrollChangedListener {
            override fun onScrollStart() {
                fab.shrink()
            }

            override fun onScrollStop() {
                fab.extend()
            }
        })

祝你好运:)

在科特林中你可以使用这个

inline fun NestedScrollView.scrollState(crossinline idle: () -> Unit, crossinline scrolled: () -> Unit) {
    setOnTouchListener { _, event ->
        when (event.action) {
            MotionEvent.ACTION_SCROLL,
            MotionEvent.ACTION_MOVE,
            MotionEvent.ACTION_DOWN -> {
                scrolled()
            }
            MotionEvent.ACTION_CANCEL,
            MotionEvent.ACTION_UP -> {
                idle()
            }
        }
        false
    }
}