您的位置:首页 > 移动开发 > Android开发

[置顶] Xamarin android 使用RecyclerView结合SwipeRefreshLayout下拉刷新滑到底部加载更多

2017-09-14 00:30 603 查看
接触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();
};




4. SwipeRefreshLayout结合RecyclerView实现下拉刷新滑到底部加载更多

这种下拉刷新和滑到底部加载更多在实际应用中非常常见SwipeRefreshLayout是兼容包v4下的控件,可以参见SwipeRefreshLayout基本使用

SwipeRefreshLayout结合RecyclerView实现1.下拉刷新2.(关键)滑动底部加载更多swipeRefresLayout刷新事件即可实现下拉刷新的效果,这个不是难点,关键是如何实现滑动到底部加载更多。最终实现的效果如首图。如果你看过java的事例通过监听RecyclerView的滑动,实现匿名内部类,重写onScrollStateChanged方法就可以实现。

但是在xamarin android中并没有提供RecyclerView滑动的接口,这就尴尬了。在RecyclerView定义中可以看到有一个AddOnScrollListtener(OnScrollListener listener)的虚方法



通过添加AddOnScrollListener方法添加一个OnScrollListener的对象即可重写滑动事件,在OnScrollListener类中的定义我们可以看到有这两个虚方法OnScrollStateChanged 、OnScrolled。



新建一个子类RecyclerViewOnScrollListtener继承OnScrollListener,在子类中我只需要重写OnScrolled方法既可

OnScrollStateChanged :当RecyclerView的滑动状态改变时触发,滑动状态有3种,0:ScrollStateIdle手指离开屏幕、1:ScrollStateDragging手指触碰屏幕、2:ScrollStateSetting。

OnScrolled:当RecyclerView滑动时触发。

所以通过上面的两个方法的比较,仅需要重写OnScrolled方法,在OnScrolled中判断是否滑到了底部,OnScrollStateChanged只是判断滑动的手势状态。

RecyclerViewOnScrollListtener.cs 如下

public   class  RecyclerViewOnScrollListtener: RecyclerView.OnScrollListener
{
private SwipeRefreshLayout _swipeRefreshLayout;
private LinearLayoutManager _linearLayoutManager;//布局管理器
private RecyclerViewAdapter _adapter;//recyclerView 适配器
private Android.OS.Handler handler;
public delegate void InsertData();//添加更多数据的委托
private InsertData _InsertDataEvent; //加载更多的事件
private bool _IsLoadingMore;
public RecyclerViewOnScrollListtener(SwipeRefreshLayout swipeRefreshLayout, Android.OS.Handler handle,LinearLayoutManager linearLayoutManager, RecyclerViewAdapter recyclerViewAdapter, InsertData InsertDataEvent,bool IsLoadingMore) {
_swipeRefreshLayout = swipeRefreshLayout;
_linearLayoutManager = linearLayoutManager;
_adapter = recyclerViewAdapter;
_InsertDataEvent = InsertDataEvent;
handler = handle;
_IsLoadingMore = IsLoadingMore;
}

//当RecyclerView的滑动状态改变时触发
//滑动状态有3种,0:ScrollStateIdle手指离开屏幕1ScrollStateDragging:手指触碰屏幕2ScrollStateSetting
public override void OnScrollStateChanged(RecyclerView recyclerView, int newState)
{
base.OnScrollStateChanged(recyclerView, newState);
System.Diagnostics.Debug.Write("test","newState:"+newState);
}

//当RecyclerView活动时触发
public override void OnScrolled(RecyclerView recyclerView, int dx, int dy)
{
base.OnScrolled(recyclerView,dx,dy);
System.Diagnostics.Debug.Write("正在滑动");
int lastVisibleItemPosition = _linearLayoutManager.FindLastVisibleItemPosition();

if (lastVisibleItemPosition + 1 == _adapter.ItemCount)
{
System.Diagnostics.Debug.Write("test","loadding已经完成");
bool isRefreshing = _swipeRefreshLayout.Refreshing;
if (isRefreshing)
{
_adapter.NotifyItemRemoved(_adapter.ItemCount);
return;
}
if (!_IsLoadingMore)
{
_IsLoadingMore = true;
handler.PostDelayed(() =>
{
_InsertDataEvent();
System.Diagnostics.Debug.Write("test", "加载more已经完成");
_IsLoadingMore = false;
}, 3000);
}
}
}
}


