RecyclerView---高仿网易新闻客户端

本文将使用RecyclerView,带领大家实现类似网易新闻客户端的Tab界面效果。

先贴上效果图:
这里写图片描述

关于RecyclerView的基本使用大家可以参考鸿洋的文章:http://blog.csdn.net/lmj623565791/article/details/45059587

好的,下面进入本文主题。。。


添加依赖包

build.gradle

compile 'com.android.support:recyclerview-v7:23.2.1'

实现界面布局

首先,可以看到每一个Tab有一个背景样式。在drawable文件夹下新建xml文件。

drawable/tv_bg.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <stroke android:width="1dp" android:color="#aaa"/>

    <solid android:color="#eee"/>

    <corners android:radius="5dp"/>

</shape>

矩形的shape,stoke为边框,solid为背景色,corners为圆角矩形的半径。

item.xml
下面,是RecyclerView中每一个Item的布局:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv"
        android:layout_width="74dp"
        android:layout_height="34dp"
        android:layout_marginTop="5dp"
        android:layout_marginLeft="5dp"
        android:background="@drawable/tv_bg"
        android:gravity="center"
        android:text="头条"
        android:textSize="14sp"
        />

    <ImageView
        android:id="@+id/delelte"
        android:layout_width="15dp"
        android:layout_height="15dp"
        android:visibility="invisible"
        android:src="@mipmap/delete"/>

</FrameLayout>

就是一个TextView和一个ImageView,ImageView为左上角小的删除图标,默认情况下是invisible状态。

下面就是Activity的主布局文件:

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingBottom="8dp"
                android:paddingLeft="16dp"
                android:paddingRight="16dp"
                android:paddingTop="8dp">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="切换栏目"/>

                <TextView
                    android:id="@+id/tv_finish"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_alignParentRight="true"
                    android:text="完成"
                    android:textColor="@android:color/holo_red_light"
                    android:textSize="16sp"
                    android:visibility="invisible"/>

            </RelativeLayout>

            <View
                android:layout_width="match_parent"
                android:layout_height="1dp"
                android:background="@android:color/darker_gray"/>


            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycle_selected"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="right"
                android:layout_marginRight="8dp"
                android:text="长按排序或删除"
                android:textColor="@android:color/darker_gray"/>
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:paddingTop="8dp"
            android:background="#ddd"
            android:orientation="vertical">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:paddingLeft="16dp"
                android:text="点击添加更多栏目"/>

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recycle_unselected"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginTop="8dp"/>

        </LinearLayout>

    </LinearLayout>

</ScrollView>

准备数据

public class MainActivity extends AppCompatActivity
{
    
    
    ...

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

        mRecycleSelected = (RecyclerView) findViewById(R.id.recycle_selected);
        mRecycleUnSelected = (RecyclerView) findViewById(R.id.recycle_unselected);
        mFinishedText = (TextView) findViewById(R.id.tv_finish);

        initData();
        initView();
        initEvent();
    }

    ...
}

在Activity的onCreate()方法中拿到两个RecyclerView的实例,和右上方”完成”按钮的实例。

先贴一下initData()代码:

private void initData()
{
    mSelectedDatas = new ArrayList<String>();
    mSelectedDatas.add("头条");
    mSelectedDatas.add("娱乐");
    mSelectedDatas.add("精选");
    mSelectedDatas.add("热点");
    mSelectedDatas.add("体育");
    mSelectedDatas.add("网易号");
    mSelectedDatas.add("直播");
    mSelectedDatas.add("财经");
    mSelectedDatas.add("科技");
    mSelectedDatas.add("房产");
    mSelectedDatas.add("汽车");
    mSelectedDatas.add("轻松一刻");
    mSelectedDatas.add("跟帖");
    mSelectedDatas.add("图片");
    mSelectedDatas.add("段子");
    mSelectedDatas.add("家具");
    mSelectedDatas.add("游戏");
    mSelectedDatas.add("健康");
    mSelectedDatas.add("政务");
    mSelectedDatas.add("漫画");
    mSelectedDatas.add("中国足球");
    mSelectedDatas.add("数码");
    mSelectedDatas.add("趣闻");

    mUnselectedDatas =  new ArrayList<String>();
    mUnselectedDatas.add("NBA");
    mUnselectedDatas.add("社会");
    mUnselectedDatas.add("军事");
    mUnselectedDatas.add("欧洲杯");
    mUnselectedDatas.add("CBA");
    mUnselectedDatas.add("跑步");
    mUnselectedDatas.add("移动互联");
    mUnselectedDatas.add("云课堂");
    mUnselectedDatas.add("房产");
    mUnselectedDatas.add("旅游");
    mUnselectedDatas.add("读书");
    mUnselectedDatas.add("酒香");
    mUnselectedDatas.add("教育");
    mUnselectedDatas.add("亲子");
    mUnselectedDatas.add("暴雪游戏");
    mUnselectedDatas.add("态度营销");
    mUnselectedDatas.add("时尚");
    mUnselectedDatas.add("情感");
    mUnselectedDatas.add("艺术");
    mUnselectedDatas.add("海外");
    mUnselectedDatas.add("博客");
    mUnselectedDatas.add("论坛");
    mUnselectedDatas.add("型男");
    mUnselectedDatas.add("萌宠");
}

没什么好说的,就是为两个RecyclerView准备了一些数据。

下面看initView()的代码:

private void initView()
{
    mRecycleSelected.setLayoutManager(new GridLayoutManager(this, 4));
    mSelectedAdatper = new SelectedRecycleAdapter(this, mSelectedDatas);
    mRecycleSelected.setAdapter(mSelectedAdatper);
    mRecycleSelected.addItemDecoration(new SpaceItemDecoration(8));

    mRecycleUnSelected.setLayoutManager(new GridLayoutManager(this, 4));
    mUnSelectedAdatper = new UnSelectedRecycleAdapter(this, mUnselectedDatas);
    mRecycleUnSelected.setAdapter(mUnSelectedAdatper);
    mRecycleUnSelected.addItemDecoration(new SpaceItemDecoration(8));

}

这个方法中,为两个RecyclerView设置了LayoutManager、RecyclerView.Adapter以及RecyclerView.ItemDecoration。其中LayoutManager为GridLayoutManager,列数为4。SpaceItemDecoration主要功能就是为每一个Item添加间隙。不然你会发现所有的Item都挤在一起。

下面看一下ItemDecoration的代码:

public class SpaceItemDecoration extends RecyclerView.ItemDecoration
{
    
    
    private int mSpace;

    public SpaceItemDecoration(int space)
    {
        mSpace = space;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state)
    {
        super.getItemOffsets(outRect, view, parent, state);
        outRect.left = mSpace;
        outRect.top = 0;
        outRect.right = mSpace;
        outRect.bottom = mSpace;
    }
}

复写了getItemOffsets()方法,其中outRect参数是为每个Item设置一个区域。RecyclerView.ItemDecoration中的onDraw()方法会在outRect所指定的范围进行绘制。通过LinearLayoutManager实现ListView的效果,其中分隔线就在ItemDecoration中进行绘制。详情请参考: Android RecyclerView 使用完全解析 体验艺术般的控件

下面看SelectedRecycleAdapter和UnSelectedRecycleAdapter的代码,两个Adapter的代码很相似,这里我就贴一个:

SelectedRecycleAdapter.java

public class SelectedRecycleAdapter extends RecyclerView.Adapter<SelectedRecycleAdapter.MyViewHolder>
{
    
    
    ...
    ...

    private List<String> mDatas;
    private MainActivity mContext;

