接触RecycleView有一段时间了,前段时间写了一篇关于ListView万能适配器的文章,有人就评论道“现在谁还用ListView”,挺尴尬的……,一般的需求实现功能就ok了,和用什么控件关系不大,不过说还是有一点道理的,从功能和用户体验上来看RecyclerView就好像是加强版的ListView,能实现ListView所有的功能,所以今天就来写一个在Xamarin android中RecyclerView的使用小白教程,大神勿扰。
RecyclerView小白教程关键的实现步骤如下:
- RecyclerView的简单实现
- RecyclerView添加分割线
- RecyclerView实现单击水波纹效果
- RecyclerView添加单击事件
- SwipeRefreshLayout结合RecyclerView实现下拉刷新滑到底部加载更多
1. RecyclerView的简单实现
RecyclerView是V7兼容包的一个控件,它的优点在于使用灵活,插拔式的体验,多种显示方式。可以说是ListView和GridView的升级版。
官方API介绍
A flexible view for providing a limited window into a large data set
一种灵活的视图,可以为大数据集提供有限的窗口
官网API链接:https://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
我们先来看一下Activity中的代码RecycleViewSimple.cs,我们要做的是设置布局管理,设置数据适配器。
[Activity(Label = "RecyclerViewSimple",MainLauncher =true,Theme = "@style/BaseAppTheme")]
public class RecyclerViewSimple : AppCompatActivity
{
private Toolbar toolbar;
private RecyclerView recyclerView;
private RecyclerViewAdapter adapter;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.RecycleView);
List<string> data = new List<string>() { "科比", "加内特", "麦迪", "费舍尔", "杰梅因奥尼尔", "大Z", "纳什", "雷阿伦", "马布里", "艾弗森", "安东尼沃克" };
recyclerView = FindViewById<RecyclerView>(Resource.Id.recyclerView);
adapter = new RecyclerViewAdapter(data,this);
recyclerView.SetLayoutManager(new LinearLayoutManager(this));
recyclerView.SetAdapter(adapter);
}
}
然后我们来实现一个RecyclerView的适配器RecyclerViewAdapter.cs
public class RecyclerViewAdapter : RecyclerView.Adapter
{
private List<string> data;
private Context _context;
public RecyclerViewAdapter(List<string> list,Context context)
{
data = list;
_context = context;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyViewHolder myViewHolder = holder as MyViewHolder;
myViewHolder.tvTitle.Text = data[position];
}
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent,int viewItem)
{
View view = LayoutInflater.From(_context).Inflate(Resource.Layout.item_recyclerView,parent,false);
MyViewHolder holder = new MyViewHolder(view);
return holder;
}
public override int ItemCount
{
get
{
return data.Count;
}
}
public override void OnViewRecycled(Java.Lang.Object holder)
{
base.OnViewRecycled(holder);
MyViewHolder myViewHolder = holder as MyViewHolder;
}
}
public class MyViewHolder : RecyclerView.ViewHolder
{
public TextView tvTitle;
public MyViewHolder(View itemView):base(itemView)
{
tvTitle = itemView.FindViewById<TextView>(Resource.Id.tv_num);
}
}
最后我们来看一下布局的代码主布局RecycleView.axml
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@color/red"
android:dividerHeight="5dp" />
</LinearLayout>
RecyclerView的子布局item_recyclerView.axml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_recyclerview">
<TextView
android:id="@+id/tv_num"
android:layout_width="match_parent"
android:layout_height="60dp"
android:textColor="#000000"
android:gravity="center"
android:text="1"/>
</LinearLayout>
最终的实现效果图:
2. RecyclerView添加分割线
我们发现RecyclerView的divider和divider是不起作用的,默认这两个属性是无效的,所以我们只能RecyclerView提供ItemDecoration类添加分割线,可以看看这篇深入理解ItemDecoration:http://www.tuicool.com/articles/fIbuYfI。
调用RecyclerView.AddItemDecoration方法添加分割线,Recycler在绘制分割线时,会调用该类的OnDraw和OnDrawOver方法。
所以我们需要重写两个方法OnDraw或OnDrawOver(绘制分割线) 和GetItemOffsets(为RecyclerView的item设置一定的偏移量)
新建一个MyItemDecoration.cs:
public class MyItemDecoration:RecyclerView.ItemDecoration
{
private static int[] ATTRS = new int[] {Android.Resource.Attribute.ListDivider};
public static int HORIZONTAL = LinearLayoutManager.Horizontal;
public static int VERTICAL = LinearLayoutManager.Vertical;
private Drawable _divider;
private int _orientation;
public MyItemDecoration(Context context ,int orientation)
{
TypedArray t = context.ObtainStyledAttributes(ATTRS);
_divider = t.GetDrawable(0);
t.Recycle();
SetOrientation(orientation);
}
public void SetOrientation(int orientation)
{
if (orientation != HORIZONTAL && orientation != VERTICAL)
throw new System.Exception("invalid orientation");
_orientation = orientation;
}
public override void OnDraw(Canvas cValue, RecyclerView parent)
{
if (_orientation == VERTICAL){
DrawVertical(cValue,parent);
}
else{
DrawHorizontal(cValue, parent);
}
}
//竖屏时画竖线
public void DrawVertical(Canvas c, RecyclerView parent)
{
int left = parent.PaddingLeft;
int right = parent.Width - parent.PaddingRight;
int childCount = parent.ChildCount;
for (int i = 0; i < childCount; i++)
{
View childView = parent.GetChildAt(i);
RecyclerView v = new RecyclerView(parent.Context);
RecyclerView.LayoutParams _params = (RecyclerView.LayoutParams)childView.LayoutParameters;
int top = childView.Bottom + _params.BottomMargin;
int bottom = top + _divider.IntrinsicHeight;
_divider.SetBounds(left,top,right,bottom);
_divider.Draw(c);
}
}
//横屏时画横线
public void DrawHorizontal(Canvas c, RecyclerView parent)
{
int top = parent.PaddingTop;
int bottom = parent.Height - parent.PaddingBottom;
int childCount = parent.ChildCount;
for (int i = 0; i < childCount; i++)
{
View childView = parent.GetChildAt(i);
RecyclerView v = new RecyclerView(parent.Context);
RecyclerView.LayoutParams _params = (RecyclerView.LayoutParams)childView.LayoutParameters;
int left = childView.Right + _params.RightMargin;
int right= left + _divider.IntrinsicHeight;
_divider.SetBounds(left, top, right, bottom);
_divider.Draw(c);
}
}
public override void GetItemOffsets(Rect outRect, int itemPosition, RecyclerView parent)
{
if (_orientation == VERTICAL) {
outRect.Set(0,0,0,_divider.IntrinsicHeight);
}
else{
outRect.Set(0,0,_divider.IntrinsicWidth,0);
}
}
}
在activity中添加分割线加上这句代码
recyclerView.AddItemDecoration(new MyDecoration(this,(int)Orientation.Vertical));
效果如图:
private static int[] ATTRS = new int[] {Android.Resource.Attribute.ListDivider};这里调用的是系统的分割线样式,参考hongyang的博客,我们也可以在Theme中自定义这个分割线的样式。Theme中是这样的
<style name="BaseAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="colorPrimary">@color/primary</item>
<item name="colorPrimaryDark">@color/primaryDark</item>
<item name="android:listDivider">@drawable/divider_bg</item>
</style>
divider_bg.xml
<?xml version="1.0" encoding="utf-8" ?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:centerColor="#ff00ff00"
android:endColor="#ff0000ff"
android:startColor="#ffff0000"
android:type="linear"/>
<size android:height="4dp"/>
</shape>
最终实现了一个彩虹色的分割线
3.RecyclerView添加点击产生水波纹效果
和分割线一样,RecyclerView默认的也是没有水波纹的效果,这点不同于ListView。由于RecyclerView是android5.0的控件,所以先新建一个drawable-v21的文件夹,添加ripple_recyclerview.xml
<?xml version="1.0" encoding="utf-8" ?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/primary">
<item android:drawable="@color/white"/>
</ripple>
同时也需要支持android5.0以下的版本,当然没有这种水波纹的效果。在drawable文件夹中添加ripple_recyclerview.xml
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/primary" android:state_pressed="true"/>
<item android:drawable="@color/white" android:state_pressed="false"/>
</selector>
在RecyclerView的子布局item_recyclerView.axml中加上background属性
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ripple_recyclerView">
<TextView
android:id="@+id/tv_num"
android:layout_height="50dp"
android:layout_width="match_parent"
android:gravity="center"
android:text="1" />
</LinearLayout>
在android5.0以上版本,现在单击就会产生一个水波纹的效果,符合Material Design的设计规范
4. RecyclerView添加单击事件
RecyclerView中的item添加单击事件,可以在RecyclerAdapter.cs中去添加,要注意两点:1.错位2.事件的具体实现推荐在MainActivity中去实现,也就是在适配器中仅声明,事件的实现交给外面的调用者
参考java的具体做法:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0327/2647.html
C#中没有java匿名内部类,不能做到在设置事件监听的时候去new 一个接口,但是C#有委托,在适配器中声明一个委托,将这个事件传递给外面的调用者,实现效果还是一样的。首先我们在RecyclerViewAdapter.cs中声明委托和事件
public delegate void ItemClick(View v ,int position);
public event ItemClick OnItemClick;
在OnCreateViewHolder方法中添加事件
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
var itemView = LayoutInflater.From(_context).Inflate(Resource.Layout.item_recyclerView, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(itemView);
itemView.SetOnClickListener(this);
itemView.Click += delegate {
OnItemClick(itemView,(int)itemView.Tag);
};
return myViewHolder;
}
这里的Tag需要在OnBindViewHolder方法中去设置item的position,以便点击时获取
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyViewHolder myViewHolder = holder as MyViewHolder;
myViewHolder.tv_title.Text=_data[position];
myViewHolder.ItemView.Tag=position;
}
在Acticity中添加事件的具体实现
adapter.OnItemClick += (view, position) =>
{
Toast.MakeText(this,"position:"+position+","+data[position],ToastLength.Short).Show();
};