RecyclerView介绍

来源:互联网 发布:上海周浦驾校 知乎 编辑:程序博客网 时间:2024/04/28 23:31

转载自http://wobushi.ren/recyclerview.html#comment-9

RecyclerView全攻略

RecylcerView也发布好一阵子,这货目前能兼容到API 7,直接继承自Viewgroup,比ListView更为轻量,使用得当的话,完全可以替代ListView/GridView。本文将基于RecyclerView实现ListView/GridView的一些基础特性,和下拉刷新,加载更多,PinnedHeader等一些高级特性。

1、添加引用

compile 'com.android.support:recyclerview-v7:21.0.0'

2、初始化

主界面xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical">  <android.support.v7.widget.RecyclerView        android:id="@+id/recycler_<a name="baidusnap4"></a><b>view</b>"        android:layout_width="match_parent"        android:layout_height="match_parent"        /></LinearLayout>

RecyclerView Adapter:

package com.example.shilizhe.recyclerview;import android.support.v7.widget.RecyclerView;import android.<b>view</b>.LayoutInflater;import android.<b>view</b>.<b>View</b>;import android.<b>view</b>.ViewGroup;import android.widget.TextView;/** * Created by slz. */public class RecyclerViewAdapter extends RecyclerView.Adapter&lt;RecyclerViewAdapter.ViewHolder&gt; {    private String[] mData;    public RecyclerViewAdapter(String[] data) {        mData = data;    }    @Override    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {        <b>View</b> <b>view</b> = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_<b>view</b>_adapter,viewGroup,false);        return new ViewHolder(<b>view</b>);    }    @Override    public void onBindViewHolder(ViewHolder viewHolder, int i) {        viewHolder.text.setText(mData[i]);    }    @Override    public int getItemCount() {        return mData.length;    }    public static class ViewHolder extends RecyclerView.ViewHolder {        public TextView text;        public ViewHolder(<b>View</b> itemView) {            super(itemView);            text = (TextView) itemView.findViewById(R.id.<a name="baidusnap3"></a><b>list</b>_tv);        }    }}

绑定数据:

        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));        mRecyclerView.setAdapter(new RecyclerViewAdapter(getData()));

3、排版方式

使用LinearLayoutManager一参构造,默认为垂直布局。如果要使用其水平布局只需在构造中传入对应的参数即可:

mRecyclerView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false));

使用网格显示(GridView)也仅仅只需要更改其LayoutManager即可:

mRecyclerView.setLayoutManager(new GridLayoutManager(this,2));

 

至此,RecyclerView就已能实现ListView与GridView的基础功能

4、生命周期

一个RecyclerView的Item加载是有顺序的,类似于Activity的生命周期(姑且这么叫把),具体可以对adapter的每个方法进行重写打下日志进行查看,具体大致为:

getItemViewType(获取显示类型,返回值可在onCreateViewHolder中拿到,以决定加载哪种ViewHolder)

onCreateViewHolder(加载ViewHolder的布局)

onViewAttachedToWindow(当Item进入这个页面的时候调用)

onBindViewHolder(将数据绑定到布局上,以及一些逻辑的控制就写这啦)

onViewDetachedFromWindow(当Item离开这个页面的时候调用)

onViewRecycled(当Item被回收的时候调用)

tips1:如果你调用

viewHolder.setIsRecyclable(false);

那么这个Item的onViewRecycled将永远不会调用。

tips2:如果你的界面出现错乱的现象,请调用如上代码可能能简单粗暴的解决,当然代价是损失少许的性能表现

进阶篇

1、下拉刷新

1.1、SwipeRefreshLayout

这个思路有2个,一是使用同样v7包中自带的SwipeRefreshLayout,将要下拉刷新的区域包裹在内:

    <android.support.v4.widget.SwipeRefreshLayout        android:id="@+id/slz_srl"        android:layout_weight="1"        android:layout_width="match_parent"        android:layout_height="0dp">        <android.support.v7.widget.RecyclerView            android:id="@+id/recycler_view"            android:layout_width="match_parent"            android:layout_height="match_parent"            />    </android.support.v4.widget.SwipeRefreshLayout>

通过:

   mSlzSrl.setRefreshing(false);

和:

   mSlzSrl.setRefreshing(true);

来控制其界面的是否正在刷新的表现。

