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

来源:互联网 发布:mac怎么关掉客人用户 编辑:程序博客网 时间:2024/05/16 17:17

本文将使用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的长按拖动排序的功能。

源码下载

3 1
原创粉丝点击