    public SelectedRecycleAdapter(Context context, List<String> datas)
    {
        mDatas = datas;
        mContext = (MainActivity) context;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
    {
        View itemView = LayoutInflater.from(mContext).inflate(R.layout.item, parent, false);
        MyViewHolder viewHolder = new MyViewHolder(itemView);

        return viewHolder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position)
    {
        holder.tv.setText(mDatas.get(position));
        ...
        ...
    }

    @Override
    public int getItemCount()
    {
        return mDatas.size();
    }

    class MyViewHolder extends RecyclerView.ViewHolder
    {
        TextView tv;
        ImageView ivDelete;

        public MyViewHolder(View itemView)
        {
            super(itemView);

            tv = (TextView) itemView.findViewById(R.id.tv);
            ivDelete = (ImageView) itemView.findViewById(R.id.delelte);
        }
    }
}

OK,Adapter主要负责为RecyclerView准备数据,并且Android已经强制我们使用ViewHolde模式来编写Adapter。

至此,整体界面框架就写好了,界面应该能够正常显示了。下面就是事件处理代码。


事件处理

实现点击、长按事件

为RecyclerView提供点击、长按事件,需要自己定义接口,并提供给外部设置回调。

首先是,SelectedRecycleAdapter.java

public class SelectedRecycleAdapter extends RecyclerView.Adapter<SelectedRecycleAdapter.MyViewHolder>
{
    
    
    ...
    public interface OnItemClickListener
    {
    
    
        void onItemClickListener(MyViewHolder viewHolder, int pos);
        void onItemLongClickListener(MyViewHolder viewHolder, int pos);
    }

    private OnItemClickListener mListener;

