如何拖放 recyclerView 元素?

Posted

技术标签:

【中文标题】如何拖放 recyclerView 元素?【英文标题】:How can I do drag and drop recyclerView elements? 【发布时间】:2017-11-12 03:21:09 【问题描述】:

我在 android studio 上有一个项目,我想将 RecylerView 中的图像项拖放到我的 Activity 上的容器中...

在我的活动中,我有一个片段容器,它使用 RecyclerView 显示片段,并在 JSON 连接中显示动物部分,可以进行拖放以将图像移动到 Cointainer 以制作动物角色并将数据发送到我的数据库,我是怎么做的???

【问题讨论】:

这看起来像是一个是/否问题。 使用“ItemTouchHelper”检查这个***.com/questions/29901044/… 【参考方案1】:

你必须实现ItemTouchHelper,这是一个关于如何做的例子:

1- 添加您的回收站视图

 <android.support.v7.widget.RecyclerView
        android:id="@+id/note_recycler_view"
        android:scrollbars="vertical"
        android:layout_
        android:layout_/>

2 - 添加模型类

public class Customer 
private Long id;
private String name;
private String emailAddress;
private int imageId;
private String imagePath;

3 - 添加依赖项

compile 'de.hdodenhof:circleimageview:2.0.0'
compile 'com.squareup.picasso:picasso:2.5.2'
compile 'com.yqritc:recyclerview-flexibledivider:1.2.6'
compile 'com.google.code.gson:gson:2.3.1'

4 - 添加 Internet 权限

<uses-permission android:name="android.permission.INTERNET" />

5- 在您的活动中创建一个新的ListCustomer 并将客户添加到其中。

6- 创建一个名为 CustomerListAdapter 的新类

 package com.okason.draganddrop;

import android.content.Context;
import android.graphics.Color;
import android.support.v4.view.MotionEventCompat;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.okason.draganddrop.listeners.OnCustomerListChangedListener;
import com.okason.draganddrop.listeners.OnStartDragListener;
import com.okason.draganddrop.utilities.ItemTouchHelperAdapter;
import com.okason.draganddrop.utilities.ItemTouchHelperViewHolder;
import com.squareup.picasso.Picasso;

import java.util.Collections;
import java.util.List;

/**
 * Created by Valentine on 10/18/2015.
 */
public class CustomerListAdapter extends
        RecyclerView.Adapter<CustomerListAdapter.ItemViewHolder>
        implements ItemTouchHelperAdapter 

private List<Customer> mCustomers;
private Context mContext;
private OnStartDragListener mDragStartListener;
private OnCustomerListChangedListener mListChangedListener;

public CustomerListAdapter(List<Customer> customers, Context context,
                           OnStartDragListener dragLlistener,
                           OnCustomerListChangedListener listChangedListener)
    mCustomers = customers;
    mContext = context;
    mDragStartListener = dragLlistener;
    mListChangedListener = listChangedListener;



@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    View rowView = LayoutInflater.from
    (parent.getContext()).inflate(R.layout.row_customer_list, parent, false);
    ItemViewHolder viewHolder = new ItemViewHolder(rowView);
    return viewHolder;


@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) 

    final Customer selectedCustomer = mCustomers.get(position);

    holder.customerName.setText(selectedCustomer.getName());
    holder.customerEmail.setText(selectedCustomer.getEmailAddress());
    Picasso.with(mContext)
            .load(selectedCustomer.getImagePath())
            .placeholder(R.drawable.profile_icon)
            .into(holder.profileImage);



    holder.handleView.setOnTouchListener(new View.OnTouchListener() 
        @Override
        public boolean onTouch(View v, MotionEvent event) 
            if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) 
                mDragStartListener.onStartDrag(holder);
            
            return false;
        
    );


@Override
public int getItemCount() 
    return mCustomers.size();


@Override
public void onItemMove(int fromPosition, int toPosition) 
    Collections.swap(mCustomers, fromPosition, toPosition);
    mListChangedListener.onNoteListChanged(mCustomers);
    notifyItemMoved(fromPosition, toPosition);


@Override
public void onItemDismiss(int position) 



