Android学习

Android RecyclerView item 拖动、滑动删

2017-06-30  本文已影响150人  博弈史密斯

在上一文中,介绍了 RecyclerView 的基本使用,
请参考:Android RecyclerView 的基本使用
(下面的代码都是基于上一篇,为了节省篇幅,省略了一些重复代码)。
这一篇主要介绍 item 拖拽、滑动删除等手势操作,以及对点击、滑动事件的监听等。

点击事件监听

和 ListView 通过 setOnItemClickListener 设置监听不同,RecyclerView 需要我们自己添加监听事件,监听事件需要放在自定义的 Adapter中,在其 onCreateViewHolder 或者 onBindViewHolder 中设置,其本质是一样的,都是通过 view 的 setOnClickListener 方法。为了节省篇幅,省略重复代码,如下:

/**
 * Created by zhangyb on 2017/6/22.
 */
public class LinearRecycleAdapter extends RecyclerView.Adapter<LinearRecycleAdapter.MyViewHolder> {

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
        View view = mInflater.inflate(R.layout.item, viewGroup, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(MyViewHolder viewHolder, int position) {
        viewHolder.mTextView.setText(mDataList.get(position));
        //在 onBindViewHolder 获取到 viewHolder
        //通过 viewHolder 获取到 View 来设置监听
        setClickListener(viewHolder);
    }

    //设置点击监听
    private void setClickListener(final MyViewHolder viewHolder) {
        if (mOnItemClickListener != null) {

            //通过 View 的 setOnClickListener 设置监听
            viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOnItemClickListener.onItemClick(viewHolder.itemView, viewHolder.getLayoutPosition());
                }
            });

            viewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    mOnItemClickListener.onItemLongClick(viewHolder.itemView, viewHolder.getLayoutPosition());
                    return false;
                }
            });
        }
    }

    //item 点击事件接口,回调到Activity
    public interface OnItemClickListener {
        void onItemClick(View view, int position);

        void onItemLongClick(View view, int position);
    }

    private OnItemClickListener mOnItemClickListener;

    //在Activity中调用
    public void setOnItemClickListener(OnItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }
}

上面代码中,也可以在 onCreateViewHolder 中设置监听,因为 onCreateViewHolder 中也可以获取到 View,但是因为获取不到 position,所以如果需要知道 item 的 position,还是需在 onBindViewHolder 中设置。

在 Activity 中 设置监听 并实现接口:

adapter.setOnItemClickListener(new LinearRecycleAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(View view, int position) {
    }

    @Override
    public void onItemLongClick(View view, int position) {
    }
});

实现 item 拖动 交换位置、滑动删除 item

拖动 item 改变位置等操作,是通过 android.support.v7.widget.helper.ItemTouchHelper 这个类来实现的,在 CallBack 回调方法中操作,然后关联到 RecyclerView。直接看代码:

private ItemTouchHelper itemTouchHelper;

itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {

    //用于设置拖拽和滑动的方向
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        //设置允许拖拽item的方向,线性式布局有2个方向
        int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        //设置侧滑方向为从两个方向都可以
        int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;

        return makeMovementFlags(dragFlags, swipeFlags);
    }

    //长摁item拖拽到和另一个item重合时,调用
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView
            .ViewHolder target) {
        Log.i("zyb", "onMove");
        int from = viewHolder.getAdapterPosition();
        int to = target.getAdapterPosition();

        String strFrom = dataList.get(from);
        dataList.remove(from);
        dataList.add(to, strFrom);

        adapter.notifyItemMoved(from, to);//更新适配器中item的位置
        return true;
    }

    //这里处理滑动删除
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
        int position = viewHolder.getAdapterPosition();
        dataList.remove(from);
        adapter.notifyItemRemoved(position);
    }

    //按下和松开item时 调用
    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        Log.i("zyb", "onSelectedChanged");
        super.onSelectedChanged(viewHolder, actionState);
        if (viewHolder != null) {
            viewHolder.itemView.setBackgroundColor(Color.LTGRAY); //拖拽时设置背景色为灰色
        }
    }

    //拖拽停止时 调用
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        Log.i("zyb", "clearView");
        super.clearView(recyclerView, viewHolder);
    }

    //当item视图变化时调用
    @Override
    public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float
            dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
        viewHolder.itemView.setAlpha((float) 0.5); //可以在这里设置透明度
    }
});

itemTouchHelper 关联到 RecyclerView

//itemTouchHelper 关联到 RecyclerView
itemTouchHelper.attachToRecyclerView(recyclerView);

上面代码中 CallBack 回调方法,都做了注释说明,不再赘述。

item 设置滚动监听

可以 通过 Adapter 自带的 addOnScrollListener 设置滚动监听:

adapter.addOnScrollListener(new ImageAutoLoadScrollListener());

private class ImageAutoLoadScrollListener extends OnScrollListener {
    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        super.onScrollStateChanged(recyclerView, newState);

        switch (newState) {
            case SCROLL_STATE_IDLE: //item停止滚动时
                break;

            case SCROLL_STATE_DRAGGING: //item正在滚动时,手指未松开
                break;

            case SCROLL_STATE_SETTLING: //item根据惯性滚动时,手指已松开
                break;
        }
    }
}

代码优化

