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

版权声明:本文为博主原创文章,未经博主允许转载随意。 https://blog.csdn.net/kebi007/article/details/77972811

接触RecycleView有一段时间了,前段时间写了一篇关于ListView万能适配器的文章,有人就评论道“现在谁还用ListView”,挺尴尬的……,一般的需求实现功能就ok了,和用什么控件关系不大,不过说还是有一点道理的,从功能和用户体验上来看RecyclerView就好像是加强版的ListView,能实现ListView所有的功能,所以今天就来写一个在Xamarin android中RecyclerView的使用小白教程,大神勿扰。

RecyclerView小白教程关键的实现步骤如下:

  1. RecyclerView的简单实现
  2. RecyclerView添加分割线
  3. RecyclerView实现单击水波纹效果
  4. RecyclerView添加单击事件
  5. 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:

扫描二维码关注公众号,回复: 2922035 查看本文章
    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 转载随意注明出处

猜你喜欢

转载自blog.csdn.net/kebi007/article/details/77972811