如何在RecyclerView和ViewPager上设置捕捉功能时获取即将被选中的页面 [英] How to get soon-to-be-selected page while settling snapping feature on RecyclerView and ViewPager

查看:110
本文介绍了如何在RecyclerView和ViewPager上设置捕捉功能时获取即将被选中的页面的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

ViewPager在执行一些滚动后会捕捉到一个视图,如果使用以下命令,RecyclerView也会捕捉到视图:

ViewPager snaps to a view after you perform some scrolling, and so can RecyclerView, if you use something like this:

LinearSnapHelper().attachToRecyclerView(recyclerView)

或通过使用库来捕捉到某个边缘,如 该库所示> .如您所愿,它几乎可以完全模仿ViewPager,如 该库 此处 提及.

Or by using a library to snap to a certain edge, as shown on this library. It can mimic ViewPager almost entirely if you wish, as shown on this library, mentioned by CommonsWare here.

如果需要,它们两个都可以以相同的方式起作用(捕捉到单个视图,可以选择占用整个可用空间),所以这个问题与两个都有关.

Both of them can function the same way if needed (snapping to a single view, optionally taking whole available space), so this question is about both of them.

ViewPager存在一些回收视图的问题,但是可以解决这些问题,例如使用 此库 .

ViewPager has some issues of recycling views, but those can be fixed, for example using this library.

当RecyclerView/ViewPager即将在滚动中处于空闲状态时,我需要更新新显示的视图的UI.

I'm required to update the UI of the newly shown view, as soon as the RecyclerView/ViewPager is about to be idle in scrolling.

当然,由于用户仍然可以触摸它,所以要知道将要发生的事情将是一个问题,因此,我还必须在其设置滚动状态时阻止触摸事件.

Of course, since the user can still touch it, it would be a problem of knowing what is about to occur, so I'm required to also block touch events when it's settling the scroll state.

例如,这意味着当用户从页面0跳到页面1时,一旦开始捕捉,触摸事件就会被阻止,我就能知道它捕捉到了页面1.并更新此页面.

This means, for example, that when the user flings a single page, from page 0 to page 1, as soon as the snapping starts, touch events are blocked, and I would be able to know that it snaps to page 1 and update this page.

这里的问题是RecyclerView和ViewPager均不提供此功能.我只能在停止滚动后才选择选定的项目,而不是在它稳定下来时得到.

The problem here is that both RecyclerView and ViewPager don't offer this functionality. I can get the item that's selected only after it stopped scrolling, not while it is settling.

对于ViewPager,适配器仅具有 addOnPageChangeListener 函数,它告诉我任何给定时间的滚动位置(使用 onPageScrolled ),但它不会告诉我我往哪个方向(向左/向右),并且我不知道它的哪个数据和哪个viewHolder即将被选中. addOnPageChangeListener还提供了滚动状态(空闲,稳定,拖动).

For ViewPager, the adapter only has setPrimaryItem , so sadly it tells me which item is selected after it finished settling. I do have addOnPageChangeListener function, which tells me about the scrolling position at any given time (using onPageScrolled), but it doesn't tell me which direction I go (left/right), and I can't know which data and which viewHolder of it is about to be selected. addOnPageChangeListener also provides the state of the scrolling (idle, settling, dragging).

对于RecyclerView,我可以获取滚动状态的回调,滚动状态的确定时间和空闲状态,但是我看不到如何获得滚动状态的项目.

For RecyclerView I can get a callback of the scrolling state, of when it's about to settle and when it becomes idle, but I can't see how to get the item it's about to settle on.

关于阻止触摸事件,我认为我可以在解决时在其上方放置一个可单击的视图(不包含任何内容,因此用户看不见),并在其隐藏时将其隐藏(将可见性设置为GONE)闲着,但我想知道是否有更好的方法.

