zoukankan      html  css  js  c++  java
  • 第三十九篇-RecyclerView的使用

    RecyclerView介绍

    RecyclerView的出现可以替代ListView,并且比ListView更高级且更具灵活性。如果有数据集合,其中的元素将因用户操作或网络事件而在运行时发生改变,请使用RecyclerVIew。

    在ListVIew中,改变列表某一个item数据,然后刷新列表,会回到最顶部,而RecyclerView可以保持原来滑动的位置不变。

    RecyclerView实现

    要实现一个RecyclerView,会引用到其它模块,其中1、 2是必须的。剩下的3、 4、 5三项,则是起到美化装饰的效果。

    1. 控制item的排列方式,需用到布局管理器LayoutManager。

    2. 创建一个适配器,需用到RecyclerView.Adapter。

    3. 控制item间的间隔,需用到RecyclerView.ItemDecoration。

    4. 控制item增删的动画,需用到RecyclerView.ItemAnimator。

    5. CardView扩展FrameLayout类,可以显示卡片内的信息,这些信息在整个平台中拥有一致的呈现方式。CardView小部件可拥有阴影和圆角。

    注:如果要使用RecyclerView小部件,必须要指定一个Adapter和一个LayoutManager。

    RecyclerView简单实例

    先感受一下怎么使用RecyclerView。

    第一步:新建一个empty Activity。(不赘述,不知道的可以看https://www.cnblogs.com/smart-zihan/p/9813940.html)

    第二步:在activity_main.xml中添加一个RecyclerView。

    刚开始在右边会有一个下载的符号,点击它下载就好,然后将其拖动到

    之后gradle sync,其实这个步骤就是在gradle文件中添加了RecyclerView包。

    implementation 'com.android.support:recyclerview-v7:28.0.0'
    

     附上activity_main.xml代码如下:

    <?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"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recycleview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_centerVertical="true"
            android:layout_centerHorizontal="true"/>
    
    </LinearLayout>
    

     第三步:在MainActivity.java中使用它。

            //通过findViewById拿到RecycleView实例
            mRecycleView = findViewById(R.id.recycleview);
            //设置RecyclerView管理器
            mRecycleView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
    

     第四步:新建Adapter类,item.xml并在MainActivity中引入使用它。

    Adapter类:

    package com.example.aimee.callbacktest;
    
    import android.content.Context;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder>{
        private static final String TAG = "MyAdapter";//可以用来debug
        private String[] mTitles;
        private final Context mContext;
        private final LayoutInflater mLayoutInflater;
    
        public static class ViewHolder extends RecyclerView.ViewHolder{
            TextView mTextView;
            public ViewHolder(View v){
                super(v);
                mTextView = (TextView) v.findViewById(R.id.item_tx);
                v.setOnClickListener(new View.OnClickListener() {//当点击item时,会产生什么事件
                    @Override
                    public void onClick(View v) {
                        Log.d(TAG,"onClick-->position = " + getAdapterPosition());
                    }
                });
            }
        }
    
        public MyAdapter(Context context) {
            mTitles = context.getResources().getStringArray(R.array.titles);//items
            mContext = context;
            mLayoutInflater = LayoutInflater.from(context);
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            holder.mTextView.setText(mTitles[position]);
    
        }
    
        @Override
        public int getItemCount() {
            return mTitles == null?0:mTitles.length;
        }
    
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            //LayoutInflater.from指定写法
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_normal, parent, false);
            return new ViewHolder(v);
        }
    }
    

    item.xml

    <?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:layout_margin="8dp"
        android:orientation="vertical">
    
        <TextView
            android:id="@+id/item_tx"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp"
            android:layout_gravity="center_horizontal"
            android:text="item"/>
    
    </LinearLayout>
    

    MainActivity.java引用Adapter类

            //初始化适配器
            myAdapter = new MyAdapter(this);
            //设置适配器
            mRecycleView.setAdapter(myAdapter);
    

     最后,附上完整MainActivity.java代码:

    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    
    import android.support.v7.widget.DefaultItemAnimator;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.Menu;
    import android.view.MenuItem;
    
    import java.util.ArrayList;
    import java.util.LinkedList;
    import java.util.List;
    
    public class MainActivity extends AppCompatActivity {
    
        public String TAG = "RECYCLE";
        private RecyclerView mRecycleView;
        private MyAdapter myAdapter;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            //通过findViewById拿到RecycleView实例
            mRecycleView = findViewById(R.id.recycleview);
            //设置RecyclerView管理器
            mRecycleView.setLayoutManager(new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false));
            //初始化适配器
            myAdapter = new MyAdapter(this);
            //设置适配器
            mRecycleView.setAdapter(myAdapter);
        }
    }

     效果图:

    围绕这个例子,介绍一下其它的模块:

    一、LayoutManager

    布局管理器,来控制Item的排列方式。

    RecyclerView提供的布局管理器:

    • LinearLayoutManager:以垂直或水平滚动列表方式显示项目。例子中以引用。
    • GridLayoutManager:在网络中显示项目。用法:mRecycleView.setLayoutManager(new GridLayoutManager(getBaseContext(),3));
    • StaggeredGridLayoutManager:在分散对齐网络中显示项目。用法:mRecycleView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));

     二、Adapter

    myAdapter = new MyAdapter(this);
    

     数据适配器与BaseAdapter比较发生了相当大的变化,主要有三个方法:

    1. getItemCount():获取总的条目数
    2. onCreateViewHolder():创建ViewHolder
    3. onBindVIewHolder():将数据绑定至ViewHolder

    创建ViewHolder必须继承RecyclerView.ViewHolder,RecyclerView.ViewHolder构造时必须传入一个View,即item布局。在RecyclerView中,把ViewHolder类作为缓存的单位,假设屏幕显示10个条目,则会创建10个ViewHolder缓存起来,每次复用的是ViewHolder。方法是onCreateViewHolder。

    瀑布式布局

    在Adapter的onBindViewHolder()为item设置个随机的高度,下面给出Adapter中添加的代码:

        public MyAdapter(Context context){
            mTitles = context.getResources().getStringArray(R.array.item_list);
            mLayoutInflater = LayoutInflater.from(context);
            mcontext = context;
            mHeights = new ArrayList<Integer>();
    
            for (int i=0;i<mTitles.length;i++){
                mHeights.add((int) (100 + Math.random() * 300));
            }
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder,int position){
            ViewGroup.LayoutParams lp = holder.mTextView.getLayoutParams();
            lp.height = mHeights.get(position);
    
            holder.mTextView.setLayoutParams(lp);
            holder.mTextView.setText(mTitles[position]);
        }
    

    效果图:

    这个是在string.xml中定义的item,如果我们要直接在MainActivity中定义列表怎么办呢?可用如下Adapter和在MainActivity中添加列表的方式.

    WaterpallStaggeredAdapter:
    package com.example.aimee.callbacktest;
    
    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;
    
    import java.util.ArrayList;
    import java.util.List;
    
    public class WaterpallStaggeredAdapter extends RecyclerView.Adapter<WaterpallStaggeredAdapter.MyViewHolder> {
    
        private List<String> mDatas;
        private LayoutInflater mInflater;
        private List<Integer> mHeights;
    
        public interface OnItemClickListener{
            void onItemCLick(View view,int position);
            void onItemLongClick(View view,int position);
        }
    
        private OnItemClickListener mOnItemClickListener;
    
        public void setmOnItemClickListener(OnItemClickListener mOnItemClickListener){
            this.mOnItemClickListener = mOnItemClickListener;
        }
    
        public WaterpallStaggeredAdapter(Context context,List<String> datas){
            mInflater = LayoutInflater.from(context);
            mDatas = datas;
    
            mHeights = new ArrayList<Integer>();
            for (int i=0;i<mDatas.size();i++){
                mHeights.add((int) (100 + Math.random() * 300));
            }
        }
    
        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parrent, int viewType) {
            MyViewHolder holder = new MyViewHolder(mInflater.inflate(R.layout.item_normal,parrent,false));
            return holder;
        }
    
        @Override
        public void onBindViewHolder(final MyViewHolder holder, final int position) {
            ViewGroup.LayoutParams lp = holder.tv.getLayoutParams();
            lp.height = mHeights.get(position);
    
            holder.tv.setLayoutParams(lp);
            holder.tv.setText(mDatas.get(position));
    
            //如果设置了回调,则设置点击事件
            if (mOnItemClickListener != null){
                holder.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        int pos = holder.getLayoutPosition();
                        mOnItemClickListener.onItemCLick(holder.itemView,pos);
                    }
                });
    
                holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
                    @Override
                    public boolean onLongClick(View v) {
                        int pos = holder.getLayoutPosition();
                        mOnItemClickListener.onItemLongClick(holder.itemView,pos);
                        removeData(pos);
                        return false;
                    }
                });
            }
    
        }
    
        @Override
        public int getItemCount() {
            return mDatas.size();
        }
    
        public class MyViewHolder extends RecyclerView.ViewHolder {
    
            TextView tv;
    
            public MyViewHolder(View itemView) {
                super(itemView);
                tv = (TextView) itemView.findViewById(R.id.item_tx);
            }
        }
    
        public void removeData(int position){
            mDatas.remove(position);
            notifyDataSetChanged();
        }
    
    }
    

     这时候,在MainActivity中调用时时需要传入两个参数的,一个是context,一个是列表。

            mDatas = new ArrayList<String>();
            mDatas.add("A");
            mDatas.add("B");
            mDatas.add("C");
            mDatas.add("D");
            mDatas.add("E");
            mDatas.add("F");
            mDatas.add("G");
            mDatas.add("H");
            mDatas.add("I");
            mDatas.add("J");
            mDatas.add("K");
            mDatas.add("L");
            mDatas.add("M");
            mDatas.add("N");
    

     至于那个LayoutManager有三种,可以自行尝试,会发现其中的区别的。其实上述列表如果想要简单一点,可以用一个For循环添加列表。。。

    三、ItemDecoration添加分割线

    使用方法:

    为了使效果明显,可以修改下item.xml, 去掉CardView,改为LinearLayout,为其设置背景色,改变字体大小。

    <?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:id="@+id/cv_item">
    
        <TextView
            android:id="@+id/text_item"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="10dp"
            android:background="#ff0000"
            android:textSize="28dp"
            android:layout_gravity="center_horizontal"
            android:text="TextView" />
    
    </LinearLayout>
    

    在MainActivity中加上分割线

    recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));

     效果图:

     

    ItemDecoration很好的实现了RecyclerView添加分割线(当使用LayoutManager为LinearLayoutManager时)。该实现类可以看到通过读取系统主题中的Android.R.attr.listDivider作为Item间的分割线,并且支持横向和纵向。

    该分割线是系统默认的,可以在theme.xml中找到该属性的使用情况。

    在styles.xml找使用的android:listDivider的xml--shape_divider,也可以对其进行自定义。

        <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
            <!-- Customize your theme here. -->
            <item name="colorPrimary">@color/colorPrimary</item>
            <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
            <item name="colorAccent">@color/colorAccent</item>
            <item name="android:listDivider">@drawable/ic_launcher_background</item>
        </style>
    

     下面给出DividerItemDecoration的代码:

    package com.example.aimee.callbacktest;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Rect;
    import android.graphics.drawable.Drawable;
    import android.support.annotation.NonNull;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    
    public class DividerItemDecoration extends RecyclerView.ItemDecoration {
        private static final int[] ATTRS = new int[]{
                android.R.attr.listDivider
        };
    
        public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
        public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
        private Drawable mDivider;
        private int mOrientation;
    
        public DividerItemDecoration(Context context,int mOrientation){
            final TypedArray a = context.obtainStyledAttributes(ATTRS);
            mDivider = a.getDrawable(0);
            a.recycle();
            setOrientation(mOrientation);
        }
    
        public void setOrientation(int orientation){
            if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){
                throw new IllegalArgumentException("invalid orientation");
            }
            mOrientation = orientation;
        }
    
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            if (mOrientation == VERTICAL_LIST){
                drawVertical(c,parent);
            }else{
                drawHorizontal(c,parent);
            }
        }
    
        public void drawVertical(Canvas c,RecyclerView parent){
            final int left = parent.getPaddingLeft();
            final int right = parent.getWidth() - parent.getPaddingRight();
    
            final int childCount = parent.getChildCount();
            for (int i=0;i<childCount;i++){
                final View child = parent.getChildAt(i);
                RecyclerView v = new RecyclerView(parent.getContext());
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
                final int top = child.getBottom() + params.bottomMargin;
                final int bottom = top + mDivider.getIntrinsicHeight();
                mDivider.setBounds(left,top,right,bottom);
                mDivider.draw(c);
            }
        }
    
        public void drawHorizontal(Canvas c,RecyclerView parent){
            final int top = parent.getPaddingTop();
            final int bottom = parent.getHeight() - parent.getPaddingBottom();
    
            final int childCount = parent.getChildCount();
            for (int i=0;i<childCount;i++){
                final View child = parent.getChildAt(i);
                final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)child.getLayoutParams();
                final int left = child.getRight() + params.rightMargin;
                final int right = left + mDivider.getIntrinsicHeight();
                mDivider.setBounds(left,top,right,bottom);
                mDivider.draw(c);
            }
        }
    
        @Override
        public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
            if (mOrientation == VERTICAL_LIST){
                outRect.set(0,0,0,mDivider.getIntrinsicHeight());
            }else{
                outRect.set(0,0,mDivider.getIntrinsicWidth(),0);
            }
        }
    }
    

     接下来,怎么实现删除和添加的动画效果呢?如果是list<String>那么很简单,指需要.add和.remove就好了,但是如果是从string-array中读取的数据呢,它可没有.add方法,这时可以用到数据类型的转换。

    当然我们这里不把list转换成array了,直接用list进行后面的操作。

    MyAdapter.java

    package com.example.aimee.recyclerviewtest;
    
    import android.content.Context;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.TextView;
    
    import java.lang.reflect.Array;
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
        private static final String TAG = "MyAdapter";
        private final LayoutInflater mLayoutInflater;
        private final Context mcontext;
        private String[] mTitles;
        private List<Integer> mHeights;
        private List<String> mDatas;
    
        public MyAdapter(Context context){
            mTitles = context.getResources().getStringArray(R.array.item_list);
            mDatas = new ArrayList<>(Arrays.asList(mTitles));
            mLayoutInflater = LayoutInflater.from(context);
            mcontext = context;
            mHeights = new ArrayList<Integer>();
    
            for (int i=0;i<mTitles.length;i++){
                mHeights.add((int) (100 + Math.random() * 300));
            }
        }
    
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
            return new ViewHolder(mLayoutInflater.inflate(R.layout.item_text,parent,false));
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder,int position){
            ViewGroup.LayoutParams lp = holder.mTextView.getLayoutParams();
            lp.height = mHeights.get(position);
    
            holder.mTextView.setLayoutParams(lp);
            holder.mTextView.setText(mDatas.get(position));
        }
    
        @Override
        public int getItemCount(){
            return  mDatas == null?0:mDatas.size();
        }
    
        public static class ViewHolder extends RecyclerView.ViewHolder {
    
            TextView mTextView;
    
            public ViewHolder(View itemView) {
                super(itemView);
                mTextView = itemView.findViewById(R.id.text_item);
            }
        }
    
        public void addData(int position){
            mDatas.add(position,"P");
            mHeights.add((int) (100 + Math.random() * 300));
            notifyDataSetChanged();
        }
    
        public void removeData(int position){
            mDatas.remove(position);
            notifyDataSetChanged();
        }
    
    }
    

     还得添加一个menu菜单,使其可以添加,删除操作。

    menu.xml

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">
        <item
            android:id="@+id/add"
            android:title="添加"
            app:showAsAction="always"/>
    
        <item
            android:id="@+id/remove"
            android:title="删除"
            app:showAsAction="always"/>
    
    </menu>
    

     然后在MainActivity中添加菜单函数

    package com.example.aimee.recyclerviewtest;
    
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.support.v7.widget.DefaultItemAnimator;
    import android.support.v7.widget.DividerItemDecoration;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.support.v7.widget.StaggeredGridLayoutManager;
    import android.view.Menu;
    import android.view.MenuItem;
    
    public class MainActivity extends AppCompatActivity {
        private RecyclerView recyclerView;
        private MyAdapter myAdapter;
        private int count=0;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            recyclerView = findViewById(R.id.recyclerview);
            recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));
            myAdapter = new MyAdapter(this);
            recyclerView.setAdapter(myAdapter);
            recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
            recyclerView.setItemAnimator(new DefaultItemAnimator());
        }
    
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            getMenuInflater().inflate(R.menu.menu,menu);
            return true;
        }
    
        @Override
        public boolean onOptionsItemSelected(MenuItem item) {
            int id = item.getItemId();
    
            if (id==R.id.add){
                myAdapter.addData(count);
                count = count + 2;
                return true;
            }
    
            if (id==R.id.remove){
                if (count>=2)
                    count = count - 2;
                myAdapter.removeData(count);
                return true;
            }
            return super.onOptionsItemSelected(item);
        }
    }
    

     效果图:

    四、CardView的使用

    第一个效果图就用了cardview,它可以拥有阴影个圆角。

    • 如果要使用阴影创建卡片,可使用card_view:cardElevation属性
    • 如果要在布局中设置圆角半径,可使用card_view.cardCornerRadius属性
    • 如果要在代码中设置圆角半径,可使用CardView.setRadius方法
    • 如果要设置卡片背景颜色,可使用card_view:cardBackgroundColor属性

    参考博客:

    https://blog.csdn.net/xx326664162/article/details/61199895

  • 相关阅读:
    JAVA中字符串比较equals()和equalsIgnoreCase()的区别
    JAVA字母的大小写转换
    对于java线程的理解
    JAVA实现文件导出Excel
    处理数据库中的null值问题
    POJO、JAVABean、Entity的区别
    Mybatis的choose标签使用
    redis详解
    Spring框架基础解析
    利用 BackgroundService 固定时间间隔执行某动作
  • 原文地址:https://www.cnblogs.com/smart-zihan/p/10148863.html
Copyright © 2011-2022 走看看