Android, Kotlin : bagaimana saya hanya mengizinkan tampilan pager saya digesek ke kiri saat diperlukan

Baiklah halo pemirsa yang budiman.

Situasi saya :

Saya memiliki halaman tampilan yang memungkinkan pengguna saya menggeser ke kiri dan kanan. Saya memiliki layar (sebuah fragmen), yang ingin saya selesaikan oleh pengguna sebelum dia dapat menggeseknya.

EDIT 4: SOLUSI

Karena ViewPager2 berfungsi seperti RecyclerView, saya telah menambahkan MutableList ke adaptor saya:

     var fragmentList = mutableListOf<Fragment>(
            BasicInfoFragment.newInstance(
                activity
            ),
            BondsFragment.newInstance(
                activity
            ),
    ...

Pada awalnya, dalam daftar, hanya ada fragmen yang ditampilkan sebelum penguncian. Ketika kondisi saya terpenuhi atau terpenuhi, saya menambahkan fragmen berikutnya ke daftar adaptor, sehingga tersedia untuk digesek

Di bawah ini adalah hal-hal lama yang tidak berfungsi:

Apa yang telah saya lakukan :

Saya menyesuaikan viewpager untuk mengunci gesekan ketika saya ingin menguncinya (lihat kode saya), tetapi menonaktifkan kedua arah.

Idealnya, saya hanya ingin menonaktifkan gesekan dari kiri ke kanan : Saya ingin pengguna dapat menggeser ke belakang (kanan ke kiri). Saya hanya ingin menonaktifkan gesekan ke depan (kiri ke kanan).

Saya mencoba menerapkan onTouchListener pada pager tampilan saya untuk mendeteksi arah gesekan, tetapi hanya berfungsi ketika ViewPager tidak terkunci.

Bisakah kamu membantuku ?

Sunting 3: Masalah baru

Saya telah mencoba mengimplementasikan viewpager2, tetapi sekarang saya mendapat masalah dengan Layout anak-anak.

Ketika saya perlu menampilkan sebuah fragmen ke dalam fragmen lain, saya menggunakan metode ekstensi yang saya tulis mendasarkan saya pada ini: cara-menambahkan-fragmen-di-kotlin-way

Sepertinya itu:

    inline fun FragmentManager.doTransaction(func: FragmentTransaction.() -> Unit) {
        Log.d(TAG, "doTransaction")
        val fragmentTransaction = beginTransaction()
        fragmentTransaction.func()
        fragmentTransaction.commit()
    }

    fun AppCompatActivity.replaceFragment(frameId: Int, fragment: Fragment) {
        Log.d(TAG, "replaceFragment")
            supportFragmentManager.doTransaction { replace(frameId, fragment) }
    }

Jadi saya punya beberapa fragmen yang memiliki hal-hal seperti ini:

    companion object : CustomCompanion() {
            @JvmStatic
            override fun newInstance(activity: Activity): IdealsFragment {
                val fragment =
                    IdealsFragment(
                        activity
                    )
                   
                (activity as NewCharacterActivity).replaceFragment(
                    R.id.fragmentIdeals_container_ideals,
                    IdealsRecyclerViewFragment.newInstance(
                        activity
                    )
                )
    
                return fragment
            }
        }

Masalahnya adalah dengan ViewPager2, saya tidak mendapatkan tampilan yang ditemukan untuk pengecualian id.

Saya mengerti itu karena saya menggunakan pengelola fragmen alih-alih ChildSupportFragmentManager, jadi saya mengganti pengelola fragmen dengan yang bagus, tapi sekarang saya mengalami error karena: Fragmen belum terpasang..

Sekali lagi, bisakah kamu membantuku?

Terima kasih banyak!

Kode :

 /**
 *   Class "LockableViewPager" :
 *   Custom viewpager that allows or not to swipe between fragments.
 **/
 class LockableViewPager : ViewPager {
    constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet)


    companion object {
        private const val TAG = "LockableViewPager"
    }

    /**
     * Is swiping enabled ?
     */
    private var swipeEnabled = true


    /**
     * Intercept all touch screen motion events.
     * Allows to watch events as they are dispatched, and
     * take ownership of the current gesture at any point.
     * 
     * @param event : The motion event being dispatched down the hierarchy.
     * @return Return true to steal motion events from the children and have
     * them dispatched to this ViewGroup through onTouchEvent().
     * The current target will receive an ACTION_CANCEL event, and no further
     * messages will be delivered here.
     */
    override fun onTouchEvent(event: MotionEvent): Boolean {
        return when (swipeEnabled) {
            true -> super.onTouchEvent(event)
            false -> //  Do nothing.
                return false
        }
    }
    

    /**
     * Implement this method to intercept all touch screen motion events.  This
     * allows you to watch events as they are dispatched to your children, and
     * take ownership of the current gesture at any point.
     *
     * @param event : The motion event being dispatched down the hierarchy.
     * @return Return true to steal motion events from the children and have
     * them dispatched to this ViewGroup through onTouchEvent().
     * The current target will receive an ACTION_CANCEL event, and no further
     * messages will be delivered here.
     */
    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
        return when (swipeEnabled) {
            true -> super.onInterceptTouchEvent(event)
            false -> swipeEnabled
        }
    }

    /**
     *  Sets the swipeEnabled value to lock or unlock swiping
     */
    fun setSwipeEnabled(swipeEnabled: Boolean) {
        this.swipeEnabled = swipeEnabled
    }    
}

