侧边栏壁纸
博主头像
零点诗人的博客博主等级

金猴奋起千钧棒,玉宇澄清万里埃。

  • 累计撰写 8 篇文章
  • 累计创建 18 个标签
  • 累计收到 4 条评论

目 录CONTENT

文章目录

结合ViewBinding封装RecyclerView.Adapter

零点诗人
2024-07-22 / 1 评论 / 0 点赞 / 43 阅读 / 13275 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2024-07-22,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

一、介绍ViewBinding

介绍

ViewBinding 是 Android 开发中一种用于绑定视图的方法,它允许开发者更安全、更简洁地访问布局中的视图。在传统的开发模式中,我们通常使用 findViewById 来获取布局中的视图,但这种方式容易出错,且代码冗长。ViewBinding 通过生成绑定类,提供了一种更优雅的方式来访问视图。

与DataBinding对比

DataBinding:

  • DataBinding 是一个 Android 库,它允许开发者将布局中的 UI 组件绑定到应用的数据源。
  • 它通过 XML 布局文件中的绑定表达式来实现数据和视图之间的自动同步。
  • DataBinding 可以减少在 Activity 或 Fragment 中设置视图和更新 UI 的样板代码。
  • 它支持双向数据绑定,即 UI 的变化可以自动更新到数据源,反之亦然。
  • 使用 DataBinding 需要在 build.gradle 文件中添加相应的依赖,并在布局文件中使用特定的语法。

ViewBinding:

  • ViewBinding 是 Android Jetpack 的一部分,用于在编译时生成绑定类,从而以编程方式安全地访问视图。
  • 它提供了一种更简单和更安全的方式来访问布局中的视图,而不需要使用 findViewById
  • ViewBinding 不涉及数据绑定,它只关注视图的访问。
  • 使用 ViewBinding 同样需要在 build.gradle 文件中添加依赖,并且在编译时自动生成绑定类。
  • ViewBinding 通常与 LiveData 和 ViewModel 结合使用,以实现响应式编程。

如何启用

在模块的 build.gradle文件中添加以下代码,同步后即可启用。

android {
    // ...
    viewBinding {
        enabled = true
    }
}

二、Binding类分析

布局文件展示

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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="wrap_content"
    android:layout_marginTop="20dp">

    <TextView
        android:id="@+id/textView9"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="10dp"
        android:text="TextView"
        android:textColor="@color/black"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView10"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginEnd="10dp"
        android:text="TextView"
        android:textColor="@color/text_gray"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

ItemProductdetailsInfoBinding 分析

ItemProductdetailsInfoBinding 是由 View Binding 编译器自动生成的绑定类,它对应于 item_productdetails_info.xml 布局文件。以下是该类中的关键方法及其作用:

  • getRoot(): 返回布局的根视图,通常是一个 ConstraintLayout 或其他布局容器。
  • inflate(): 静态方法,用于将布局文件实例化为视图。它接受 LayoutInflater 作为参数,并且可以指定父视图和是否附加到父视图。
  • bind(): 静态方法,用于绑定视图到 ItemProductdetailsInfoBinding 类的实例。这个方法内部通过 ViewBindings.findChildViewById 来查找布局中的子视图,并将其赋值给绑定类的相应字段。

二、封装思路

封装 RecyclerView 的 Adapter 时,我们希望简化创建流程,减少冗余代码。使用 ViewBinding 可以让我们更方便地访问布局中的视图,而封装 Adapter 则可以让我们将数据和视图的绑定逻辑集中管理。

封装步骤

  1. 定义泛型: 定义两个泛型参数,T 表示数据类型,VB 表示对应的 ViewBinding 类型。
    public abstract class BindingAdapter<T, VB extends ViewBinding> extends RecyclerView.Adapter<BindingAdapter.BindingViewHolder<VB>>
    
  2. 存储数据: 在 Adapter 中维护一个数据列表 mData
        private List<T> mData;
    
        public BindingAdapter(List<T> mData) {
            this.mData = mData;
        }
    
        @Override
        public int getItemCount() {
            return mData.size();
        }
    
    
  3. 创建 ViewHolder: 重写 onCreateViewHolder 方法,使用 ViewBinding 创建 ViewHolder。通过抽象方法 onCreateViewBinding实现与具体的布局文件绑定。
        @NonNull
        @Override
        public BindingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            return new BindingViewHolder(onCreateViewBinding(LayoutInflater.from(parent.getContext()), parent));
        }
    
  4. 绑定数据: 重写 onBindViewHolder 方法,将数据绑定到 ViewHolder 的视图上。由于必须使用ViewBinding具体类的 inflate方法,所以设置一个抽象方法onConvert,具体在使用时实现。
        @Override
        public void onBindViewHolder(@NonNull BindingAdapter.BindingViewHolder<VB> holder, int position) {
            onConvert(holder.getBinding(), mData.get(position));
        }
        protected abstract void onConvert(VB binding, T itemData);
    

三、具体应用实例

假设我们有一个商品详情列表,每个列表项需要显示商品名称和描述。我们可以按照以下步骤实现:

1. 定义数据模型


import android.annotation.SuppressLint;

import com.google.gson.annotations.SerializedName;

import java.util.List;

public class GoodItem {

    @SerializedName("home_good_item")
    private List<HomeGoodItemDTO> homeGoodItem;

    public List<HomeGoodItemDTO> getHomeGoodItem() {
        return homeGoodItem;
    }

