Android MVVM框架搭建RecyclerVIew + ViewPager2 + BaseQuickAdapter

Posted 初学者-Study

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android MVVM框架搭建RecyclerVIew + ViewPager2 + BaseQuickAdapter相关的知识,希望对你有一定的参考价值。

前言


  在日常的开发中,最常用于展示数据的形式就是列表,你会看到各种各样的列表,比如图片列表、视频列表,联系人列表,而在RecyclerView出来之前列表的开发是使用ListView,而现在绝大多数开发者都使用RecyclerVIew了,优势就不说了,都已经用了这么多年了,那么RecyclerView在MVVM中要怎么使用呢?另外它与JetPack的组件Paging之间,有什么联系呢?我们往下看。

正文

  在上一篇文章中,我讲述了怎么使用Room和MMKV去管理本地数据,本文将是不一样的介绍方式,因为会和页面打交道比较多,所以会比上一篇更有意思,起码我是这么觉得的。

一、图片列表数据

  首先我们要拿到数据才行,拿到数据才能去展示,最好是有图片的数据,我这里找了一个网络上的免费API接口,在我发布文章的时候这个API接口还是能用的。地址如下:

http://service.picasso.adesk.com/v1/vertical/vertical?limit=30&skip=180&adult=false&first=0&order=hot

我在写好天气的时候用过这个接口作为每日壁纸列表的使用。建议用浏览器测试一下找个接口,看有没有数据返回。那么同样的这个接口每天也只需要请求一次即可,后面对这个再做处理,前面先解决列表显示的问题。

我这里是有数据返回的,通过返回的数据构建一个数据实体,命名为WallPaperResponse,放在model包下,代码如下:

public class WallPaperResponse 
    private String msg;
    private ResBean res;
    private int code;

    public String getMsg() 
        return msg;
    

    public void setMsg(String msg) 
        this.msg = msg;
    

    public ResBean getRes() 
        return res;
    

    public void setRes(ResBean res) 
        this.res = res;
    

    public int getCode() 
        return code;
    

    public void setCode(int code) 
        this.code = code;
    

    public static class ResBean 
        private List<VerticalBean> vertical;

        public List<VerticalBean> getVertical() 
            return vertical;
        

        public void setVertical(List<VerticalBean> vertical) 
            this.vertical = vertical;
        

        public static class VerticalBean 
            private String preview;
            private String thumb;
            private String img;
            private int views;
            private String rule;
            private int ncos;
            private int rank;
            private String source_type;
            private String wp;
            private boolean xr;
            private boolean cr;
            private int favs;
            private double atime;
            private String id;
            private String store;
            private String desc;
            private List<String> cid;
            private List<?> tag;
            private List<?> url;

            public String getPreview() 
                return preview;
            

            public void setPreview(String preview) 
                this.preview = preview;
            

            public String getThumb() 
                return thumb;
            

            public void setThumb(String thumb) 
                this.thumb = thumb;
            

            public String getImg() 
                return img;
            

            public void setImg(String img) 
                this.img = img;
            

            public int getViews() 
                return views;
            

            public void setViews(int views) 
                this.views = views;
            

            public String getRule() 
                return rule;
            

            public void setRule(String rule) 
                this.rule = rule;
            

            public int getNcos() 
                return ncos;
            

            public void setNcos(int ncos) 
                this.ncos = ncos;
            

            public int getRank() 
                return rank;
            

            public void setRank(int rank) 
                this.rank = rank;
            

            public String getSource_type() 
                return source_type;
            

            public void setSource_type(String source_type) 
                this.source_type = source_type;
            

            public String getWp() 
                return wp;
            

            public void setWp(String wp) 
                this.wp = wp;
            

            public boolean isXr() 
                return xr;
            

            public void setXr(boolean xr) 
                this.xr = xr;
            

            public boolean isCr() 
                return cr;
            

            public void setCr(boolean cr) 
                this.cr = cr;
            

            public int getFavs() 
                return favs;
            

            public void setFavs(int favs) 
                this.favs = favs;
            

            public double getAtime() 
                return atime;
            

            public void setAtime(double atime) 
                this.atime = atime;
            

            public String getId() 
                return id;
            

            public void setId(String id) 
                this.id = id;
            

            public String getStore() 
                return store;
            

            public void setStore(String store) 
                this.store = store;
            

            public String getDesc() 
                return desc;
            

            public void setDesc(String desc) 
                this.desc = desc;
            

            public List<String> getCid() 
                return cid;
            

            public void setCid(List<String> cid) 
                this.cid = cid;
            

            public List<?> getTag() 
                return tag;
            

            public void setTag(List<?> tag) 
                this.tag = tag;
            

            public List<?> getUrl() 
                return url;
            

            public void setUrl(List<?> url) 
                this.url = url;
            
        
    