上面的代码 Adapter、RecyclerView的初始化,ItemTouchHelper、Click的回调,都是在 Activity中,这样代码看着很乱,所以能实现一个自定义类,继承自 RecyclerView,把所有和 RecyclerView 相关的都放在自定义的类中,而初始化工作只需调用一个封装好的方法即可,下面看这个类的代码:

/**
 * Created by zhangyb on 2017/6/27.
 */
public class LinearRecyclerView extends RecyclerView {

    private Context myContext;
    private LinearRecycleAdapter adapter;
    private ItemTouchHelper itemTouchHelper;
    private RecyclerView.LayoutManager layoutManager;

    public LinearRecyclerView(Context context) {
        super(context, null, 0);
        myContext = context;
    }

    public void initRecyclerView(List<String> dataList) {
        layoutManager = new LinearLayoutManager(myContext, VERTICAL, false);
        setLayoutManager(layoutManager);

        adapter = new LinearRecycleAdapter(myContext, dataList);
        setAdapter(adapter);

        addOnScrollListener(new ImageAutoLoadScrollListener());
        addItemMoveListener();
    }

    private void addItemMoveListener() {
        itemTouchHelper = new ItemTouchHelper(new ItemMoveListener());
        itemTouchHelper.attachToRecyclerView(this);
    }

    private class ItemMoveListener extends ItemTouchHelper.Callback {

        //用于设置拖拽和滑动的方向
        @Override
        public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

            //设置允许拖拽item的方向,线性式布局有2个方向
            int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            //设置侧滑方向为从两个方向都可以
            int swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;

            return makeMovementFlags(dragFlags, swipeFlags);
        }

        //长摁item拖拽到和另一个item重合时,调用
        @Override
        public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView
                .ViewHolder target) {
            Log.i("zyb", "onMove");
            int from = viewHolder.getAdapterPosition();
            int to = target.getAdapterPosition();

            String strFrom = adapter.getDataList().get(from);
            adapter.getDataList().remove(from);
            adapter.getDataList().add(to, strFrom);

            adapter.notifyItemMoved(from, to);//更新适配器中item的位置
            return true;
        }

        //这里处理滑动删除
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
            int position = viewHolder.getAdapterPosition();
            adapter.notifyItemRemoved(position);
        }

        //按下和松开item时 调用
        @Override
        public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
            Log.i("zyb", "onSelectedChanged");
            super.onSelectedChanged(viewHolder, actionState);
            if (viewHolder != null) {
                viewHolder.itemView.setBackgroundColor(Color.LTGRAY); //拖拽时设置背景色为灰色
            }
        }

        //拖拽停止时 调用
        @Override
        public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
            Log.i("zyb", "clearView");
            super.clearView(recyclerView, viewHolder);
        }

        ////当item视图变化时调用
        @Override
        public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float
                dX, float dY, int actionState, boolean isCurrentlyActive) {
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            viewHolder.itemView.setAlpha((float) 0.5); //可以在这里设置透明度
        }
    }

    private class ImageAutoLoadScrollListener extends OnScrollListener {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);

            switch (newState) {
                case SCROLL_STATE_IDLE: //item停止滚动时
                    break;

                case SCROLL_STATE_DRAGGING: //item正在滚动时,手指未松开
                    break;

                case SCROLL_STATE_SETTLING: //item通过惯性滚动时,手指已松开
                    break;
            }
        }
    }
}

在 Activity 中调用:

public class MainActivity extends AppCompatActivity {

    /*优化后*/
    private List<String> dataList;
    private LinearRecyclerView linearRecyclerView;
    
    /*优化前*/
    private RecyclerView recyclerView;
    private RecyclerView.LayoutManager layoutManager;
    private LinearRecycleAdapter adapter;
    private ItemTouchHelper itemTouchHelper;
    private List<String> dataList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        /*优化后*/
        linearRecyclerView = (LinearRecyclerView) findViewById(R.id.main_recycle_view);
        linearRecyclerView.initRecyclerView(initData());
        
        
        /*优化前*/
        layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
        recyclerView = (RecyclerView) findViewById(R.id.main_recycle_view);
        recyclerView.setLayoutManager(layoutManager);

        initData();
        adapter = new LinearRecycleAdapter(this, dataList);

        itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
            ..... //省略
        });

        itemTouchHelper.attachToRecyclerView(recyclerView);

        adapter.setOnItemClickListener(new LinearRecycleAdapter.OnItemClickListener() {
           ..... //省略
        });

        recyclerView.setAdapter(adapter);
    }
    
    private List<String> initData() {
        dataList = new ArrayList<>();
        for (int i = 0; i < 50; i++) {
            dataList.add("item : " + i);
        }
        return dataList;
    }
}


对比发现,优化后只需要两行代码。我们理想的状态,就是只需要一行代码,创建出这个类,就完成这个类的初始化工作,最多传入一个或者多个参数,初始化的工作尽可能的交给构造函数来完成,也就是编译器帮你做,尽量不要让用户操心 或者让用户完成一系列的初始化。但在这个例子中,DataList,也就是 和 RecyclerView 绑定的数据,需要外面来传入,所以封装了一个 public void initRecyclerView(List<String> dataList) 方法,而添加的监听功能,如果用户不想使用,只需不去处理监听的回调函数即可。

上一篇下一篇

猜你喜欢

热点阅读