首页 > 系统 > Android > 正文

Android RecyclerView使用

2019-11-09 17:38:51
字体:
来源:转载
供稿:网友

辛苦堆砌,转载请注明出处,谢谢!

Android中的RecyclerView类似于ListView,但是它使用更加灵活,可以实现更复杂的列表结构。使用RecyclerView需要了解几个概念。

首先是布局管理器,RecyclerView使用布局管理器控制子视图之间的布局关系,有两种布局管理器:(1)LinearLayoutManager:把子视图按照垂直或者水平的方式进行排列;(2)GridLayoutManager:把子视图按照垂直或者水平的方式放置在网格中,网格不要求必须是矩阵形式的,可以是每行的列数不同表格。其次是RecyclerView.Adapter,它用来将数据集的变化反映到视图中。我们必须派生Adapter类,实现数据更新逻辑。RecyclerView.Adapter使用ViewHolder模式,我们需要派生RecyclerView. ViewHolder,这样可以防止过多调用findViewById,提高效率。

最后是RecyclerView.ItemDecoration,它用来提供子视图之间填充的内容,如果不提供,默认和ListView类似,子视图之间紧密挨在一起,我们可以派生该类,实现自己的填充。该类使用装饰器模式,我们可以为RecyclerView添加和移除各种装饰,装饰可以相互叠加。

我们示例代码的结果如图所示,分别给出了垂直,网格和水平三种布局方式:

    

水平的不太好展示,可以自己运行程序,然后左右滑动看看。下面看看我们的代码,我们首先定义了两个尺寸,在实现decoration时会用到,在dimens.xml文件中

<dimen name="decoration_margin">10dp</dimen><dimen name="decoration_line_stroke">2dp</dimen>然后看看添加空隙的decoration
package com.yjp.recyclerviewtest;import android.content.Context;import android.graphics.Rect;import android.support.v7.widget.RecyclerView;import android.view.View;public class MarginDecoration extends RecyclerView.ItemDecoration {    PRivate int mDimen;    public MarginDecoration(Context context) {        super();        mDimen = context.getResources().getDimensionPixelOffset(R.dimen.decoration_margin);    }    @Override    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {        outRect.set(mDimen, mDimen, mDimen, mDimen);    }}MarginDecoration派生自RecyclerView.ItemDecoration,在构造函数中加载了dimens.xml中定义的decoration_margin,然后重写了getItemOffsets方法,该方法返回一个矩形,用来说明两个条目之间的间隙。

上面示例图片中在网格布局中,一列两个条目之间用短横线连接,这是由另一个decoration实现的

package com.yjp.recyclerviewtest;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.support.v7.widget.RecyclerView;import android.view.View;public class LineDecoration extends RecyclerView.ItemDecoration {    private Paint mLinePaint;    private int mLineLength;    public LineDecoration(Context context) {        super();        mLineLength = context.getResources()                .getDimensionPixelOffset(R.dimen.decoration_margin);        int lineStroke = context.getResources()                .getDimensionPixelOffset(R.dimen.decoration_line_stroke);        mLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG);        mLinePaint.setColor(Color.RED);        mLinePaint.setStyle(Paint.Style.STROKE);        mLinePaint.setStrokeWidth(lineStroke);    }    @Override    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {        RecyclerView.LayoutManager layoutManager =                parent.getLayoutManager();        for (int i = 0; i < parent.getChildCount(); i++) {            View view = parent.getChildAt(i);            boolean isLeft = parent.getChildAdapterPosition(view) % 3 == 1;            if (isLeft) {                final int childRight = layoutManager.getDecoratedRight(view);                final int childTop = layoutManager.getDecoratedTop(view);                final int childBottom = layoutManager.getDecoratedBottom(view);                int y = childTop + (childBottom - childTop) / 2;                c.drawLine(childRight - mLineLength, y,                        childRight + mLineLength, y,                        mLinePaint);            }        }    }}该decoration重写了onDraw方法,通过position判断是否是两个一行的前一个条目,如果是的话,绘制短直线。