二、新增访问地址和接口

  这个API的地址和必应明显是两个地址,那么我们就需要对第二篇文章中所写的网络框架做一些修改,首先我们修改NetworkApi中的代码。

将BASE_URL的默认值改成null,并去掉final关键字,然后我们在NetworkApi中增加一个方法,代码如下:

	/**
     * 设置访问Url类型
     * @param type 0 必应 1 壁纸列表
     */
    private static void setUrlType(int type)
        switch (type) 
            case 0:
                //必应
                BASE_URL = "https://cn.bing.com";
                break;
            case 1:
                //热门壁纸
                BASE_URL = "http://service.picasso.adesk.com";
                break;
            default:break;
        
    

这里根据输入的类型使用不同的网络服务器地址,然后再修改createService方法,修改后如下:

	public static <T> T createService(Class<T> serviceClass, int type) 
        //设置Url类型
        setUrlType(type);
        return getRetrofit(serviceClass).create(serviceClass);
    

那么我们就同样需要修改代码中调用了createService方法的地方,在MainRepository中

这样就可以了,这样做的好处就在于我们既增加了访问API的可拓展性,同时易于修改,还不会对你之前的网络请求很影响。

对于之前的内容改动目前就这些了,下面需要增加新的接口了。在ApiService中增加如下接口。

	/**
     * 热门壁纸
     */
    @GET("/v1/vertical/vertical?limit=30&skip=180&adult=false&first=0&order=hot")
    Observable<WallPaperResponse> wallPaper();

三、访问接口

  接口有了,下面就是访问的事情了,现在主页面有点太空旷了,所以找个接口的数据访问依然可以在MainRepository中进行请求。打开MainRepository,在里面增加如下代码:

	/**
     * 热门壁纸数据
     */
    final MutableLiveData<WallPaperResponse> wallPaper = new MutableLiveData<>();
	
	/**
     * 获取壁纸数据
     * @return wallPaper
     */
    public LiveData<WallPaperResponse> getWallPaper() 
        NetworkApi.createService(ApiService.class,1).
                wallPaper().compose(NetworkApi.applySchedulers(new BaseObserver<WallPaperResponse>() 
            @Override
            public void onSuccess(WallPaperResponse wallPaperResponse) 
                KLog.e("WallPaper: " + new Gson().toJson(wallPaperResponse));
                wallPaper.setValue(wallPaperResponse);
            

            @Override
            public void onFailure(Throwable e) 
                KLog.e("WallPaper Error: " + e.toString());
            
        ));
        return wallPaper;
    

然后进入到MainViewModel中,在里面增加如下代码:

	public LiveData<WallPaperResponse> wallPaper;

	public void getWallPaper()  wallPaper = new MainRepository().getWallPaper(); 

现在访问接口数据这一块就搞定了,下面就是显示出来就可以了。

四、RecyclerView显示数据

  因为返回的数据比较多,因此通过RecyclerView来进行显示,作为壁纸显示可以通过更改布局管理器,把列表变成纵向两列的形式去显示,首先我们先修改activity_main.xml的布局代码,如下图所示
这里我去掉了页面的居中布局,然后增加了一个RecyclerView,添加了一个id,同事改了一下CustomImageView的scaleType=“fitXY”,这样可以让我们的壁纸完整呈现出来。

这里我需要修改一下CustomImageView类的代码:

其实就是改它所继承的父类,为什么要这么改呢?现在就来说明一下。下面我们写一个列表适配器的item布局,在layout下新建一个item_wall_paper.xml文件,里面的代码我们先不写,先去写一个样式,在themes.xml文件中(老版本的AS中是styles.xml),增加如下样式代码:

	<!-- 圆角图片 -->
    <style name="roundedImageStyle">
        <item name="cornerFamily">rounded</item>
        <item name="cornerSize">24dp</item>
    </style>

这里是设置一个圆角图片的样式代码,那么怎么去使用它呢,下面我们修改一下item_wall_paper.xml,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!--绑定数据-->
    <data>
        <variable
            name="wallPaper"
            type="com.llw.mvvm.model.WallPaperResponse.ResBean.VerticalBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <com.llw.mvvm.view.CustomImageView
            networkUrl="@wallPaper.img"
            android:layout_width="match_parent"
            android:layout_height="300dp"
            android:layout_margin="5dp"
            android:scaleType="centerCrop"
            app:shapeAppearanceOverlay="@style/roundedImageStyle" />
    </LinearLayout>
</layout>

这里依然是使用DataBinding,因为我们数据是要显示在列表上的,因此直接绑定item就可以了,然后这里我用的是networkUrl的属性,因为你如果使用了biyingUrl会添加一个前缀,而这个API不需要前缀,同时我把刚才写的样式设置了进来,这里就解释了为什么要更改继承的父类,因为之前的那个父类没有这个属性值,这个属性值可以让你的Image图片做很多的形状上的变化,相当Nice! 这样就不用再去导入其他的依赖库或者使用自定义View了,这得力于Material,关于ShapeableImageView更多的介绍可以看一下这一篇文章:Android Material UI控件之ShapeableImageView,如果你感兴趣的话。

好了回到正题,那就是我们现在布局都已经写好了,下面写一个适配器,在com.llw.mvvm包下新建一个adapter包,adapter包下新建一个WallPaperAdapter类,里面的代码如下:

public class WallPaperAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> 

    /**
     * 传递过来的数据
     */
    private final List<WallPaperResponse.ResBean.VerticalBean> verticalBeans;


    public WallPaperAdapter(List<WallPaperResponse.ResBean.VerticalBean> verticalBeans) 
        this.verticalBeans = verticalBeans;
    

    @NonNull
    @NotNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) 
        ItemWallPaperBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_wall_paper, parent, false);
        return new ViewHolderWallPaper(binding);
    

    @Override
    public void onBindViewHolder(@NonNull @NotNull RecyclerView.ViewHolder holder, int position) 
        ItemWallPaperBinding binding = ((ViewHolderWallPaper) holder).getBinding();
        binding.setWallPaper(verticalBeans.get(position));
        binding.executePendingBindings();
    

    @Override
    public int getItemCount() 
        return verticalBeans.size();
    

    private static class ViewHolderWallPaper extends RecyclerView.ViewHolder 

        private ItemWallPaperBinding binding;

        public ItemWallPaperBinding getBinding() 
            return binding;
        

        public void setBinding(ItemWallPaperBinding binding) 
            this.binding = binding;
        

        public ViewHolderWallPaper(ItemWallPaperBinding inflate) 
            super(inflate.getRoot());
            this.binding = inflate;
        
    

这就是RecyclerView.Adapter的常规使用而已,很简单,其中要注意的就是DataBinding的使用,这个很关键了,它决定了你的数据与xml绑定。下面我们回到MainActivity中,首先增加一个initView()方法,里面的代码如下:

	/**
     * 初始化
     */
    private void initView() 
        GridLayoutManager manager = new GridLayoutManager(this, 2);
        dataBinding.rv.setLayoutManager(manager以上是关于Android MVVM框架搭建RecyclerVIew + ViewPager2 + BaseQuickAdapter的主要内容,如果未能解决你的问题,请参考以下文章

Android MVVM框架搭建ViewModel + LiveData + DataBinding

Android MVVM框架搭建ViewModel + LiveData + DataBinding

Android MVVM框架搭建高德地图定位天气查询BottomSheetDialog

Android MVVM框架搭建HiltViewBindingActivity Result API

Android MVVM框架搭建HiltViewBindingActivity Result API

Android MVVM框架搭建Navigation + Fragment + BottomNavigationView