gpt4 book ai didi

Android:带有 LongClickListener 和拖放的 RecyclerView 项目

转载 作者:行者123 更新时间:2023-12-05 00:04:29 31 4
gpt4 key购买 nike

所以,我正在努力解决这个问题。我在网上看到很多关于通过滑动和拖动来使用 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/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com