Android,Kotlin:我怎样才能只允许我的视图寻呼机在需要时向左滑动
Android, Kotlin : how can I only allow my view pager swiping on the left when needed
亲爱的观众您好。
我的情况:
我有一个 viewpager,可以让我的用户左右滑动。我有一个屏幕(片段),我希望用户在滑动之前完成它。
编辑 4:解决方案
由于 ViewPager2 像 RecyclerView 一样工作,我在我的适配器中添加了一个 MutableList :
var fragmentList = mutableListOf<Fragment>(
BasicInfoFragment.newInstance(
activity
),
BondsFragment.newInstance(
activity
),
...
一开始,列表中只有“锁定”之前显示的片段。
当满足或满足我的条件时,我将下一个片段添加到适配器的列表中,这样它们就可以刷卡了
下面是一些旧的不起作用的东西:
我做了什么:
我将 viewpager 自定义为在我想要锁定时锁定滑动(请参阅我的代码),但它禁用了两个方向。
理想情况下,我只想禁用从左向右滑动:我希望用户可以向后滑动(从右向左)。我只想禁用向前滑动(从左到右)。
我试图在我的视图寻呼机上实现一个 onTouchListener 来检测滑动方向,但它只在 ViewPager 解锁时有效。
你能帮帮我吗?
编辑 3:新问题
我已经尝试实现 viewpager2,但现在子布局出现问题。
当我需要将一个片段显示到另一个片段中时,我使用基于此编写的扩展方法:
how-to-add-a-fragment-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:也许是解决方案?
我将原始视图放在包含视图寻呼机的 activity 布局的底部:
<View
android:id="@+id/touch_layer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" />
我创建了一个实现 View.OnTouchListener 的摘要 class :
abstract class OverlayTouchListener : View.OnTouchListener {
companion object{
private const val TAG = "OverlayTouchListener"
}
}
在我的 activity 中反序列化叠加视图:
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。
有点棘手,所以如果你有任何建议...
首先,我推荐使用 ViewPager2,因为它通过 RecyclerView 实现得到了极大的优化,允许您优雅地删除页面而没有帧滞后。
但是,无论您选择哪种实现方式,我都只会听取页面是否已滚动(它们都有一个 onPageChange interface/callback 您可以 add/register,当位置 > currPage 和( positionOffset == 0 OR scroll state == IDLE)(页面已经完全稳定),我会删除之前的页面。
如果您想要一个示例,请告诉我,但是,上面的所有内容应该都是您需要做的。
如果您想保留旧的实现方式,我会捕获用户手指的当前 x 位置(正确地使用 onTouch),如果较小,请设置 ViewPager2.isUserInputEnabled = false
ViewPager2.isUserInputEnabled = newX >= originalX
但是,这很棘手,因为用户可以使用多个手指,而 ViewPager 的 onScroll 实现仅在用户滚动后触发,如果在用户触摸屏幕后 1 个像素后禁用滚动,则会造成卡顿,我建议页面删除。
亲爱的观众您好。
我的情况:
我有一个 viewpager,可以让我的用户左右滑动。我有一个屏幕(片段),我希望用户在滑动之前完成它。
编辑 4:解决方案
由于 ViewPager2 像 RecyclerView 一样工作,我在我的适配器中添加了一个 MutableList :
var fragmentList = mutableListOf<Fragment>(
BasicInfoFragment.newInstance(
activity
),
BondsFragment.newInstance(
activity
),
...
一开始,列表中只有“锁定”之前显示的片段。 当满足或满足我的条件时,我将下一个片段添加到适配器的列表中,这样它们就可以刷卡了
下面是一些旧的不起作用的东西:
我做了什么:
我将 viewpager 自定义为在我想要锁定时锁定滑动(请参阅我的代码),但它禁用了两个方向。
理想情况下,我只想禁用从左向右滑动:我希望用户可以向后滑动(从右向左)。我只想禁用向前滑动(从左到右)。
我试图在我的视图寻呼机上实现一个 onTouchListener 来检测滑动方向,但它只在 ViewPager 解锁时有效。
你能帮帮我吗?
编辑 3:新问题
我已经尝试实现 viewpager2,但现在子布局出现问题。
当我需要将一个片段显示到另一个片段中时,我使用基于此编写的扩展方法: how-to-add-a-fragment-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:也许是解决方案?
我将原始视图放在包含视图寻呼机的 activity 布局的底部:
<View
android:id="@+id/touch_layer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true" />
我创建了一个实现 View.OnTouchListener 的摘要 class :
abstract class OverlayTouchListener : View.OnTouchListener {
companion object{
private const val TAG = "OverlayTouchListener"
}
}
在我的 activity 中反序列化叠加视图:
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。
有点棘手,所以如果你有任何建议...
首先,我推荐使用 ViewPager2,因为它通过 RecyclerView 实现得到了极大的优化,允许您优雅地删除页面而没有帧滞后。
但是,无论您选择哪种实现方式,我都只会听取页面是否已滚动(它们都有一个 onPageChange interface/callback 您可以 add/register,当位置 > currPage 和( positionOffset == 0 OR scroll state == IDLE)(页面已经完全稳定),我会删除之前的页面。
如果您想要一个示例,请告诉我,但是,上面的所有内容应该都是您需要做的。
如果您想保留旧的实现方式,我会捕获用户手指的当前 x 位置(正确地使用 onTouch),如果较小,请设置 ViewPager2.isUserInputEnabled = false
ViewPager2.isUserInputEnabled = newX >= originalX
但是,这很棘手,因为用户可以使用多个手指,而 ViewPager 的 onScroll 实现仅在用户滚动后触发,如果在用户触摸屏幕后 1 个像素后禁用滚动,则会造成卡顿,我建议页面删除。