Android, Kotlin: как я могу разрешить прокрутку окна просмотра влево только при необходимости

ну привет уважаемые телезрители.

Моя ситуация:

У меня есть окно просмотра, которое позволяет пользователю проводить пальцем влево и вправо. У меня есть экран (фрагмент), который я хочу, чтобы пользователь завершил, прежде чем он сможет провести.

РЕДАКТИРОВАТЬ 4: РЕШЕНИЕ

Поскольку ViewPager2 работает как RecyclerView, я добавил в свой адаптер MutableList:

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

Вначале в списке присутствуют только фрагменты, отображаемые до блокировки. Когда мое условие выполнено или выполнено, я добавляю следующие фрагменты в список адаптера, чтобы они были доступны для считывания.

Ниже приведены старые вещи, которые не работают:

Что я сделал:

Я настроил окно просмотра для блокировки пролистывания, когда я хочу, чтобы он был заблокирован (см. Мой код), но он отключает оба направления.

В идеале я хочу отключить смахивание только слева направо: я хочу, чтобы пользователь мог смахивать назад (справа налево). Я просто хочу отключить смахивание вперед (слева направо).

Я попытался реализовать onTouchListener на моем пейджере просмотра, чтобы определить направление прокрутки, но он работает только тогда, когда ViewPager разблокирован.

Вы можете мне помочь ?

Изменить 3: Новые проблемы

Я попытался реализовать viewpager2, но теперь у меня проблемы с дочерним Layout.

Когда мне нужно отобразить фрагмент в другом фрагменте, я использую метод расширения, который я написал на основе этого: как добавить-фрагмент-in-kotlin-way

Это выглядит так:

    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) }
    }

Итак, у меня есть несколько фрагментов с такими вещами:

    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
            }
        }

Дело в том, что с ViewPager2 я не нашел представления для исключения id.

Я понял, что это произошло потому, что я использовал диспетчер фрагментов вместо ChildSupportFragmentManager, поэтому я заменил диспетчер фрагментов на хороший, но теперь у меня сбой: Фрагмент еще не прикреплен ..

Еще раз, вы можете мне помочь?

Большое спасибо!

Код:

 /**
 *   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
    }    
}

Изменить 2. Может быть, решение?

Я помещаю необработанное представление в нижнюю часть макета действия, содержащего пейджер представлений:

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

Я создал абстрактный класс, реализующий View.OnTouchListener:

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

Десериализовал представление наложения в моем действии:

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

Добавлен OverlayTouchListener в 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
            }

        })

Теперь мне просто нужно проверить, включена ли прокрутка. В противном случае вызовите viewPager.onTouchEvent, только если проведите пальцем справа налево.

Это немного сложно, поэтому, если у вас есть предложения ...


person Ch4BRN    schedule 09.05.2020    source источник


Ответы (2)


Во-первых, я рекомендую использовать ViewPager2, поскольку он намного более оптимизирован с помощью реализации RecyclerView, что позволяет изящно удалять страницы без задержек кадра.

Однако, независимо от вашего выбора реализации, я бы просто послушал, прокручивается ли страница (у них обоих есть интерфейс / обратный вызов onPageChange, который вы можете добавить / зарегистрировать, и когда position> currPage и (positionOffset == 0 OR scroll state == IDLE) (страница полностью осела), я бы удалил предыдущую страницу.

Дайте мне знать, если вам нужен пример, однако все, что указано выше, должно быть всем, что вам нужно сделать.

Если вы хотите сохранить свою старую реализацию, я бы зафиксировал текущее положение x пальца пользователя (правильно с onTouch), а если меньше, установите ViewPager2.isUserInputEnabled = false

ViewPager2.isUserInputEnabled = newX >= originalX 

Однако это сложно, поскольку пользователь может использовать несколько пальцев, а реализация ViewPager onScroll запускается только ПОСЛЕ того, как пользователь прокручивает страницу, создавая спам, если вы отключаете прокрутку даже через 1 пиксель после того, как пользователь коснулся экрана, я рекомендую удалить страницу.

person PenguinDan    schedule 09.05.2020
comment
Спасибо за ваш ответ и вашу рекомендацию. Совершенно не знал, что есть виджет viewpager2. Виноват ! Я чувствовал, что использование Recyclerview вызывает некоторые сложности и, возможно, некоторые задержки, но я упорно боролся с viewpager. Я попытаюсь реализовать и то, и другое, просто из любопытства, но мне кажется, что правильным выбором будет viewpager2. - person Ch4BRN; 10.05.2020
comment
Я просто надеюсь, что быстро найду примеры, чтобы понять, как реализовать VP2. - person Ch4BRN; 10.05.2020

Решение состоит в том, чтобы использовать physics из NeverScrollableScrollPhysics()

person NN73 Player    schedule 09.05.2020
comment
это не только на Flutter? - person Ch4BRN; 09.05.2020