Advanced Recycler View - 处理项目点击和长按拖动操作
Advanced Recycler View - Handling on item click and long click drag operations
我正在使用 advanced recycler view 创建拖放网格视图。我的代码基于库作者提供的示例。一切似乎都工作得很好,拖放很好地保存了它的状态、交换位置等,但我无法附加到这个简单的项目点击监听器上。就像 RecyclerViewDragDropManager
包装的适配器只能执行那些拖放操作。
在这种情况下,我很困惑点击监听器应该去哪里,因为通常在 RecyclerView
中你可以在 bindView
中附加到你的适配器,但我的适配器只有在长按后才会启动并且只处理拖放。
我尝试了堆栈中关于如何将点击侦听器附加到 RecyclerView 的所有解释,似乎没有任何效果。
我需要的是如果长按没有启动并知道它发生在哪个项目上,则拦截操作。
这是设置回收视图和适配器的片段代码:
private void setLayout() {
mRecyclerView = (RecyclerView) mView.findViewById(R.id.recycler_view);
mLayoutManager = new GridLayoutManager(mContext, Const.GRID_ROW_SIZE, GridLayoutManager.VERTICAL, false);
//drag & drop manager
mRecyclerViewDragDropManager = new RecyclerViewDragDropManager();
mRecyclerViewDragDropManager.setDraggingItemShadowDrawable(
(NinePatchDrawable) ContextCompat.getDrawable(mContext, R.drawable.material_shadow_z3));
//start dragging after long press
mRecyclerViewDragDropManager.setInitiateOnLongPress(true);
mRecyclerViewDragDropManager.setInitiateOnMove(false);
mRecyclerViewDragDropManager.setLongPressTimeout(Const.LONG_PRESS_DRAG_TIMEOUT);
}
private void setWrappedAdapter(DraggableGridAdapter draggableGridAdapter) {
mAdapter = draggableGridAdapter;
mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(draggableGridAdapter); //wrap for dragging
final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator();
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mWrappedAdapter); // requires *wrapped* adapter
mRecyclerView.setItemAnimator(animator);
if (supportsViewElevation()) {
// Lollipop or later has native drop shadow feature. ItemShadowDecorator is not required.
} else {
mRecyclerView.addItemDecoration(new ItemShadowDecorator((NinePatchDrawable)
ContextCompat.getDrawable(mContext, R.drawable.material_shadow_z1)));
}
mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView);
mRecyclerViewDragDropManager.setItemMoveMode(RecyclerViewDragDropManager.ITEM_MOVE_MODE_DEFAULT);
mAdapter.setItemMoveMode(RecyclerViewDragDropManager.ITEM_MOVE_MODE_DEFAULT);
}
适配器代码:
public class DraggableGridAdapter extends RecyclerView.Adapter<DraggableGridAdapter.MyViewHolder>
implements DraggableItemAdapter<DraggableGridAdapter.MyViewHolder> {
private static final String TAG = "MyDraggableItemAdapter";
private int mItemMoveMode = RecyclerViewDragDropManager.ITEM_MOVE_MODE_DEFAULT;
// NOTE: Make accessible with short name
private interface Draggable extends DraggableItemConstants {
}
private AbstractDataProvider mProvider;
public static class MyViewHolder extends AbstractDraggableItemViewHolder {
public FrameLayout mContainer;
public View mDragHandle;
public TextView mTextView;
public MyViewHolder(View v) {
super(v);
mContainer = (FrameLayout) v.findViewById(R.id.container);
mDragHandle = v.findViewById(R.id.drag_handle);
mTextView = (TextView) v.findViewById(android.R.id.text1);
}
}
public DraggableGridAdapter(AbstractDataProvider dataProvider) {
mProvider = dataProvider;
// DraggableItemAdapter requires stable ID, and also
// have to implement the getItemId() method appropriately.
// Ids must be unique not changing with position.
setHasStableIds(true);
}
public void setItemMoveMode(int itemMoveMode) {
mItemMoveMode = itemMoveMode;
}
@Override
public long getItemId(int position) {
return mProvider.getItem(position).getId();
}
@Override
public int getItemViewType(int position) {
return mProvider.getItem(position).getViewType();
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final View v = inflater.inflate(R.layout.list_grid_item, parent, false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
final AbstractDataProvider.Data item = mProvider.getItem(position);
// TODO: 2016-09-09 Add switch for checklist items
//set text
holder.mTextView.setText(item.getPlainText());
final int dragState = holder.getDragStateFlags();
if (((dragState & Draggable.STATE_FLAG_IS_UPDATED) != 0)) {
int bgResId;
if ((dragState & Draggable.STATE_FLAG_IS_ACTIVE) != 0) {
bgResId = R.drawable.bg_item_dragging_active_state;
com.example.Utilities.DrawableUtils.clearState(holder.mContainer.getForeground());
} else if ((dragState & Draggable.STATE_FLAG_DRAGGING) != 0) {
bgResId = R.drawable.bg_item_dragging_state;
} else {
bgResId = R.drawable.bg_item_normal_state;
}
holder.mContainer.setBackgroundResource(bgResId);
}
}
@Override
public int getItemCount() {
return mProvider.getCount();
}
@Override
public void onMoveItem(int fromPosition, int toPosition) {
Log.d(TAG, "onMoveItem(fromPosition = " + fromPosition + ", toPosition = " + toPosition + ")");
if (fromPosition == toPosition) {
return;
}
if (mItemMoveMode == RecyclerViewDragDropManager.ITEM_MOVE_MODE_DEFAULT) {
mProvider.moveItem(fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
} else {
mProvider.swapItem(fromPosition, toPosition);
notifyDataSetChanged();
}
}
@Override
public boolean onCheckCanStartDrag(MyViewHolder holder, int position, int x, int y) {
return true;
}
@Override
public ItemDraggableRange onGetItemDraggableRange(MyViewHolder holder, int position) {
// no drag-sortable range specified
return null;
}
@Override
public boolean onCheckCanDrop(int draggingPosition, int dropPosition) {
return true;
}
这是我试过但没有成功的示例代码,来自 onBindViewHolder 方法中的适配器:
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Clicked " + position);
}
});
}
好的,所以,我的问题的解决方案是 将 onBindViewHolder 中的侦听器分配给 mContainer 而不是 itemView。
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.mContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Clicked " + position);
}
});
}
由于布局 xml.
中的 clickable="true" 语句,mContainer framelayout 消耗了所有点击次数
<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
tools:ignore="UselessParent">
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|left"
android:gravity="center"
tools:ignore="RtlHardcoded"/>
</FrameLayout>
我正在使用 advanced recycler view 创建拖放网格视图。我的代码基于库作者提供的示例。一切似乎都工作得很好,拖放很好地保存了它的状态、交换位置等,但我无法附加到这个简单的项目点击监听器上。就像 RecyclerViewDragDropManager
包装的适配器只能执行那些拖放操作。
在这种情况下,我很困惑点击监听器应该去哪里,因为通常在 RecyclerView
中你可以在 bindView
中附加到你的适配器,但我的适配器只有在长按后才会启动并且只处理拖放。
我尝试了堆栈中关于如何将点击侦听器附加到 RecyclerView 的所有解释,似乎没有任何效果。
我需要的是如果长按没有启动并知道它发生在哪个项目上,则拦截操作。
这是设置回收视图和适配器的片段代码:
private void setLayout() {
mRecyclerView = (RecyclerView) mView.findViewById(R.id.recycler_view);
mLayoutManager = new GridLayoutManager(mContext, Const.GRID_ROW_SIZE, GridLayoutManager.VERTICAL, false);
//drag & drop manager
mRecyclerViewDragDropManager = new RecyclerViewDragDropManager();
mRecyclerViewDragDropManager.setDraggingItemShadowDrawable(
(NinePatchDrawable) ContextCompat.getDrawable(mContext, R.drawable.material_shadow_z3));
//start dragging after long press
mRecyclerViewDragDropManager.setInitiateOnLongPress(true);
mRecyclerViewDragDropManager.setInitiateOnMove(false);
mRecyclerViewDragDropManager.setLongPressTimeout(Const.LONG_PRESS_DRAG_TIMEOUT);
}
private void setWrappedAdapter(DraggableGridAdapter draggableGridAdapter) {
mAdapter = draggableGridAdapter;
mWrappedAdapter = mRecyclerViewDragDropManager.createWrappedAdapter(draggableGridAdapter); //wrap for dragging
final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator();
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mWrappedAdapter); // requires *wrapped* adapter
mRecyclerView.setItemAnimator(animator);
if (supportsViewElevation()) {
// Lollipop or later has native drop shadow feature. ItemShadowDecorator is not required.
} else {
mRecyclerView.addItemDecoration(new ItemShadowDecorator((NinePatchDrawable)
ContextCompat.getDrawable(mContext, R.drawable.material_shadow_z1)));
}
mRecyclerViewDragDropManager.attachRecyclerView(mRecyclerView);
mRecyclerViewDragDropManager.setItemMoveMode(RecyclerViewDragDropManager.ITEM_MOVE_MODE_DEFAULT);
mAdapter.setItemMoveMode(RecyclerViewDragDropManager.ITEM_MOVE_MODE_DEFAULT);
}
适配器代码:
public class DraggableGridAdapter extends RecyclerView.Adapter<DraggableGridAdapter.MyViewHolder>
implements DraggableItemAdapter<DraggableGridAdapter.MyViewHolder> {
private static final String TAG = "MyDraggableItemAdapter";
private int mItemMoveMode = RecyclerViewDragDropManager.ITEM_MOVE_MODE_DEFAULT;
// NOTE: Make accessible with short name
private interface Draggable extends DraggableItemConstants {
}
private AbstractDataProvider mProvider;
public static class MyViewHolder extends AbstractDraggableItemViewHolder {
public FrameLayout mContainer;
public View mDragHandle;
public TextView mTextView;
public MyViewHolder(View v) {
super(v);
mContainer = (FrameLayout) v.findViewById(R.id.container);
mDragHandle = v.findViewById(R.id.drag_handle);
mTextView = (TextView) v.findViewById(android.R.id.text1);
}
}
public DraggableGridAdapter(AbstractDataProvider dataProvider) {
mProvider = dataProvider;
// DraggableItemAdapter requires stable ID, and also
// have to implement the getItemId() method appropriately.
// Ids must be unique not changing with position.
setHasStableIds(true);
}
public void setItemMoveMode(int itemMoveMode) {
mItemMoveMode = itemMoveMode;
}
@Override
public long getItemId(int position) {
return mProvider.getItem(position).getId();
}
@Override
public int getItemViewType(int position) {
return mProvider.getItem(position).getViewType();
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());
final View v = inflater.inflate(R.layout.list_grid_item, parent, false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(MyViewHolder holder, final int position) {
final AbstractDataProvider.Data item = mProvider.getItem(position);
// TODO: 2016-09-09 Add switch for checklist items
//set text
holder.mTextView.setText(item.getPlainText());
final int dragState = holder.getDragStateFlags();
if (((dragState & Draggable.STATE_FLAG_IS_UPDATED) != 0)) {
int bgResId;
if ((dragState & Draggable.STATE_FLAG_IS_ACTIVE) != 0) {
bgResId = R.drawable.bg_item_dragging_active_state;
com.example.Utilities.DrawableUtils.clearState(holder.mContainer.getForeground());
} else if ((dragState & Draggable.STATE_FLAG_DRAGGING) != 0) {
bgResId = R.drawable.bg_item_dragging_state;
} else {
bgResId = R.drawable.bg_item_normal_state;
}
holder.mContainer.setBackgroundResource(bgResId);
}
}
@Override
public int getItemCount() {
return mProvider.getCount();
}
@Override
public void onMoveItem(int fromPosition, int toPosition) {
Log.d(TAG, "onMoveItem(fromPosition = " + fromPosition + ", toPosition = " + toPosition + ")");
if (fromPosition == toPosition) {
return;
}
if (mItemMoveMode == RecyclerViewDragDropManager.ITEM_MOVE_MODE_DEFAULT) {
mProvider.moveItem(fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
} else {
mProvider.swapItem(fromPosition, toPosition);
notifyDataSetChanged();
}
}
@Override
public boolean onCheckCanStartDrag(MyViewHolder holder, int position, int x, int y) {
return true;
}
@Override
public ItemDraggableRange onGetItemDraggableRange(MyViewHolder holder, int position) {
// no drag-sortable range specified
return null;
}
@Override
public boolean onCheckCanDrop(int draggingPosition, int dropPosition) {
return true;
}
这是我试过但没有成功的示例代码,来自 onBindViewHolder 方法中的适配器:
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Clicked " + position);
}
});
}
好的,所以,我的问题的解决方案是 将 onBindViewHolder 中的侦听器分配给 mContainer 而不是 itemView。
public void onBindViewHolder(MyViewHolder holder, final int position) {
holder.mContainer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Clicked " + position);
}
});
}
由于布局 xml.
中的 clickable="true" 语句,mContainer framelayout 消耗了所有点击次数<FrameLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:foreground="?attr/selectableItemBackground"
tools:ignore="UselessParent">
<TextView
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|left"
android:gravity="center"
tools:ignore="RtlHardcoded"/>
</FrameLayout>