仿CSDN客户端首页(二)----拖拽排序Tabs的实现

  1. 云栖社区>
  2. 博客>
  3. 正文

仿CSDN客户端首页(二)----拖拽排序Tabs的实现

tangyangkai 2016-05-17 18:03:00 浏览1358
展开阅读全文

上一篇博客用TabLayout实现了类似CSDN客户端首页选项卡的滑动效果:
仿CSDN客户端首页(一)—-TabLayout实现选项卡滑动效果
然后发现了CSDN首页拖拽排序效果,先上自己完成的效果图:
这里写图片描述
这里是在之前的基础上进行修改的:点击右侧的箭头,出现Tabs的列表,按住Item右侧图标,可以进行上下拖动,进行排序,排序以后再次点击箭头,首页Tabs顺序也会改变;点击列表的某一个Item,会跳转到对应的界面。整体上效果还是和CSDN客户端很像的。

OK,一起看看怎么实现的吧。

1.界面布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    app:tabTextAppearance="@style/MyTabLayoutTextAppearanceInverse"
    tools:context="com.example.tangyangkai.testdemo.TabLayoutActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginRight="5dp"
        android:orientation="horizontal"
        android:weightSum="11">


        <android.support.design.widget.TabLayout
            android:id="@+id/tab"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_weight="10"></android.support.design.widget.TabLayout>


        <View
            android:layout_width="1dp"
            android:layout_height="match_parent"
            android:layout_marginLeft="5dp"
            android:layout_toLeftOf="@+id/tab_check"
            android:background="@color/font_text" />


        <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1">

            <CheckBox
                android:id="@+id/tab_check"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true"
                android:layout_marginBottom="15dp"
                android:layout_marginLeft="8dp"
                android:layout_marginRight="8dp"
                android:layout_marginTop="15dp"
                android:background="@drawable/down"
                android:button="@null"
                android:checked="false" />

        </RelativeLayout>
    </LinearLayout>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:id="@+id/tabs_ll"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@android:color/white"
            android:orientation="vertical"
            android:visibility="gone"></LinearLayout>

        <LinearLayout
            android:id="@+id/viewpager_ll"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical"
            android:visibility="visible">

            <android.support.v4.view.ViewPager
                android:id="@+id/viewpager"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="@android:color/white" />
        </LinearLayout>
    </RelativeLayout>
</LinearLayout>

主界面还是延续之前的TabLayout+ViewPager来实现的,右侧用的一个checkbox来控制视图的显示与隐藏。

2.主界面代码实现:

  recyclerview = (RecyclerView) view.findViewById(R.id.sorts_recyclerview);
        if (type == TABS) {
            sortTxt.setVisibility(View.VISIBLE);
            sortView.setVisibility(View.VISIBLE);
            txt.setVisibility(View.GONE);
            recyclerview.setVisibility(View.VISIBLE);
            initSortViews();
        } else {
            sortTxt.setVisibility(View.GONE);
            sortView.setVisibility(View.GONE);
            txt.setVisibility(View.VISIBLE);
            recyclerview.setVisibility(View.GONE);
        }

    }

    //加载排序布局
    private void initSortViews() {
        adapter = new TabsAdapter(getActivity(), this);
        adapter.setListener(new TabsAdapter.onItemClickListener() {
            @Override
            public void onItemClick(int position) {
                Event item = new Event(position);
                EventBus.getDefault().post(item);
            }


        });
        LinearLayoutManager manger = new LinearLayoutManager(getActivity());
        recyclerview.setLayoutManager(manger);
        recyclerview.setAdapter(adapter);
        }

这里复用的是同一个Fragment,根据传递过来的参数,显示不同的界面。
可参考:从BaseActivity与BaseFragment的封装谈起

当点击checkbox跳转过来的时候,传递一个不一样的参数,显示recyclerview。然后就是适配器的代码。

3.recyclerview的点击事件:

这里的处理我之前有一篇博客说的很详细:
Android接口回调处理item点击事件
这种接口回调的方式本质就是为了拿到被点击item的position,在Fragment获取到position以后,我们需要在Activity里面根据position进行切换界面的操作。activity与fragment的通信,我用的是Eventbus来进行处理的:
Android Fragment与Activity之间的相互通信(二)

    @Subscribe
    public void onEventMainThread(Event item) {
        viewpager.setCurrentItem(item.getPosition());
    }

目的就是在Activity中让viewpager切换到指定的位置

4.recyclerview的拖拽排序事件
这里使用了RecyclerView的ItemTouchHelper类来实现了Item的拖动和删除功能,ItemTouchHelper是v7包下的一个类,专门用来配合RecyclerView实现滑动删除和拖拽功能的类。我们看看怎么使用。

