1

バックグラウンド

ViewPagerスクロールを実行した後、ビューにスナップしますRecyclerView。次のようなものを使用すると、スナップできます。

LinearSnapHelper().attachToRecyclerView(recyclerView)

または、このライブラリに示されているように、ライブラリを使用して特定のエッジにスナップします。CommonsWare hereで言及されているこのライブラリViewPagerに示されているように、必要に応じてほぼ完全に模倣できます。

どちらも必要に応じて同じように機能する (単一のビューにスナップし、オプションで使用可能なスペース全体を使用する) ことができるため、この質問は両方についてです。

ViewPager にはビューのリサイクルに関するいくつかの問題がありますが、たとえば、このライブラリを使用して修正できます。

問題

RecyclerView/ViewPager がスクロール中にアイドル状態になるとすぐに、新しく表示されたビューの UI を更新する必要があります。

もちろん、ユーザーはまだタッチできるので、何が起きようとしているのかを知るのは問題になるので、スクロール状態を整えるときにタッチ イベントもブロックする必要があります。

これは、たとえば、ユーザーが単一のページをページ 0 からページ 1 に移動すると、スナップが開始されるとすぐにタッチ イベントがブロックされ、ページ 1 にスナップされたことを認識してこれを更新できることを意味します。ページ。

ここでの問題は、RecyclerView と ViewPager の両方がこの機能を提供していないことです。スクロールが停止した後にのみ、選択されたアイテムを取得できます。

私が試したこと

の場合ViewPager、アダプターにはsetPrimaryItemしかないため、悲しいことに、解決が完了した後にどのアイテムが選択されているかがわかります。私はaddOnPageChangeListener関数を持っています。これは、( onPageScrolledを使用して) いつでもスクロール位置を教えてくれますが、どちらの方向 (左/右) に行くのか教えてくれません。それが選択されようとしています。addOnPageChangeListenerスクロールの状態 (アイドル、整定、ドラッグ) も提供します。

スクロール状態のコールバックを取得できるためRecyclerView、スクロール状態が安定する時期とアイドル状態になる時期を取得できますが、安定しようとしているアイテムを取得する方法がわかりません。

タッチイベントのブロックについてですが、落ち着いたらその上にクリッカブルなビュー(コンテンツがないので、ユーザーには見えません)を置いて、GONEアイドル状態のときは非表示(可視性を に設定)できると思いますが、より良い方法があります。

アイドル状態と整定状態で使用setOnTouchListenerしてみRecyclerViewましたが、整定中にタッチしようとすると、現在のスクロール位置で動かなくなりました。

したがって、 と の両方にRecyclerViewViewPagerすべてを完了するための障害があります...

これが私が現在働いているものです:

ViewPager:

ここに画像の説明を入力

RecyclerView:

ここに画像の説明を入力

私が試したもののPOCコード(との両方ViewPagerRecyclerView

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>

こことここから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'

質問

  1. ViewPager/RecyclerView が落ち着くときのコールバックを取得するにはどうすればよいですか?どのアイテムがスナップされるかを含めて?

  2. 安定してからアイドル状態になるまで、タッチイベントをブロックするにはどうすればよいですか? 私が書いたものよりも良い方法はありますか (上部にクリック可能なビューを表示する) ?


アップデート:

ViewPager の場合、onPageSelectedコールバックを使用して、解決しようとしているアイテムを取得できるようです。この方法でページの ViewHolder を取得する最良の方法は何だろうか。必要なデータを保存しonBindViewHolderて自分で確認することもできますが、もっと良い方法があるのではないかと思います。

今欠けているのは、それを行うRecyclerView方法とタッチイベントをブロックする方法です(私が書いたものよりも良い方法がある場合)。ライブラリには という関数がありますがaddOnPageChangedListener、セトリングが終わってから呼び出されるので、ここでは役に立ちません。

4

1 に答える 1