About blocking touch events, I think I could put a clickable view (that has no content, so it's invisible to the user) on top of it when it's settling, and hide it (set visibility to GONE) when it's idle, but I wonder if there is a better way.

我尝试将setOnTouchListener用于RecyclerView空闲和稳定状态,但是当我在稳定状态下尝试触摸时,它卡在了当前滚动位置.

I tried using setOnTouchListener for RecyclerView idle and settling states, but when I tried touching while it's settling, it got stuck on its current scrolling location.

因此RecyclerViewViewPager都难以完成所有任务...

So both of RecyclerView and ViewPager have obstacles for getting it all done...

这是我目前正在工作的内容:

Here's what I got currently working :

ViewPager:

ViewPager:

RecyclerView:

RecyclerView:

我尝试过的POC代码(ViewPagerRecyclerView):

POC code of what I tried (both ViewPager and RecyclerView):

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val inflater = LayoutInflater.from(this)

        //viewPager area

        viewPager.adapter = object : RecyclerPagerAdapter<RecyclerPagerAdapter.ViewHolder>() {
            var selectedHolder: RecyclerPagerAdapter.ViewHolder? = null
            override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
                return object : RecyclerPagerAdapter.ViewHolder(inflater.inflate(R.layout.cell, parent, false)) {}
            }

            override fun getItemCount(): Int = 100

            override fun onBindViewHolder(holder: ViewHolder, position: Int) {
                (holder.itemView as TextView).text = position.toString()
            }

            override fun setPrimaryItem(container: ViewGroup?, position: Int, obj: Any?) {
                super.setPrimaryItem(container, position, obj)
                //TODO get the soon-to-be-selected page sooner
                val holder = obj as RecyclerPagerAdapter.ViewHolder
                if (selectedHolder != null && selectedHolder != holder)
                    (selectedHolder!!.itemView as TextView).text = position.toString()
                (holder.itemView as TextView).text = "selected:${position.toString()}"
                selectedHolder = holder
            }

        }
        viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrollStateChanged(state: Int) {
                when (state) {
                    ViewPager.SCROLL_STATE_DRAGGING -> Log.d("AppLog", "onPageScrollStateChanged: SCROLL_STATE_DRAGGING")
                    ViewPager.SCROLL_STATE_IDLE -> Log.d("AppLog", "onPageScrollStateChanged: SCROLL_STATE_IDLE")
                    ViewPager.SCROLL_STATE_SETTLING -> Log.d("AppLog", "onPageScrollStateChanged: SCROLL_STATE_SETTLING")
                }
            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                Log.d("AppLog", "onPageScrolled: position:$position positionOffset :$positionOffset positionOffsetPixels:$positionOffsetPixels")
            }

            override fun onPageSelected(position: Int) {
                Log.d("AppLog", "onPageSelected:" + position)
            }
        })

        //recyclerView area

        // Not needed, as I use a library for this: LinearSnapHelper().attachToRecyclerView(recyclerView)
        recyclerView.setHasFixedSize(true)
        recyclerView.adapter = object : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
            override fun getItemCount(): Int = 100

            override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): RecyclerView.ViewHolder {
                return object : RecyclerView.ViewHolder(inflater.inflate(R.layout.cell, parent, false)) {}
            }

            override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
                (holder.itemView as TextView).text = position.toString()
            }
        }
        recyclerView.addOnPageChangedListener { oldPosition, newPosition -> Log.d("AppLog", "OnPageChanged:$oldPosition->$newPosition") }
        recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
            @SuppressLint("ClickableViewAccessibility")
            override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) {
                super.onScrollStateChanged(recyclerView, newState)
                when (newState) {
                    RecyclerView.SCROLL_STATE_IDLE -> {
                        Log.d("AppLog", "state: SCROLL_STATE_IDLE")
                        recyclerViewStateTextView.text = "state: SCROLL_STATE_IDLE"
                        //setOnTouchListener doesn't really work well. It makes the scrolling stuck
                        //                        recyclerView!!.setOnTouchListener(null)
                    }
                    RecyclerView.SCROLL_STATE_SETTLING -> {
                        //TODO when settling, block touches, and update the soon-to-be-focused page
                        Log.d("AppLog", "state: SCROLL_STATE_SETTLING")
                        recyclerViewStateTextView.text = "state: SCROLL_STATE_SETTLING"
                        //                        recyclerView!!.setOnTouchListener(object : View.OnTouchListener {
                        //                            override fun onTouch(v: View?, event: MotionEvent?): Boolean {
                        //                                return true
                        //                            }
                        //
                        //                        })
                    }
                    RecyclerView.SCROLL_STATE_DRAGGING -> {
                        Log.d("AppLog", "state: SCROLL_STATE_DRAGGING")
                        recyclerViewStateTextView.text = "state: SCROLL_STATE_DRAGGING"
                    }
                }
            }
        })
        recyclerViewStateTextView.text = "state: SCROLL_STATE_IDLE"
    }

cell.xml

<TextView
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:id="@android:id/text1"
    android:textSize="36sp" tools:text="@tools:sample/lorem"/>

activity_main.xml

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:layout_height="match_parent" android:orientation="vertical"
    tools:context="com.example.user.snapblockertest.MainActivity">

    <TextView
        android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="ViewPager :"/>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager" android:layout_width="match_parent" android:layout_height="0px"
        android:layout_weight="1"/>

    <TextView
        android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:text="RecyclerView with snapping:"/>

    <com.lsjwzh.widget.recyclerviewpager.RecyclerViewPager
        android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="0px"
        android:layout_weight="1" android:orientation="horizontal"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager" app:rvp_singlePageFling="true"
        app:rvp_triggerOffset="0.1"/>

    <TextView
        android:id="@+id/recyclerViewStateTextView" android:layout_width="match_parent" android:layout_height="wrap_content"/>

