Android, Kotlin: как я могу разрешить прокрутку окна просмотра влево только при необходимости
Ну привет дорогие зрители.
Моя ситуация:
У меня есть окно просмотра, которое позволяет пользователю проводить пальцем влево и вправо. У меня есть экран (фрагмент), который я хочу, чтобы пользователь завершил, прежде чем он сможет провести.
РЕДАКТИРОВАТЬ 4: РЕШЕНИЕ
Поскольку ViewPager2 работает как RecyclerView, я добавил в свой адаптер MutableList:
var fragmentList = mutableListOf<Fragment>(
BasicInfoFragment.newInstance(
activity
),
BondsFragment.newInstance(
activity
),
...
Вначале в списке присутствуют только фрагменты, отображаемые до "блокировки". Когда мое условие выполнено или выполнено, я добавляю следующие фрагменты в список адаптера, чтобы они были доступны для считывания.
Ниже приведены старые вещи, которые не работают:
Что я сделал:
Я настроил окно просмотра для блокировки пролистывания, когда я хочу, чтобы он был заблокирован (см. Мой код), но он отключает оба направления.
В идеале я хочу отключить смахивание только слева направо: я хочу, чтобы пользователь мог смахивать назад (справа налево). Я просто хочу отключить смахивание вперед (слева направо).
Я попытался реализовать onTouchListener на моем пейджере просмотра для определения направления прокрутки, но он работает только тогда, когда ViewPager разблокирован.
Можешь мне помочь?
Изменить 3: Новые проблемы
Я попытался реализовать viewpager2, но теперь у меня проблемы с дочерним Layout.
Когда мне нужно отобразить фрагмент в другом фрагменте, я использую метод расширения, который я написал на основе этого:как-добавить-фрагмент-в-котлине
Это выглядит так:
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, только если проведите пальцем справа налево.
Это немного сложно, поэтому, если у вас есть предложения...
2 ответа
Сначала я рекомендую использовать ViewPager2, поскольку он намного более оптимизирован с помощью реализации RecyclerView, что позволяет изящно удалять страницы без задержек кадра.
Однако независимо от вашего выбора реализации, я бы просто послушал, прокручивается ли страница (у них обоих есть интерфейс / обратный вызов onPageChange, который вы можете добавить / зарегистрировать, и когда position> currPage и (positionOffset == 0 OR scroll state == IDLE) (страница полностью осела), я бы удалил предыдущую страницу.
Дайте мне знать, если вам нужен пример, однако все, что указано выше, должно быть всем, что вам нужно сделать.
Если вы хотите сохранить свою старую реализацию, я бы зафиксировал текущее положение x пальца пользователя (правильно с onTouch), а если он меньше, установите ViewPager2.isUserInputEnabled = false
ViewPager2.isUserInputEnabled = newX >= originalX
Однако это сложно, поскольку пользователь может использовать несколько пальцев, а реализация ViewPager onScroll запускается только ПОСЛЕ того, как пользователь прокрутил страницу, создавая спазм, если вы отключаете прокрутку даже через 1 пиксель после того, как пользователь коснулся экрана, я рекомендую удалить страницу.
Решение - использовать physics
из NeverScrollableScrollPhysics()