Sunting 2 : Mungkin solusinya?

Saya meletakkan View mentah di bagian bawah tata letak aktivitas yang berisi view pager :

<View
    android:id="@+id/touch_layer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clickable="true" />

Saya membuat kelas abstrak yang mengimplementasikan View.OnTouchListener :

abstract class OverlayTouchListener : View.OnTouchListener {
    companion object{
        private const val TAG = "OverlayTouchListener"
    }
}

Deserialisasi tampilan overlay dalam aktivitas saya:

var touchLayer = this.findViewById<View>(R.id.touch_layer)

Menambahkan OverlayTouchListener ke touchLayer :

touchLayer?.setOnTouchListener(object : OverlayTouchListener() {
            /**
             * Called when a touch event is dispatched to a view. This allows listeners to
             * get a chance to respond before the target view.
             *
             * @param v The view the touch event has been dispatched to.
             * @param event The MotionEvent object containing full information about
             * the event.
             * @return True if the listener has consumed the event, false otherwise.
             */
            override fun onTouch(v: View?, event: MotionEvent?): Boolean {
                Log.d(TAG, "onTouch")
                if(event != null){
                    //  Check the swiping direction and ViewPager.isSwipeEnabled 
                    viewPager?.setSwipeEnabled(true)
                    viewPager?.onTouchEvent(event)
                }

                return true
            }

        })

Sekarang, saya hanya perlu memeriksa apakah swiping diaktifkan. Jika tidak, panggil viewPager.onTouchEvent hanya jika gesekan dilakukan dari kanan ke kiri.

Ini sedikit rumit, jadi jika Anda punya saran ...


person Ch4BRN    schedule 09.05.2020    source sumber


Jawaban (2)


pertama saya sarankan menggunakan ViewPager2 karena jauh lebih optimal dengan implementasi RecyclerView, memungkinkan Anda menghapus halaman dengan baik tanpa jeda bingkai.

Namun, apa pun pilihan implementasi Anda, saya hanya akan mendengarkan apakah halaman telah digulir (keduanya memiliki antarmuka/panggilan balik onPageChange yang dapat Anda tambahkan/daftarkan, dan ketika posisi > currPage dan (positionOffset == 0 OR status gulir == IDLE) (halaman telah selesai sepenuhnya), saya akan menghapus halaman sebelumnya.

Beri tahu saya jika Anda menginginkan contohnya, namun hanya semua hal di atas yang perlu Anda lakukan.

Jika Anda ingin mempertahankan implementasi lama Anda, saya akan menangkap lokasi x jari pengguna saat ini (benar dengan onTouch) dan jika kurang, setel ViewPager2.isUserInputEnabled = false

ViewPager2.isUserInputEnabled = newX >= originalX 

Namun, ini rumit karena pengguna dapat menggunakan beberapa jari dan implementasi onScroll ViewPager hanya terpicu SETELAH pengguna menggulir, menimbulkan jank jika Anda menonaktifkan gulir bahkan setelah 1 piksel setelah pengguna menyentuh layar, saya sarankan penghapusan halaman.

person PenguinDan    schedule 09.05.2020
comment
Terima kasih atas balasan dan rekomendasi Anda. Saya tidak tahu sama sekali kalau ada widget viewpager2. Salahku ! Saya merasa bahwa menggunakan Recyclerview membawa beberapa komplikasi dan mungkin beberapa kelambatan, namun saya tetap berjuang dengan viewpager. Saya akan mencoba menerapkan keduanya, hanya karena rasa ingin tahu, tetapi sepertinya pilihan yang tepat adalah viewpager2. - person Ch4BRN; 10.05.2020
comment
Saya hanya berharap saya dapat segera menemukan contoh untuk memahami cara mengimplementasikan VP2. - person Ch4BRN; 10.05.2020

Solusinya adalah dengan menggunakan physics dari NeverScrollableScrollPhysics()

person NN73 Player    schedule 09.05.2020
comment
apakah ini tidak hanya di Flutter? - person Ch4BRN; 09.05.2020