另外我还需要添加recyclerView底部的布局,所以在RecyclerViewAdapter中需要重写GetItemViewType 方法,以及添加底部的布局,修改后的代码如下:

public class RecyclerViewAdapter : RecyclerView.Adapter
{
private List<string> _data;
private Context _context;
private const int VIEW_ITEM = 0;
private const int VIEW_FOOTER = 1;
public delegate void ItemClick(View v ,int position);
public  event ItemClick OnItemClick;
private  interface OnItemClickListener {
void onItemClick(View view ,int position);
};
private OnItemClickListener _OnItemClickListener = null;
public RecyclerViewAdapter(List<string> data,Context context)
{
_data = data;
_context = context;
}
public override int ItemCount
{
get
{
return _data.Count==0?0:_data.Count+1;
}
}
public override int GetItemViewType(int position)
{
if (position + 1 == ItemCount)
return VIEW_FOOTER;
return VIEW_ITEM;
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
if (holder is MyViewHolder)
{
MyViewHolder myViewHolder = holder as MyViewHolder;
myViewHolder.tv_title.Text = _data[position];
myViewHolder.ItemView.Tag = position;
}
}

public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
if (viewType == VIEW_ITEM)
{
var itemView = LayoutInflater.From(_context).Inflate(Resource.Layout.item_recyclerView, parent, false);
MyViewHolder myViewHolder = new MyViewHolder(itemView);
itemView.Click += delegate
{
OnItemClick(itemView, (int)itemView.Tag);
};
return myViewHolder;
}
else if (viewType==(int)VIEW_FOOTER)
{
View view = LayoutInflater.From(_context).Inflate(Resource.Layout.item_recyclerView_foot,parent,false);
return new FootViewHolder(view);
}
return null;
}
}
public class MyViewHolder:RecyclerView.ViewHolder
{
public TextView tv_title;
public MyViewHolder(View itemView):base(itemView)
{
tv_title = itemView.FindViewById<TextView>(Resource.Id.tv_num);
}
}
public class FootViewHolder : RecyclerView.ViewHolder
{
public FootViewHolder(View view):base(view)
{
}
}
}


布局代码Main.axml 、item_recyclerView_foot.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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:id="@+id/toolbar"
android:minHeight="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_height="match_parent"
android:layout_width="match_parent"
android:divider="#000000"
android:dividerHeight="5dp"
android:id="@+id/recyclerView" />
</android.support.v4.widget.SwipeRefreshLayout>
</LinearLayout>


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp">
<ProgressBar
android:layout_marginRight="6dp"
android:id="@+id/progressBar_loadmMore"
style="?android:attr/progressBarStyle"
android:progressDrawable="@color/primary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="加载更多..."
android:textSize="14sp"
android:textColor="#808080" />
</LinearLayout>


最后我们要在MainActivity中实现刷新的逻辑了。

_swipeRefreshLayout.Refresh +=delegate {
_swipeRefreshLayout_Refresh();
};
//进入activity即开始刷新
_swipeRefreshLayout.Post(()=> {
_swipeRefreshLayout.Refreshing = true;
_swipeRefreshLayout_Refresh();                                                           });
//下拉刷新数据的方法
private void _swipeRefreshLayout_Refresh()
{
handle.PostDelayed(()=> {
//_data.Clear();
InsertData();
},2000);
}
//加载更多数据模拟添加
private  void AddData()
{
for (int i = 0; i < 4; i++)
{
_data.Add("张林" + _adapter.ItemCount);
}
_adapter.NotifyDataSetChanged();
_swipeRefreshLayout.Refreshing = false;
_adapter.NotifyItemRemoved(_adapter.ItemCount);
}
//下拉刷新模拟往前插入数据
private void InsertData()
{
for (int i = 0; i< 4; i++)
{
_data.Insert(i,"张林" + _adapter.ItemCount);
}
_adapter.NotifyDataSetChanged();
_swipeRefreshLayout.Refreshing = false;
_adapter.NotifyItemRemoved(_adapter.ItemCount);
}
//添加滑动底部加载更多方法的对象
RecyclerView.OnScrollListener scroll = new RecyclerViewOnScrollListtener(_swipeRefreshLayout,handle, _linearLayoutManager,_adapter, AddData, IsLoadingMore);
_recyclerView.AddOnScrollListener(scroll);


作者:张林 标题:Xamarin android

使用RecyclerView结合SwipeRefreshLayout下拉刷新滑到底部加载更多

原文地址:http://blog.csdn.net/kebi007/article/details/77972811 转载随意注明出处
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