</LinearLayout>

从这里到这里为文件非标准"依赖项

//https://github.com/henrytao-me/recycler-pager-adapter
implementation "me.henrytao:recycler-pager-adapter:2.1.0"
//https://github.com/lsjwzh/RecyclerViewPager
implementation 'com.github.lsjwzh.RecyclerViewPager:lib:v1.1.2@aar'

问题

  1. 如何获取ViewPager/RecyclerView何时沉降的回调,包括即将捕捉到哪个项目?

  1. How can I get a callback of when the ViewPager/RecyclerView is settling, including which item is about to be snapped to ?

如何解决从触摸事件稳定到空闲的事件?有没有比我写的更好的方法(在顶部有一个可单击的视图)?

How can I block the touch events from the time it's settling, till the time it's idle? Is there a better way than what I wrote (of having a clickable view on top) ?


更新:


Update:

似乎对于ViewPager,我可以使用 onPageSelected 回调,以获取要结算的商品.想知道用这种方式获取其页面的ViewHolder的最佳方法是什么.我可以将所需的数据保存在onBindViewHolder中,然后自己检查,但我想知道是否有更好的方法.

Seems that for ViewPager, I could use the onPageSelected callback to get which item it's about to settle on. Wonder though what's the best way to get the ViewHolder of its page this way. I could save the needed data in onBindViewHolder and then check it myself, but I wonder if there is a better way.

现在缺少的是如何为RecyclerView做到这一点以及如何阻止触摸事件(如果有比我写的更好的方法).该库具有一个名为addOnPageChangedListener的函数,但在结算完成后会调用该函数,因此在此无济于事.

Now what's missing is how to do it for RecyclerView and how to block touch events (if there is a better way than what I wrote). The library has a function called addOnPageChangedListener , but it's called after the settling has finished, so it can't help here.

推荐答案

似乎对于ViewPager,我可以使用onPageSelected回调来获取将要安置在哪个项目上.

Seems that for ViewPager, I could use the onPageSelected callback to get which item it's about to settle on.

所以这是ViewPager的解决方案(使用我使用的库):

So here's the solution for ViewPager (using the library I used) :

    val inflater = LayoutInflater.from(this)
    val viewPagerHolders = HashMap<Int, ViewPagerViewHolder>()
    viewPager.adapter = object : RecyclerPagerAdapter<ViewPagerViewHolder>() {

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPagerViewHolder {
            return ViewPagerViewHolder(inflater.inflate(R.layout.cell, parent, false))
        }

        override fun getItemCount(): Int = 100

        override fun onBindViewHolder(holder: ViewPagerViewHolder, position: Int) {
            holder.textView.text = position.toString()
            viewPagerHolders.remove(holder.adapterPosition)
            holder.adapterPosition = position
            viewPagerHolders[position] = holder

        }
    }
    viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
        var selectedHolder: ViewPagerViewHolder? = null

        override fun onPageScrollStateChanged(state: Int) {}
        override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}

        override fun onPageSelected(position: Int) {
            val holder = viewPagerHolders[position]
            if (holder != null) {
                if (selectedHolder != null && selectedHolder != holder)
                    selectedHolder!!.textView.text = selectedHolder!!.adapterPosition.toString()
                holder.textView.text = "selected:${position.toString()}"
                selectedHolder = holder
            }
        }
    })

还有一个ViewHolder类,该类仅记住与它关联的适配器位置(因为某些原因而丢失了它):

And a ViewHolder class that just remembers which adapter position is associated with it (because this is missing for some reason) :

class ViewPagerViewHolder(itemView: View) : RecyclerPagerAdapter.ViewHolder(itemView) {
    val textView: TextView = itemView.findViewById(android.R.id.text1)
    var adapterPosition: Int = Int.MIN_VALUE
}

由于它运行良好,即使没有阻止触摸事件功能,我也决定使用它.

Since it works so well, I decided I will use it, even without blocking touch events functionality.

仍然很高兴知道如何为RecyclerView做到这一点.

Still could be nice to know how to do it for RecyclerView.

对于RecyclerView,可以完成以下操作: https://stackoverflow.com/a/47580753/878126

for RecyclerView, this can be done: https://stackoverflow.com/a/47580753/878126

这篇关于如何在RecyclerView和ViewPager上设置捕捉功能时获取即将被选中的页面的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