标签:
本文将使用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。
直接看代码:
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中可以同步更新。
做一个简单的总结:
标签:
原文地址:http://blog.csdn.net/h_zhang/article/details/51886724