最近因为项目需要所以碰到了一个需要重新排序的ListView布局,然后就去网上找了一个严振杰(noHttp的作者)写的一个demo,运行成功之后就去看代码,看了半天感觉代码的可读性很差,本来很简单的事被他封装了好几层实现,所以我就开始研究了代码了。
实际上这个可交换item位置也可以滑动删除的控件是用RecyclerView实现的。下面就给大家从代码开始分析一下实现原理。
这个RecyclerView用Studio开发的童鞋们呢就直接依赖下就好了 具体怎么使用我就不跟大家细说了 我就开始捞干货说了啊。首先看主程序的布局,很简单,我为了让大家能够看到RecyclerView我上下放了两个TextView。代码就不贴了 太占篇幅了。最后我会把我的代码分享给大家,希望大家可以看完文章。
先看效果↓
看主Activity代码,我直接贴上代码,注释我已经写的很清楚了
/** * @author 宋鑫 * Created by admin on 2017/1/3. * 主界面 * 布局很简单 */ public class MainActivity extends AppCompatActivity implements StartDragListener{ private RecyclerView myrecycler; private ItemTouchHelper touchHelper;//这个就是RecyclerView实现拖动和滑动的帮助类 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /** * 制造点假数据 */ List<User> list = new ArrayList<>(); for (int i = 0; i < 20; i++) { User u = new User(); u.setName("用户"+i); u.setPhone("1324044535"+i); u.setAddress("丰台科技园"+i+"号楼"); list.add(u); } myrecycler = (RecyclerView) findViewById(R.id.myRecycler);//实例化组件 myrecycler.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));//设置布局管理器,我设置的是纵向的LinearLyaout MyrecyclerAdapter adapter = new MyrecyclerAdapter(list,this);//实例化适配器 myrecycler.setAdapter(adapter); touchHelper = new ItemTouchHelper(new RecyclerViewCallback(adapter));//这里大家可能不知道传入的参数是什么,再这个类里面我会注释 touchHelper.attachToRecyclerView(myrecycler);//将事件绑定导RecyclerView上 } @Override public void onStartDrag(RecyclerView.ViewHolder view) {//这个方法主要是为了将拖动事件绑定给某一个控件上 touchHelper.startDrag(view); } }
看完之后是不是有两个类不认识?ItemTouchHelperand MyrecyclerAdapter
那么其它的先不要管,先去看看这个!首先是ItemTouchHelper这个类是google粑粑给的,所以已经封装好了很多东西。大家用空可以去看看源码....(太多了我就不分析了),看着ItemTochHelper类跟Recycler绑定之前需要一个callback,就是一个回掉,就是说你拖动了,滑动了,告诉谁啊?所以我就去new了一个类来实现这个itemTochHelper.callback。贴上我的代码↓
/** * @author 宋鑫 * Created by admin on 2017/1/3. * RecyclerView监听回调类 */ public class RecyclerViewCallback extends ItemTouchHelper.Callback { @Override public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) { //判断时候需要监听哪些动作 /** * 000001 ItemTouchHelper.UP * 000010 ItemTouchHelper.DOWN * 000100 ItemTouchHelper.LEFT * 001000 ItemTouchHelper.RIGHT * 001111 上面四个的合成版 */ int dragfalgs = ItemTouchHelper.UP | ItemTouchHelper.DOWN;//上下拖动 如果是这两个的话结果就为000011 int swiped = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;//左右滑动 如果是这两个的话结果就为001100 return makeMovementFlags(dragfalgs, swiped);//调用方法来判断当前的操作是什么 } /** * 拖动完的回调方法 */ @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { return true; } /** * 滑动完的回调方法 * * @param viewHolder * @param direction */ @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { } /** * 是否允许拖动 * @return 为true是允许RecyclerView可以拖动 */ @Override public boolean isLongPressDragEnabled() { //返回true允许拖动 false不允许拖动 return true; } }通过以上代码大家应该能看明白,我们只需要在 isLongPressDragEnabled方法处返回true就可以上下拖动位置,但是大家可以试试拖动的效果,相信看完之后就会想到下一步了,因为你拖动完之后没有停到所在的位置。那么我们需要在 onMove方法中干啥呢?对,我们需要刷新数据和刷新界面。好的,我们回到主Activity中,大家看到我在实例化ItemTochhelper的时候还把适配器传进去了,其实我需要的并不是适配器,而是一个回调接口。因为需要刷新适配器的数据,所以RecyclerView的适配器就实现了这个接口,看看这个接口里有什么。看代码↓
/** * Created by admin on 2017/1/3. * 宋鑫 */ public interface itemTochHerlper { boolean onItemMove(int startIndex,int endIndex);//拖动完成之后刷新的方法 void onItemSwiped(int position);//左右滑动之后刷新的方法 }
那么我们再去Adapter中看看,RecyclerView的适配器我相信应该不用给大家说了吧?过多的代码我就不贴了,主要看实现接口的方法吧
/** * 上下拖动之后刷新 * @param startIndex 被拖动的item的位置 * @param endIndex 最终目标位置 * @return */ @Override public boolean onItemMove(int startIndex, int endIndex) { Collections.swap(list,startIndex,endIndex);//交换集合中的位置 notifyItemMoved(startIndex,endIndex);//只刷新这两个位置的视图 return false; } /** * 左右滑动之后刷新 * @param position 被滑动item的位置 */ @Override public void onItemSwiped(int position) { list.remove(position); notifyItemRemoved(position); }刚才还说了一下,有一个功能是对item里面的某一个组件,添加可拖动事件,需要在adapter的 onBindViewHolder 里面实现,请看↓
@Override public void onBindViewHolder(final ViewHolder holder, int position) { holder.tv_name.setText(list.get(position).getName()); holder.tv_phone.setText(list.get(position).getPhone()); holder.tv_address.setText(list.get(position).getAddress()); /** * 给holder的tv1绑定拖动事件 * 按住tv_name就可以拖动整个item进行移动 */ holder.tv_name.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { startCallback.onStartDrag(holder); return true; } }); }
这个startCallback也是一个回调接口,主要是为了能够调到主Activity里面的ItemTochHelper实例。看接口↓
/** * @author 宋鑫 * 这个接口主要是为了监听某一个组件的Touch事件的 */ public interface StartDragListener { void onStartDrag(RecyclerView.ViewHolder view); }然后在Adapter的构造器中实例一个接口private List<User> list; private StartDragListener startCallback; public MyrecyclerAdapter( List<User> list,StartDragListener startCallback){ this.list = list; this.startCallback = startCallback; }好了,讲到这里基本原理就全讲完了,代码在这里===》本人的文采不是很好啊,写的不好的地方还需要大家来指正,谢谢支持。