- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
所以,我正在努力解决这个问题。我在网上看到很多关于通过滑动和拖动来使用 RecyclerView 项目的帖子——我已经记下了。但是我无法开始工作的是拖动和长按操作。
我有拖动和长按的工作代码,问题是,当我长按回收站项目时,它是否会运行长按功能或拖动功能是一个折腾。
这是长按监听器的代码:
holder.itemView.setOnLongClickListener {
currentNote.toggleSelection(it)
// change the MainActivity menu to the selection menu
MainActivity.currentMenu = R.menu.menu_select
(it.context as Activity).invalidateOptionsMenu()
// set a flag to change the onClickListener to select notes rather than edit/view
SELECTING = true
notifyDataSetChanged()
true
很简单。以下是我如何实现拖动操作:
val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
override fun isLongPressDragEnabled() = true
override fun isItemViewSwipeEnabled() = true
override fun getMovementFlags(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder
): Int {
val dragFlags = UP or DOWN or START or END
val swipeFlags = LEFT or RIGHT
return makeMovementFlags(dragFlags, swipeFlags)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
val fromPosition = viewHolder.adapterPosition
val toPosition = target.adapterPosition
val item = NOTES_ARRAY.removeAt(fromPosition)
NOTES_ARRAY.add(toPosition, item)
recyclerView.adapter!!.notifyItemMoved(fromPosition, toPosition)
return true
}
})
这是一个笔记应用程序,所以我使用长按操作进行多选。所以问题是,我如何区分长按 Action 和拖动 Action ,因为它们都与“长按”绑定(bind)。
我知道这就是问题所在,因为如果我注释掉 onLongClickListener,我可以轻松拖放而不会出现问题。
最佳答案
我通过实现自定义 RecyclerView.OnItemTouchListener
并重新定义什么是长按解决了这个问题。
Android 中的长按由 MotionEvent#ACTION_DOWN
触发,然后连续 MotionEvent#ACTION_MOVE
持续特定的持续时间,无需移动太多,也不需要触发 MotionEvent#ACTION_UP
的指针(手指或鼠标)。
对于 ItemTouchHelper,我必须将长按更改为在
以免失火并触发拖动事件。MotionEvent#ACTION_UP
上连续 MotionEvent#ACTION_MOVE
持续特定时间后触发
import android.graphics.PointF;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView;
import java.util.function.Supplier;
import de.blueworldgmbh.tracemate.util.TimberWrapper;
public class SelectionAndDragNDropOnTouchHelper
{
private static final long LONG_PRESS_TIME_MS = ViewConfiguration.getLongPressTimeout();
private static final float MOVE_MARGIN_PX = 15;
private long initialTouchTime;
private boolean isDragging = false;
private boolean isScrolling = false;
@NonNull
private PointF initialPoint = new PointF();
@NonNull
private final ItemTouchHelper itemTouchHelper;
@NonNull
private final ItemTouchCallback touchCallback;
@NonNull
private final Supplier<Boolean> isMultiSelectActive;
public SelectionAndDragNDropOnTouchHelper(
@NonNull ItemTouchHelper itemTouchHelper,
@NonNull ItemTouchCallback touchCallback,
@NonNull Supplier<Boolean> isMultiSelectActive
)
{
this.itemTouchHelper = itemTouchHelper;
this.touchCallback = touchCallback;
this.isMultiSelectActive = isMultiSelectActive;
touchCallback.enabledDragNDrop(false);
}
public boolean onTouch(OnSelectionAndDragNDropOnTouchHelper v, MotionEvent event)
{
int action = event.getAction();
PointF currentPoint = new PointF(event.getX(), event.getY());
if (action == MotionEvent.ACTION_DOWN)
{
reset();
initialTouchTime = System.currentTimeMillis();
initialPoint = new PointF(event.getX(), event.getY());
}
else if (action == MotionEvent.ACTION_MOVE)
{
long pressTime = System.currentTimeMillis() - initialTouchTime;
float distanceX = Math.abs(currentPoint.x - initialPoint.x);
float distanceY = Math.abs(currentPoint.y - initialPoint.y);
if (!isMultiSelectActive.get() && !isDragging && !isScrolling && pressTime > LONG_PRESS_TIME_MS)
{
if (distanceX > MOVE_MARGIN_PX || distanceY > MOVE_MARGIN_PX)
{
isDragging = true;
touchCallback.enabledDragNDrop(true);
itemTouchHelper.startDrag(v.getViewHolder());
}
}
else
{
if (distanceX > MOVE_MARGIN_PX || distanceY > MOVE_MARGIN_PX)
{
isScrolling = true;
}
}
}
else if (action == MotionEvent.ACTION_UP)
{
if (isDragging)
{
touchCallback.enabledDragNDrop(false);
return false;
}
if (isScrolling)
{
return false;
}
long pressTime = System.currentTimeMillis() - initialTouchTime;
if (pressTime <= LONG_PRESS_TIME_MS)
{
v.notifyOnClick();
}
else
{
v.notifyOnLongClick();
}
return true;
}
return false;
}
private void reset()
{
touchCallback.enabledDragNDrop(false);
isDragging = false;
isScrolling = false;
}
public interface OnTouchHelper
{
void notifyOnClick();
void notifyOnLongClick();
RecyclerView.ViewHolder getViewHolder();
}
}
要初始化此类,您需要列表的 ItemTouchHelper
及其回调 ItemTouchHelper.Callback
实现,以及列表处于多选模式时的供应商。
查看 LONG_PRESS_TIME_MS
常量,用户必须按住多长时间才能触发拖放或长按事件,我使用了 ViewConfiguration.getLongPressTimeout()
我用的手机是 400ms,我觉得有点多,但我还在微调中。
每次我们返回false
,我们就表明我们没有“处理”触摸事件。这就是拖放事件在此处手动启动后如何使用,或者滚动将如何继续作为预期功能。
OnTouchHelper
接口(interface)此接口(interface)用于列表的 View 持有者。请参阅下文了解实现情况。
ItemTouchHelper.Callback
实现此实现需要一些补充。首先是启用或禁用列表的拖放功能的方法,请参阅 enabledDragNDrop(boolean)
并且在方法 getMovementFlags(RecyclerView, RecyclerView.ViewHolder)
中它应该检查拖放功能是否处于 Activity 状态以传递拖动标志。 ❕ 不要忽视滑动标志,我只需要处理拖放操作。
@Override
public int getMovementFlags(
@NotNull RecyclerView recyclerView,
@NotNull RecyclerView.ViewHolder viewHolder
)
{
if (enabled)
{
return makeFlag(ACTION_STATE_DRAG, ItemTouchHelper.DOWN | ItemTouchHelper.UP);
}
else
{
return 0;
}
}
public void enabledDragNDrop(boolean enabled) {
this.enabled = enabled;
}
RecyclerView.ViewHolder 实现 OnTouchHelper
在ViewHolder中,点击或长按监听器不要注册到view中,而是手动“触发”,否则会触发两次。如果您找到一种方法来注册这些监听器并且不会触发它两次,我们将不胜感激 🙂
@Override
public void notifyOnClick()
{
if (clickListener != null)
{
// custom click listener interface
clickListener.onItemClick(this, getAdapterPosition());
}
}
@Override
public void notifyOnLongClick()
{
if (longClickListener != null)
{
// custom long click listener interface
longClickListener.onItemLongClick(this, getAdapterPosition());
}
}
@Override
public RecyclerView.ViewHolder getViewHolder()
{
return this;
}
RecyclerView.OnItemTouchListener
最后是RecyclerView.OnItemTouchListener
,只实现onInterceptTouchEvent
,忽略其他两个方法
@Override
public boolean onInterceptTouchEvent(
@NonNull RecyclerView rv,
@NonNull MotionEvent motionEvent
)
{
View view = rv.findChildViewUnder(motionEvent.getX(), motionEvent.getY());
if (view != null)
{
RecyclerView.ViewHolder viewHolder = rv.findContainingViewHolder(view);
if (viewHolder instanceof SelectionAndDragNDropOnTouchHelper.OnTouchHelper)
{
return selectionAndDragNDropOnTouchHelper.onTouch(
(SelectionAndDragNDropOnTouchHelper.OnTouchHelper) viewHolder,
motionEvent
);
}
}
return false;
}
这里我们通过触摸坐标获得 View ,并通过 View 获得 View Holder。
只需将项目触摸事件传递给 SelectionAndDragNDropOnTouchHelper
中的自定义项目触摸逻辑实现,并返回它的返回值,否则返回 false
。
我知道代码可以进一步优化并编写得更好,但它正在实现它的功能。
希望对你有帮助
关于Android:带有 LongClickListener 和拖放的 RecyclerView 项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63219792/
我使用库 MaterialWidget 中的 CircleButton。在我的 fragment 中,我尝试在此组件上使用 LongClickListener,但它不起作用。 ClickListene
我在本教程的帮助下创建了一个 ExpandableListView:link .我或多或少地理解了代码,并一直在尝试在组上设置一个 longclicklistener。 子项上已经有一个 setOnC
protected void onListItemClick (ListView l, View v, int position, long id) 正常的方法对我来说很好用,但是还有长按的方法吗?因
有没有办法在 google maps v2 的标记上设置 longClickListsner?我想让用户长按标记并显示一个对话框,其中包含删除或查看信息的选项。这能做到吗? 最佳答案 我还有一个提议。
我正在创建一个调用/拨号按钮,当我单击该调用/拨号按钮时,将根据编辑文本中显示的输入进行调用。我设法做到了那部分。你们能告诉我我是否可以在同一个调用/拨号按钮上做更长的点击,这样就可以出来 toast
所以,我正在努力解决这个问题。我在网上看到很多关于通过滑动和拖动来使用 RecyclerView 项目的帖子——我已经记下了。但是我无法开始工作的是拖动和长按操作。 我有拖动和长按的工作代码,问题是,
我有一个按钮,想使用 LongClickListener,通过按下按钮来获取更改按钮位置期间的坐标。如何在 LongClickListener 或其他方法中获取单击/鼠标的 X、Y 坐标。 我用 On
我有一个 ListFragment,我想在其中添加一个普通点击监听器和一个长监听器。 我覆盖了 ListFragment 上的 onListItemClick() 方法。在这里,它工作没有问题。 然后
如何使用 LongClickListener 而不是 OnTouchListener 来拖动 ImageButton?我需要该按钮来执行 OnClick 操作,当我单击它时,它认为我正在开始拖动。 我
我是 Android 开发新手。 我希望当用户长按列表中的项目时,其值会存储在变量中,以便我可以进一步使用该变量来运行其他函数。 public class Second extends Fragmen
我是一名优秀的程序员,十分优秀!