背景
ViewPager
在您执行一些滚动后捕捉到视图RecyclerView
,如果您使用以下内容,也可以:
LinearSnapHelper().attachToRecyclerView(recyclerView)
或者通过使用库来捕捉到某个边缘,如此库所示。ViewPager
如果您愿意,它几乎可以完全模仿,如 CommonsWare在这里提到的这个库所示。
如果需要,它们都可以以相同的方式运行(捕捉到单个视图,可选择占用整个可用空间),所以这个问题是关于它们的。
ViewPager 有一些回收视图的问题,但可以修复这些问题,例如使用这个库。
问题
只要 RecyclerView/ViewPager 即将在滚动中空闲,我就需要更新新显示视图的 UI。
当然,由于用户仍然可以触摸它,因此知道将要发生什么是一个问题,因此我需要在设置滚动状态时也阻止触摸事件。
这意味着,例如,当用户从第 0 页翻到第 1 页时,一旦开始捕捉,触摸事件就会被阻止,我将能够知道它捕捉到第 1 页并更新它页。
这里的问题是 RecyclerView 和 ViewPager 都不提供这个功能。我只能在它停止滚动后才能获得选择的项目,而不是在它稳定时。
我试过的
对于ViewPager
,适配器只有setPrimaryItem,所以很遗憾它告诉我在完成设置后选择了哪个项目。我确实有addOnPageChangeListener函数,它告诉我在任何给定时间的滚动位置(使用onPageScrolled),但它没有告诉我我去哪个方向(左/右),我不知道哪些数据和哪个 viewHolder即将被选中。addOnPageChangeListener
还提供滚动的状态(空闲、稳定、拖动)。
因为RecyclerView
我可以得到滚动状态的回调,它什么时候要稳定,什么时候它变得空闲,但我看不到如何获取它即将稳定的项目。
关于阻止触摸事件,我想我可以在它稳定时在它上面放置一个可点击的视图(没有内容,所以它对用户不可见),并GONE
在它空闲时隐藏它(将可见性设置为 ),但我想知道是否有个更好的方法。
我尝试使用setOnTouchListener
空闲RecyclerView
和稳定状态,但是当我尝试在它稳定时触摸时,它卡在了当前的滚动位置。
因此,RecyclerView
完成ViewPager
这一切都有障碍......
这是我目前的工作:
浏览器:
回收站视图:
我尝试过的 POC 代码(ViewPager
和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"
}
单元格.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>
gradle 文件“非标准”依赖项,来自这里和这里
//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'
问题
如何获取 ViewPager/RecyclerView 何时稳定的回调,包括即将捕捉到的项目?
如何阻止触摸事件从它稳定到空闲的时间?有没有比我写的更好的方法(在顶部有一个可点击的视图)?
更新:
似乎对于 ViewPager,我可以使用onPageSelected回调来获取它即将解决的项目。想知道以这种方式获取其页面的 ViewHolder 的最佳方法是什么。我可以保存所需的数据,onBindViewHolder
然后自己检查,但我想知道是否有更好的方法。
现在缺少的是如何做到这一点RecyclerView
以及如何阻止触摸事件(如果有比我写的更好的方法)。该库有一个名为 的函数addOnPageChangedListener
,但它是在设置完成后调用的,因此在这里无济于事。