    public void setHomeGoodItem(List<HomeGoodItemDTO> homeGoodItem) {
        this.homeGoodItem = homeGoodItem;
    }

    public static class HomeGoodItemDTO {
        @SerializedName("game_icon")
        private String gameIcon;
        @SerializedName("game_name")
        private String gameName;
        @SerializedName("good_title")
        private String goodTitle;
        @SerializedName("good_price")
        private double goodPrice;
        @SerializedName("purchased_number")
        private String purchasedNumber;

        public String getGameIcon() {
            return gameIcon;
        }

        public void setGameIcon(String gameIcon) {
            this.gameIcon = gameIcon;
        }

        public String getGameName() {
            return gameName;
        }

        public void setGameName(String gameName) {
            this.gameName = gameName;
        }

        public String getGoodTitle() {
            return goodTitle;
        }

        public void setGoodTitle(String goodTitle) {
            this.goodTitle = goodTitle;
        }

        @SuppressLint("DefaultLocale")
        public String getGoodPrice() {
            return String.format("¥%.2f",goodPrice);
        }

        public void setGoodPrice(double goodPrice) {
            this.goodPrice = goodPrice;
        }

        public String getPurchasedNumber() {
            return purchasedNumber+"人已购买";
        }

        public void setPurchasedNumber(String purchasedNumber) {
            this.purchasedNumber = purchasedNumber;
        }
    }
}

2. 创建 Adapter

继承 BindingAdapter 并实现抽象方法:

    private class GoodItemAdapter extends BindingAdapter<GoodItem.HomeGoodItemDTO, ItemLayoutHomeGoodsBinding> {

        public GoodItemAdapter(List<GoodItem.HomeGoodItemDTO> mData) {
            super(mData);
        }

        @Override
        protected ItemLayoutHomeGoodsBinding onCreateViewBinding(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
            return ItemLayoutHomeGoodsBinding.inflate(inflater, parent, false);
        }

        @Override
        protected void onConvert(ItemLayoutHomeGoodsBinding binding, GoodItem.HomeGoodItemDTO itemData) {
            Glide.with(getContext())
                    .load(itemData.getGameIcon())
                    .into(binding.gameImage);
            binding.itemTitle.setText(itemData.getGoodTitle());
            binding.price.setText(itemData.getGoodPrice());
            binding.purchasedNumber.setText(itemData.getPurchasedNumber());
            binding.gameName.setText(itemData.getGameName());
        }
    }

3. 使用 Adapter

在 Activity 或 Fragment 中设置 RecyclerView 的 Adapter:

这里的数据通过网络请求获取,通过方法response.body().getHomeGoodItem()返回数据模型类的List。

        homeGoodItemEnqueue.enqueue(new Callback<>() {
            @SuppressLint("NotifyDataSetChanged")
            @Override
            public void onResponse(Call<GoodItem> call, Response<GoodItem> response) {
                List<GoodItem.HomeGoodItemDTO> homeGoodItem = response.body().getHomeGoodItem();
                RecyclerView goodsRecyclerView = mBinding.goodsRecyclerView;
                GoodItemAdapter goodItemAdapter = new GoodItemAdapter(homeGoodItem);
                goodsRecyclerView.setAdapter(goodItemAdapter);
                goodsRecyclerView.setLayoutManager(new GridLayoutManager(getContext(), 2));
                goodItemAdapter.notifyDataSetChanged();
            }

            @Override
            public void onFailure(Call<GoodItem> call, Throwable t) {
                NetworkUtils.netErr(view);
            }
        });

通过这种方式,我们不仅简化了 Adapter 的创建过程,还利用 ViewBinding 减少了对视图的直接操作,提高了代码的可读性和可维护性。

四、封装类整体代码

package com.hnucm.c202201020245.Utils;

import android.view.LayoutInflater;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import androidx.viewbinding.ViewBinding;

import java.util.List;

/**
 * 使用viewBinding封装好的RecyclerView.Adaptor,简化了创建流程
 *
 * @author lingdianshiren
 * @param <T> 数据类型
 * @param <VB> 布局类型
 */
public abstract class BindingAdapter<T, VB extends ViewBinding> extends RecyclerView.Adapter<BindingAdapter.BindingViewHolder<VB>> {
    private List<T> mData;

    public BindingAdapter(List<T> mData) {
        this.mData = mData;
    }

    @NonNull
    @Override
    public BindingViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new BindingViewHolder(onCreateViewBinding(LayoutInflater.from(parent.getContext()), parent));
    }

    @Override
    public void onBindViewHolder(@NonNull BindingAdapter.BindingViewHolder<VB> holder, int position) {
        onConvert(holder.getBinding(), mData.get(position));
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }


    /**
     * 创建itemView方法
     * @param inflater
     * @param parent
     * @return VB.infalte(inflater,parent,false)
     */
    protected abstract VB onCreateViewBinding(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent);

    /**
     * 布局与数据具体绑定关系以及监听器等
     * @param binding
     * @param itemData
     */
    protected abstract void onConvert(VB binding, T itemData);

    public static class BindingViewHolder<T extends ViewBinding> extends RecyclerView.ViewHolder {

        private final T mBinding;

        public BindingViewHolder(@NonNull T binding) {
            super(binding.getRoot());
            mBinding = binding;
        }

        public T getBinding() {
            return mBinding;
        }
    }

}

0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区