public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {
    private onMoveAndSwipedListener mAdapter;

    public SimpleItemTouchHelperCallback(onMoveAndSwipedListener listener) {
        mAdapter = listener;
    }


    /**
     * 这个方法是用来设置我们拖动的方向以及侧滑的方向的
     */
    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        //如果是ListView样式的RecyclerView
        //设置拖拽方向为上下
        final int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        //将方向参数设置进去
        return makeMovementFlags(dragFlags, 0);
    }

    /**
     * 当我们拖动item时会回调此方法
     */
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {

        //如果两个item不是一个类型的,我们让他不可以拖拽
        if (viewHolder.getItemViewType() != target.getItemViewType()) {
            return false;
        }
        //回调adapter中的onItemMove方法
        mAdapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    /**
     * 当我们侧滑item时会回调此方法
     */
    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

    }

(1)自定义一个类继承实现ItemTouchHelper.Callback接口,实现里面的三个方法,分别是设置拖动与侧滑的方向,拖动时回调的方法,侧滑时回调的方法。
我这里没有进行侧滑的处理,所以将 makeMovementFlags(dragFlags, swipeFlags)中第二个参数设置为0,表示不进行侧滑操作。
(2)如果我们设置了非0的dragFlags ,那么当item被拖拽的时候会不断的回调onMove方法,所以我们需要同时Adapter做出相应的改变,对mItems数据做出交换的操作,因此我们需要一个回调接口来继续回调Adapter中的方法。

public interface onMoveAndSwipedListener {
    boolean onItemMove(int fromPosition , int toPosition);
}

我们让TabsAdapter实现此接口,并且重写里面的方法

public class TabsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements onMoveAndSwipedListener {}

重写拖动的方法,其实就是交换集合中指定元素的位置:

  @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        //交换mItems数据的位置
        Collections.swap(TabLayoutActivity.tabTitle, fromPosition, toPosition);
        //交换RecyclerView列表中item的位置
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

再回到我们的SimpleItemTouchHelperCallback,在构造方法中将实现了onMoveAndSwipedListener接口的TabsAdapter 传进来。然后我们就在onMove()方法里获取当前拖拽的item和已经被拖拽到所处位置的item的ViewHolder,有了这2个ViewHolder,我们就可以拿到对应的position,然后调用传递过来的adapter中的onItemMove方法,这样adapter就会进行改变。

(3)这样recyclerview的拖拽就实现了,我们希望拖拽的Item在拖拽的过程中背景颜色加深,这样就需要继续重写下面两个方法:

/**当状态改变时回调此方法*/
  @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {
        if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
            viewHolder.itemView.setBackgroundColor(Color.LTGRAY);
        }
        super.onSelectedChanged(viewHolder, actionState);
    }

 /**当用户拖拽完或者侧滑完一个item时回调此方法,用来清除施加在item上的一些状态*/
    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        super.clearView(recyclerView, viewHolder);
        viewHolder.itemView.setBackgroundColor(0);
    }

(4)我们要是希望拖动右侧图标也可以进行拖拽,那么我们需要实现ItemTouchHelper的StartDrag(ViewHolder viewHolder)方法。
现在问题就是取得按住图标时对应的viewholder就行,其实这个实现方法和获取点击item的position一样,接口回调即可实现。

        tabViewHolder.imgs.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                //如果按下
                if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
                    //回调RecyclerListFragment中的startDrag方法
                    //让mItemTouchHelper执行拖拽操作
                    dragListener.startDrag(holder);
                }
                return false;
            }
        });

拿到holder以后,让fragment实现这个接口里的方法,进行操作即可。

5.fragment的相关代码:

public class TabLayoutFragment extends Fragment implements TabsAdapter.onStartDragListener {}

实现拖动图标时候拖拽事件的接口

        //关联ItemTouchHelper和RecyclerView
        ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter);
        mItemTouchHelper = new ItemTouchHelper(callback);
        mItemTouchHelper.attachToRecyclerView(recyclerview);

然后就是关联我们的ItemTouchHelper和RecyclerView,并且记得在实现方法里面加上操作:

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

6.activity的相应代码:
为了演示效果,我这里使用的是一个静态集合,所以在fragment排序以后,数据顺序已经改变,我要做的就是在点击checkbox的时候,通知activity的tablayout的标题进行改变。

     adapter.setTabTitle(tabTitle);
     tab.setupWithViewPager(viewpager);

其实就是重新设置了一下标题,并进行关联。可以看到,还是达到了预期效果。

过几天试着模仿类似网易和微博那种tabs的添加,删除,排序等效果,今天就到这里,收工~~~

源码下载

网友评论

登录后评论
0/500
评论
tangyangkai
+ 关注