通过:

        mSlzSrl.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {            @Override            public void onRefresh() {                //refresh data            }        });

来控制其刷新时的操作。

1.2、pulltorefresh祖传代码法:

参考:https://github.com/baoyongzhang/android-PullRefreshLayout �0�2这个fork,还做几个比较漂亮的下拉动画。

 @Override    public boolean onTouchEvent(MotionEvent ev) {        if (!mIsBeingDragged) {            return super.onTouchEvent(ev);        }        final int action = MotionEventCompat.getActionMasked(ev);        switch (action) {            case MotionEvent.ACTION_MOVE: {                final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);                if (pointerIndex &lt; 0) {                    return false;                }                final float y = MotionEventCompat.getY(ev, pointerIndex);//                final float yDiff = Math.abs(y - mInitialMotionY);                final float yDiff = y - mInitialMotionY;                final float scrollTop = yDiff * DRAG_RATE;                float originalDragPercent = scrollTop / mTotalDragDistance;                if (originalDragPercent &lt; 0) {                    return false;                }                float dragPercent = Math.min(1f, Math.abs(originalDragPercent));//                float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3;//                    float adjustedPercent = dragPercent;                float extraOS = Math.abs(scrollTop) - mTotalDragDistance;                float slingshotDist = mSpinnerFinalOffset;                float tensionSlingshotPercent = Math.max(0,                        Math.min(extraOS, slingshotDist * 2) / slingshotDist);                float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow(                        (tensionSlingshotPercent / 4), 2)) * 2f;                float extraMove = (slingshotDist) * tensionPercent * 2;                int targetY = (int) ((slingshotDist * dragPercent) + extraMove);                if (mRefreshView.getVisibility() != <b>View</b>.VISIBLE) {                    mRefreshView.setVisibility(<b>View</b>.VISIBLE);                }                if (scrollTop &lt; mTotalDragDistance) {                    mRefreshDrawable.setPercent(dragPercent);                }                setTargetOffsetTop(targetY - mCurrentOffsetTop, true);                break;            }            case MotionEventCompat.ACTION_POINTER_DOWN:                final int index = MotionEventCompat.getActionIndex(ev);                mActivePointerId = MotionEventCompat.getPointerId(ev, index);                break;            case MotionEventCompat.ACTION_POINTER_UP:                onSecondaryPointerUp(ev);                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL: {                if (mActivePointerId == INVALID_POINTER) {                    return false;                }                final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);                final float y = MotionEventCompat.getY(ev, pointerIndex);                final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE;                mIsBeingDragged = false;                if (overscrollTop &gt; mTotalDragDistance) {                    setRefreshing(true, true);                } else {                    mRefreshing = false;                    animateOffsetToStartPosition();                }                mActivePointerId = INVALID_POINTER;                return false;            }        }        return true;    }

祖传代码…

2、加载更多(Endless List

2.1、onBindViewHolder法

在onBindViewHolder中可以知道当前加载到的position,那就比较好办

@Overridepublic void onBindViewHolder(ViewHolder viewHolder, int position) {    Item item = mItems.get(position);    viewHolder.Text.setText(item.getTitle());    if (position == mItems.size() - 1) {        mListener.onListEnded();    }}

2.2、onScrollListener法

你需要这些:

private int previousTotal = 0;private boolean loading = true;private int visibleThreshold = 5;int firstVisibleItem, visibleItemCount, totalItemCount;

然后:

mRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {    @Override    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {        super.onScrolled(recyclerView, dx, dy);        visibleItemCount = mRecyclerView.getChildCount();        totalItemCount = mLayoutManager.getItemCount();        firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition();        if (loading) {            if (totalItemCount &gt; previousTotal) {                loading = false;                previousTotal = totalItemCount;            }        }        if (!loading &amp;&amp; (totalItemCount - visibleItemCount)             &lt;= (firstVisibleItem + visibleThreshold)) {            // your loadmore()            loading = true;        }    }});

3、Item项的刷新

RecyclerView依然可以使用notifyDataSetChanged()来刷新整个列表,不过其增加swapAdapter与notifyItemChanged方法,以便适应于各种情况。

public void�0�2swapAdapter�0�2(Adapter�0�2adapter, boolean removeAndRecycleExistingViews)

Swaps the current adapter with the provided one. It is similar to�0�2setAdapter(Adapter)�0�2but assumes existing adapter and the new adapter uses the same�0�2RecyclerView.ViewHolder�0�2and does not clear the RecycledViewPool.

Note that it still calls onAdapterChanged callbacks.

 

PARAMETERS
adapterThe new adapter to set, or null to set no adapter.removeAndRecycleExistingViewsIf set to true, RecyclerView will recycle all existing Views. If adapters have stable ids and/or you want to animate the disappearing views, you may prefer to set this to false.
SEE ALSO
  • setAdapter(Adapter)

 

仍然会调用setAdapter,但是如果你的adapter和原来的viewholder一样,他将不会清空RecycledViewPool(回收池),性能表现要比直接setAdapter好。

public final void�0�2notifyItemChanged�0�2(int position)

Notify any registered observers that the item at�0�2position�0�2has changed.

This is an item change event, not a structural change event. It indicates that any reflection of the data at�0�2position�0�2is out of date and should be updated. The item at�0�2position�0�2retains the same identity.

PARAMETERS
positionPosition of the item that has changed
SEE ALSO
  • notifyItemRangeChanged(int, int)

根据position仅仅更新你想要更新的那一项,如果你要实现一个下载列表的话,是不是比每动一下进度就notify整个列表方便的多呢?

4、PinnedHeader(粘性头部)

在RecyclerView中,google给我们一种更友好的方式来实现这个东西—ItemDecoration:

An ItemDecoration allows the application to add a special drawing and layout offset to specific item views from the adapter’s data set. This can be useful for drawing dividers between items, highlights, visual grouping boundaries and more.

All ItemDecorations are drawn in the order they were added, before the item views (in�0�2onDraw()�0�2and after the items (in�0�2onDrawOver(Canvas, RecyclerView, RecyclerView.State).

可以看到,ItemDecoration就是类似于一个装饰物的存在,因此,一些自定义的分割线也可以通过它实现(亦或者写在你的ViewHolder的布局中也是可以的)。

可引用此项目:

https://github.com/eowise/recyclerview-stickyheaders


1 0
原创粉丝点击