public static class ItemViewHolder extends RecyclerView.ViewHolder implements
        ItemTouchHelperViewHolder 
    public final TextView customerName, customerEmail;
    public final ImageView handleView, profileImage;


    public ItemViewHolder(View itemView) 
        super(itemView);
        customerName = (TextView)itemView.findViewById(R.id.text_view_customer_name);
        customerEmail = (TextView)itemView.findViewById(R.id.text_view_customer_email);
        handleView = (ImageView)itemView.findViewById(R.id.handle);
        profileImage = (ImageView)itemView.findViewById(R.id.image_view_customer_head_shot);
    

    @Override
    public void onItemSelected() 
        itemView.setBahttp://valokafor.com/wp-admin/post.php?post=1804&action=edit#ckgroundColor(Color.LTGRAY);
    

    @Override
    public void onItemClear() 
        itemView.setBackgroundColor(0);
    


7- 实施ItemTouchHelper 在您的实用程序包中,添加 ItemTouchHelperAdapter.java 并且下面是内容:

public interface ItemTouchHelperAdapter 
/**
 * Called when an item has been dragged far enough to trigger a move. This is called every time
 * an item is shifted, and not at the end of a "drop" event.
 *
 * @param fromPosition The start position of the moved item.
 * @param toPosition   Then end position of the moved item.

 */
void onItemMove(int fromPosition, int toPosition);


/**
 * Called when an item has been dismissed by a swipe.
 *
 * @param position The position of the item dismissed.

 */
void onItemDismiss(int position);

在你的实用程序包中,添加 ItemTouchHelperViewHolder.java 和下面是内容:

public interface ItemTouchHelperViewHolder 
    /**
      * Implementations should update the item view to indicate it's active state.
     */
    void onItemSelected();


    /**
     * state should be cleared.
     */
    void onItemClear();

在您的实用程序包中,添加 SimpleItemTouchHelperCallback.java,这是内容:

public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback 

private final ItemTouchHelperAdapter mAdapter;

public SimpleItemTouchHelperCallback(ItemTouchHelperAdapter adapter) 
    mAdapter = adapter;


@Override
public boolean isLongPressDragEnabled() 
    return true;


@Override
public boolean isItemViewSwipeEnabled() 
    return false;


@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 
    final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
    final int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;
    return makeMovementFlags(dragFlags, swipeFlags);


@Override
public boolean onMove(RecyclerView recyclerView, 
RecyclerView.ViewHolder source, RecyclerView.ViewHolder target) 
    mAdapter.onItemMove(source.getAdapterPosition(), target.getAdapterPosition());
    return true;


@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int i) 
    mAdapter.onItemDismiss(viewHolder.getAdapterPosition());


@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) 
    if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) 
        ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
        itemViewHolder.onItemSelected();
    

    super.onSelectedChanged(viewHolder, actionState);


@Override
public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) 
    super.clearView(recyclerView, viewHolder);

    ItemTouchHelperViewHolder itemViewHolder = (ItemTouchHelperViewHolder) viewHolder;
    itemViewHolder.onItemClear();


添加一个名为 listener 的包并添加一个名为 OnStartDragListener.java 的接口,这是内容:

public interface OnCustomerListChangedListener 
void onNoteListChanged(List<Customer> customers);

实现自定义行

private RecyclerView mRecyclerView;
private CustomerListAdapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
private ItemTouchHelper mItemTouchHelper;
private List<Customer> mCustomers;

在onCreate方法之后,添加这个方法。然后可能在调用设置工具栏之后从 onCreate() 方法调用此方法。忽略错误警告一分钟。

private void setupRecyclerView()
        mRecyclerView = (RecyclerView) `findViewById(R.id.note_recycler_view);`
        mRecyclerView.setHasFixedSize(true);
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mCustomers = SampleData.addSampleCustomers();

        //setup the adapter with empty list
        mAdapter = new CustomerListAdapter(mCustomers, this, this, this);
        ItemTouchHelper.Callback callback = new `SimpleItemTouchHelperCallback(mAdapter);`
        mItemTouchHelper = new ItemTouchHelper(callback);
        mItemTouchHelper.attachToRecyclerView(mRecyclerView);
mRecyclerView.addItemDecoration(new  HorizontalDividerItemDecoration.Builder(this)

                .colorResId(R.color.colorPrimaryDark)
                .size(2)
                .build());
        mRecyclerView.setAdapter(mAdapter);
    

更新您的 MainActivity 的签名以实现我们添加的两个侦听器,如下所示,并使用 Android Studio 快速修复来实现这些方法。