现在我们就准备好了两个Decoration,后面会将其交给RecyclerView使用。现将他们放在这里,我们再来看看Adapter实现

package com.yjp.recyclerviewtest;import android.content.Context;import android.support.v7.widget.RecyclerView;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ItemHolder> {    private LayoutInflater mLayoutInflater;    private int mItemsCount;    private OnItemClickListener mOnItemClickListener;    public interface OnItemClickListener {        void onItemClicked(ItemHolder holder, int position);    }    public ItemAdapter(Context context) {        mLayoutInflater = LayoutInflater.from(context);    }    @Override    public ItemAdapter.ItemHolder onCreateViewHolder(ViewGroup parent, int viewType) {        View itemView = mLayoutInflater.inflate(R.layout.item_layout, parent, false);        return new ItemHolder(itemView, this);    }    @Override    public void onBindViewHolder(ItemAdapter.ItemHolder holder, int position) {        holder.setContent("条目" + position);    }    @Override    public int getItemCount() {        return mItemsCount;    }    public void insertItem(int position) {        mItemsCount++;        notifyItemInserted(position);    }    public void removeItem(int position) {        mItemsCount--;        notifyItemRemoved(position);    }    public void setOnItemClickListener(OnItemClickListener listener) {        mOnItemClickListener = listener;    }    public OnItemClickListener getOnItemClickListener() {        return mOnItemClickListener;    }    public static class ItemHolder extends RecyclerView.ViewHolder implements View.OnClickListener {        private TextView mContentView;        private ItemAdapter mItemAdapter;        public ItemHolder(View itemView, ItemAdapter adapter) {            super(itemView);            mItemAdapter = adapter;            itemView.setOnClickListener(this);            mContentView = (TextView) itemView.findViewById(R.id.content);        }        public void setContent(String content) {            mContentView.setText(content);        }        public String getContent() {            return mContentView.getText().toString();        }        @Override        public void onClick(View v) {            OnItemClickListener listener = mItemAdapter.getOnItemClickListener();            if (listener != null) {                listener.onItemClicked(this, getAdapterPosition());            }        }    }}重写了三个主要的方法:

public ItemAdapter.ItemHolder onCreateViewHolder(ViewGroup parent, int viewType)

在创建ViewHolder时调用,主要创建我们自定义的ViewHolder并在其构造函数调用findViewById,拿到对应视图的引用。

public void onBindViewHolder(ItemAdapter.ItemHolder holder, int position)

在绑定数据时调用,如果使用回收的视图进行显示,不会调用上面的方法,只会调用这个方法,这样就可以减少findViewById的调用,同时可以复用资源。

public int getItemCount()

返回当前条目数。

这个示例中的Adapter主要依靠调用insertItem和removeItem来动态添加和移除条目数据,另外我们还设置了一个点击监听器,来处理点击事件,最后,可以注意看看ViewHolder,它是Adapter访问操作视图的媒介。下面是我们item的布局,很简单,只有一个TextView

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:background="@android:color/holo_blue_bright">    <TextView        android:id="@+id/content"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_gravity="center"        android:textAppearance="?android:textAppearanceLarge"        android:textStyle="bold"/></FrameLayout>现在我们就准备好了decoration和adapter,就可以使用RecyclerView了,首先是布局

<?xml version="1.0" encoding="utf-8"?><FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:id="@+id/activity_main"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context="com.yjp.recyclerviewtest.MainActivity">    <android.support.v7.widget.RecyclerView        android:id="@+id/recycler_view"        android:layout_width="match_parent"        android:layout_height="match_parent" /></FrameLayout>我们还使用了一个菜单,资源文件如下:

<?xml version="1.0" encoding="utf-8"?><menu xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:android="http://schemas.android.com/apk/res/android">    <item android:title="垂直"        android:id="@+id/vertical"        app:showAsAction="never" />    <item android:title="水平"        android:id="@+id/horizontal"        app:showAsAction="never" />    <item android:title="网格"        android:id="@+id/grid"        app:showAsAction="never" />    <item android:title="添加"        android:id="@+id/add"        app:showAsAction="never" />    <item android:title="删除"        android:id="@+id/remove"        app:showAsAction="never" /></menu>最后是我们的类文件:

package com.yjp.recyclerviewtest;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.support.v7.widget.GridLayoutManager;import android.support.v7.widget.LinearLayoutManager;import android.support.v7.widget.RecyclerView;import android.view.Menu;import android.view.MenuItem;import android.widget.Toast;public class MainActivity extends AppCompatActivity implements ItemAdapter.OnItemClickListener {    private RecyclerView mRecyclerView;    private ItemAdapter mItemAdapter;    private MarginDecoration mMarginDecoration;    private LineDecoration mLineDecoration;    private LinearLayoutManager mHorizontalLayoutManager;    private LinearLayoutManager mVerticalLayoutManager;    private GridLayoutManager mGridLayoutManager;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //装饰        mMarginDecoration = new MarginDecoration(this);        mLineDecoration = new LineDecoration(this);        //创建LayoutManager        mHorizontalLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);        mVerticalLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);        mGridLayoutManager = new GridLayoutManager(this, 2, LinearLayoutManager.VERTICAL, false);        mGridLayoutManager.setSpanSizeLookup(new CustomGridSpanSizeLookup());        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);        //设置默认布局管理器        mRecyclerView.setLayoutManager(mVerticalLayoutManager);        //设置装饰        mRecyclerView.addItemDecoration(mMarginDecoration);        //设置Adapter        mItemAdapter = new ItemAdapter(this);        mRecyclerView.setAdapter(mItemAdapter);        //设置监听器        mItemAdapter.setOnItemClickListener(this);    }    @Override    public boolean onCreateOptionsMenu(Menu menu) {        getMenuInflater().inflate(R.menu.options, menu);        return true;    }    @Override    public boolean onOptionsItemSelected(MenuItem item) {        int id = item.getItemId();        switch (id) {            case R.id.horizontal:                mRecyclerView.setLayoutManager(mHorizontalLayoutManager);                mRecyclerView.removeItemDecoration(mLineDecoration);                return true;            case R.id.vertical:                mRecyclerView.setLayoutManager(mVerticalLayoutManager);                mRecyclerView.removeItemDecoration(mLineDecoration);                return true;            case R.id.grid:                mRecyclerView.setLayoutManager(mGridLayoutManager);                mRecyclerView.addItemDecoration(mLineDecoration);                return true;            case R.id.add:                mItemAdapter.insertItem(mItemAdapter.getItemCount());                return true;            case R.id.remove:                mItemAdapter.removeItem(mItemAdapter.getItemCount() - 1);                return true;            default:                return false;        }    }    @Override    public void onItemClicked(ItemAdapter.ItemHolder holder, int position) {        Toast.makeText(this, holder.getContent() + "被点击", Toast.LENGTH_SHORT).show();    }}注释已经写的很清楚了,唯一需要特别说明的一个东西,我们前面没有准备,就是为GridLayoutManager设置的CustomGridSpanSizeLookup,代码如下:

package com.yjp.recyclerviewtest;import android.support.v7.widget.GridLayoutManager;public class CustomGridSpanSizeLookup extends GridLayoutManager.SpanSizeLookup {    @Override    public int getSpanSize(int position) {        return (position % 3 == 0 ? 2: 1);    }}它派生自GridLayoutManager.SpanSizeLookup,一个Item占用的span宽度,当position能被3整除时,一个item占用一行,也就是2个span,无法整除的,一个占用1个span,就产生了我们上面网格布局的效果。

可以看到,使用RecyclerView并不是很复杂,但是要理解各个类的作用以及相互之间的关系,在合适的类中做合适的工作,最后将它们放在一起就能产生很好的效果。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表