    public void setOnItemClickListener(OnItemClickListener listener)
    {
        this.mListener = listener;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int position)
    {
        holder.tv.setText(mDatas.get(position));

        ...

        if(mListener != null) {
            holder.itemView.setOnClickListener(new View.OnClickListener()
            {
                @Override
                public void onClick(View v)
                {
                    mListener.onItemClickListener(holder, position);
                }
            });

            holder.itemView.setOnLongClickListener(new View.OnLongClickListener()
            {
                @Override
                public boolean onLongClick(View v)
                {
                    mListener.onItemLongClickListener(holder, position);
                    return false;
                }
            });
        }
}

接下来,在外部设置该接口:
MainActivity.java

private void initEvent()
{
    ...

    mSelectedAdatper.setOnItemClickListener(new SelectedRecycleAdapter.OnItemClickListener()
    {
        @Override
        public void onItemClickListener(SelectedRecycleAdapter.MyViewHolder viewHolder, int pos)
        {
            if(!isDeleteIconsShow) {
                Toast.makeText(MainActivity.this, mSelectedDatas.get(pos), Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        public void onItemLongClickListener(SelectedRecycleAdapter.MyViewHolder viewHolder, int pos)
        {
            if(!isDeleteIconsShow) {
                showAllDeleteIcons();

                mFinishedText.setVisibility(View.VISIBLE);
            }
        }
    });

    ...
}

如果发生点击事件,简单弹一个Toast出来;如果是长按,就显示所有的Delete Icon图标,并且显示右上角”完成”TextView。

长按拖动排序

借助ItemTouchHelper来实现长按拖动排序的功能。首先查看一下这个类的作用:

This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView.
It works with a RecyclerView and a Callback class, which configures what type of interactions are enabled and also receives events when user performs these actions.

大致意思:ItemTouchHelper是一个工具类,为RecyclerView提供拖拽、滑动的支持。并且配合一起工作的还有个ItemTouchHelper.Callback类,这个类内部可以处理用户具体的action事件。

首先,编写Callback类, ItemTouchHelperCallback .java:

/**
 * Created by hzh on 2016/7/8.
 * 该类工作与ItemTouchHelper和你的app之间,起一个桥梁的作用
 * 主要负责,定义用户drag和swipe的方向,以及当户产生了指定手势会收到相应的回调方法
 */
public class ItemTouchHelperCallback extends ItemTouchHelper.Callback
{
    
    
    private OnItemPositionChangeListener  mListener;

    //通过构造函数,设置接口实例
    public ItemTouchHelperCallback(OnItemPositionChangeListener mListener)
    {
        this.mListener = mListener;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder)
    {
        //the direction of item which be dragged
        final int dragFlags = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT
                | ItemTouchHelper.UP | ItemTouchHelper.DOWN;


        //can be dragged, can not be swiped
        return makeMovementFlags(dragFlags, 0);
    }

    //接口回调,Adapter根据这个接口交换item的位置
    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target)
    {
        if(mListener != null) {
            return mListener.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        }

        return false;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction)
    {

    }

    public interface OnItemPositionChangeListener
    {
    
    
        boolean onItemMove(int fromPos, int toPos);
    }
}

这个类的构造函数,需要外部设置一个OnItemPositionChangeListener 接口传递进来,可以看到这是一个自定义的接口,用来实现拖动排序的功能。复写getMovementFlags()方法,这个方法用来用户控制拖动的方向。onMove()方法,当用户在RecyclerView上move,就会回调这个方法,并且这个方法还会将RecyclerView,SrcViewHolder,targetViewHodler作为参数传递进来,在方法内部调用mListener.onItemMove()让Adapter根据移动的位置变化去更新数据集中的数据的位置。具体大家可以查阅官方文档。

接下来,SelectedRecycleAdapter需要实现这个接口。

SelectedRecycleAdapter.java

public class SelectedRecycleAdapter extends RecyclerView.Adapter<SelectedRecycleAdapter.MyViewHolder>
    implements ItemTouchHelperCallback.OnItemPositionChangeListener
{
    
    
    ...
    //根据用户的手势,交换Adapter数据集中item的位置
    @Override
    public boolean onItemMove(int fromPos, int toPos)
    {
        Collections.swap(mDatas, fromPos, toPos);
        notifyItemMoved(fromPos, toPos);
        return true;
    }
    ...
}

Adapter实现了自定义接口并复写onItemMove(),根据传过来的两个位置,去交换数据集合中数据的位置,之后调用notifyItemMoved(fromPos, toPos)方法去通过UI,数据发生了移动。这就实现了拖动排序的功能。

接下来看,Activity中使用:

private void initEvent()
{
    //初始化ItemTouchHelper实例
    ItemTouchHelperCallback callback = new ItemTouchHelperCallback(mSelectedAdatper);
    mItemTouchHelper = new ItemTouchHelper(callback);
    //mItemTouchHelper关联RecyclerView
    mItemTouchHelper.attachToRecyclerView(mRecycleSelected);

    ....
}

new了ItemTouchHelper实例,并且ItemTouchHelper需要传入ItemTouchHelper.Callback实例。最后通过ItemTouchHelper.attachToRecyclerView()方法关联具体的RecyclerView。

点击添加、删除Item

直接看代码:

MainActivity.java

private void initEvent()
{
    ...

    mSelectedAdatper.setOnDeleteIconClickListener(new SelectedRecycleAdapter.OnDeleteIconClickListener()
    {
        @Override
        public void onDeleteIconClick(int pos)
        {
            mUnSelectedAdatper.addData(mSelectedDatas.get(pos), mUnselectedDatas.size());
            mSelectedAdatper.removeData(pos);
        }
    });

    ...
}

为delete Icon注册点击事件,在事件处理方法中,实现RecyclerView数据的添加或删除。

下面看一下addData()和removeData()方法的实现:

SelectedRecycleAdapter.java

public class SelectedRecycleAdapter extends RecyclerView.Adapter<SelectedRecycleAdapter.MyViewHolder>
    implements ItemTouchHelperCallback.OnItemPositionChangeListener
{
    
    
    ...

    public void addData(String data, int pos)
    {
        mDatas.add(pos, data);
        notifyItemInserted(pos);
    }

    public void removeData(int pos)
    {
        mDatas.remove(pos);
        notifyItemRemoved(pos);
    }

    ...
}

先更新数据集mDatas中的数据,然后通过notifyItemInserted()和notifyItemRemoved()通知所有的观察者Adapter中的数据发生了变化,这样在UI中可以同步更新。


总结

做一个简单的总结:

  • RecyclerView基本使用:导入依赖包,RecyclerView、RecyclerView.Adapter、RecyclerView.LayoutManager、RecyclerView.ItemDecoration之间的关系。
  • 为RecyclerView实现点击、长按事件处理。
  • 了解ItemTouchHelper的功能、用法。可以通过这个类实现RecyclerView的长按拖动排序的功能。

源码下载

猜你喜欢

转载自blog.csdn.net/H_Zhang/article/details/51886724