public class MainActivity extends AppCompatActivity
implements OnCustomerListChangedListener,
    OnStartDragListener

这里是其中一种方法的实现,下一节我们将实现另一种。

@Override
public void onStartDrag(RecyclerView.ViewHolder viewHolder) 
    mItemTouchHelper.startDrag(viewHolder);


此时,您的拖放列表应该可以工作了,我们现在想要记住重新组织后的列表项的位置。就像我在文章开头提到的那样,这是通过将列表项的 id 保存到 SharedPreference 来完成的,因此请继续将以下类成员添加到文件顶部。

private SharedPreferences mSharedPreferences;
private SharedPreferences.Editor mEditor;
public static final String LIST_OF_SORTED_DATA_ID = "json_list_sorted_data_id";
public final static String PREFERENCE_FILE = "preference_file";

并在 onCreate() 中像这样实例化 SharedPreference:

mSharedPreferences = this.getApplicationContext()
            .getSharedPreferences(PREFERENCE_FILE, Context.MODE_PRIVATE);
    mEditor = mSharedPreferences.edit();

然后继续实现另一个方法来监听列表何时发生变化,这里是该方法的实现:

@Override
public void onNoteListChanged(List<Customer> customers) 
    //after drag and drop operation, the new list of Customers is passed in here

    //create a List of Long to hold the Ids of the
    //Customers in the List
    List<Long> listOfSortedCustomerId = new ArrayList<Long>();

    for (Customer customer: customers)
        listOfSortedCustomerId.add(customer.getId());
    

    //convert the List of Longs to a JSON string
    Gson gson = new Gson();
    String jsonListOfSortedCustomerIds = gson.toJson(listOfSortedCustomerId);


    //save to SharedPreference
    mEditor.putString(LIST_OF_SORTED_DATA_ID, jsonListOfSortedCustomerIds).commit();
    mEditor.commit();

然后,将此方法添加到您的 MainActivity.java:

private List<Customer> getSampleData()

    //Get the sample data
    List<Customer> customerList = SampleData.addSampleCustomers();

    //create an empty array to hold the list of sorted Customers
    List<Customer> sortedCustomers = new ArrayList<Customer>();

    //get the JSON array of the ordered of sorted customers
    String jsonListOfSortedCustomerId = mSharedPreferences.getString(LIST_OF_SORTED_DATA_ID, "");

    //check for null
    if (!jsonListOfSortedCustomerId.isEmpty())

        //convert JSON array into a List<Long>
        Gson gson = new Gson();
        List<Long> listOfSortedCustomersId = gson.fromJson
        (jsonListOfSortedCustomerId, new TypeToken<List<Long>>().getType());

        //build sorted list
        if (listOfSortedCustomersId != null && listOfSortedCustomersId.size() > 0)
            for (Long id: listOfSortedCustomersId)
                for (Customer customer: customerList)
                    if (customer.getId().equals(id))
                        sortedCustomers.add(customer);
                        customerList.remove(customer);
                        break;
                    
                
            
        

        //if there are still customers that were not in the sorted list
        //maybe they were added after the last drag and drop
        //add them to the sorted list
        if (customerList.size() > 0)
            sortedCustomers.addAll(customerList);
        

        return sortedCustomers;
    else 
       return customerList;
    

现在更新setupRecyclerView() 中您从中获取数据的行:

mCustomers = SampleData.addSampleCustomers();

到:

mCustomers = getSampleData();

Here is the source of my answer你可以找到更多关于每一步的信息和描述。

【讨论】:

这是边界线link-only answer。您应该在此处扩展您的答案以包含尽可能多的信息,并使用该链接仅供参考。 @Glory 我已经尝试过这个答案,但这不起作用它导致我在non-null exceptionif (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) mDragStartListener.onStartDrag(holder);

以上是关于如何拖放 recyclerView 元素?的主要内容,如果未能解决你的问题,请参考以下文章

拖放 RecyclerView 的第一项会移动几个随机位置

如何使用 AsyncTask 更新 RecyclerView 项目

Android:在 RecyclerView 中拖放项目时如何有效更新 SQLite 数据库?

RecyclerView smoothScroll位于中心位置。安卓

在ANDROID中相互拖放时合并Recyclerview中的项目?

RecyclerView分隔线定制