将DataBinding整合到Activity/Fragment的一种极简方式

自从Google推出DataBinding/ViewBinding后,获取视图控件变得简单、高效且安全。而Activity中原本

public class TestActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        //...
    }
}

这样的代码也变成了像下面这样

public class TestActivity extends AppCompatActivity {
    ActivityTestBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = ActivityTestBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        //...
    }
}

public class TestActivity extends AppCompatActivity {
    ActivityTestBinding binding;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_test);
        //...
    }
}

每次都要写一遍inflatesetContentView调用并设置一个binding字段也听麻烦的,不如原来setContentView(R.layout.activity_test)这样来的简单。因此就考虑新建一个BaseActivity来简化这个过程。

1. 最初的方案

一开始的方案如下:

public abstract class BaseActivity<B extends ViewDataBinding> extends AppCompatActivity {
    private B binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, getLayoutId());
        binding.setLifecycleOwner(this);
        initView(binding);
    }

    protected abstract void initView(B binding);

    protected abstract int getLayoutId();

    protected B getBinding() {
        return binding;
    }
}

这样新建一个Activity的代码就变成了

public class TestActivity extends BaseActivity<ActivityTestBinding> {
    @Override
    protected void initView(ActivityTestBinding binding) {

    }

    @Override
    protected int getLayoutId(){
        return R.layout.activity_test;
    }
}

这样只需要在继承BaseActivity在泛型上写上数据绑定的类型,并在自动生成的getLayoutId方法中返回对应布局id就可以了。

但是在用了一段时间这种方法后觉得每次写id也挺麻烦,有时候引用错了id启动就直接崩溃报错。因此考虑下,就想出接下来要讲述的更简单的方法。

2. 利用反射来实现更简单的整合

最终的实现效果是下面这样,只需要在泛型中编写引用的Binding类型就可以,并且在这个Activity的作用域中,可以通过getBinding方法得到对应的应用

public class TestActivity extends BaseActivity<ActivityTestBinding> {
    @Override
    protected void initView(ActivityTestBinding binding) {
        test();
    }

    private void test(){
        getBinding().button.setOnClickListener(v -> {
            //...
        });
    }
}

首先我们需要一个反射工具,用于获得Activity泛型参数中ViewBinding/ViewDataBinding的子类的Class:

public class ReflectUtils {
    public static <T> Class<? extends T> findParameterizedClass(Class<?> aClass, Class<T> targetClass) {
        return findParameterizedClass(aClass.getGenericSuperclass(), targetClass);
    }

    public static <T> Class<? extends T> findParameterizedClass(Type type, Class<T> targetClass) {
        if (type instanceof ParameterizedType) {
            Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
            for (Type typeArgument : typeArguments) {
                if (typeArgument instanceof Class) {
                    if (targetClass.isAssignableFrom((Class<?>) typeArgument)) {
                        return (Class<? extends T>) typeArgument;
                    }
                } else {
                    throw new IllegalStateException("typeArgument is not a Class");
                }
            }
            Type rawType = ((ParameterizedType) type).getRawType();
            if (rawType.equals(Object.class)) {
                return null;
            } else {
                return findParameterizedClass(rawType, targetClass);
            }
        } else if (type instanceof Class) {
            if (type.equals(Object.class)) {
                return null;
            } else {
                Type superclass = ((Class<?>) type).getGenericSuperclass();
                return findParameterizedClass(superclass, targetClass);
            }
        } else {
            throw new IllegalStateException("type is not ParameterizedType or Class!");
        }
    }
}

然后,我们的BaseActivity就改写为下面这样

public abstract class BaseActivity<B extends ViewDataBinding> extends AppCompatActivity {
    private B binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        try {
            Class parameterizedClass = ReflectUtils.findParameterizedClass(getClass().getGenericSuperclass(), ViewBinding.class);
            if (parameterizedClass == null) {
                parameterizedClass = ReflectUtils.findParameterizedClass(getClass().getGenericSuperclass(), ViewDataBinding.class);
            }
            if (parameterizedClass == null) {
                throw new IllegalStateException("not find parameterized type extends ViewBinding or ViewDataBinding");
            } else {
                Method inflateMethod = parameterizedClass.getMethod("inflate", LayoutInflater.class);
                binding = (B) inflateMethod.invoke(null, getLayoutInflater());
            }
        } catch (Exception e) {
            throw new RuntimeException("Something wrong when create Binding:", e);
        }
        if (binding == null) {
            throw new RuntimeException("binding is null!");
        } else {
            setContentView(binding.getRoot());
            binding.setLifecycleOwner(this);
            initView(binding);
        }
    }

    protected abstract void initView(B binding);

    protected B getBinding() {
        return binding;
    }
}

3. 将这种方式推广到Fragment

我们还可以用这种方式来实现FragmentDataBinding/ViewBinding的整合:

public abstract class BaseFragment<B extends ViewDataBinding> extends Fragment {
    private B binding;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        try {
            Class parameterizedClass = ReflectUtils.findParameterizedClass(getClass().getGenericSuperclass(), ViewBinding.class);
            if (parameterizedClass == null) {
                parameterizedClass = ReflectUtils.findParameterizedClass(getClass().getGenericSuperclass(), ViewDataBinding.class);
            }
            if (parameterizedClass == null) {
                throw new IllegalStateException("not find parameterized type extends ViewBinding or ViewDataBinding");
            } else {
                Method inflateMethod = parameterizedClass.getMethod("inflate", LayoutInflater.class, ViewGroup.class, boolean.class);
                binding = (B) inflateMethod.invoke(null, getLayoutInflater(), container, false);
            }
        } catch (Exception e) {
            throw new RuntimeException("Something wrong when create Binding:", e);
        }
        if (binding != null) {
            return binding.getRoot();
        } else {
            throw new RuntimeException("binding is null!");
        }
    }

    public B getBinding() {
        return binding;
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是一个单的示例: 首先,在build.gradle文件中添加以下依赖项: ```groovy // ViewModel and LiveData implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' // Data Binding implementation 'androidx.databinding:databinding-runtime:4.0.1' ``` 接下来,创建一个名为MainActivityActivity,并在其布局文件中添加两个Fragment的占位符: activity_main.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <FrameLayout android:id="@+id/container" android:layout_width="match_parent" android:layout_height="match_parent"/> </layout> ``` MainActivity.java: ```java public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); // 加载第一个Fragment getSupportFragmentManager().beginTransaction() .replace(R.id.container, new FirstFragment()) .commit(); } } ``` 接下来,创建一个名为FirstFragmentFragment,并在其布局文件中使用DataBinding绑定数据: first_fragment.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.text}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Next" android:onClick="@{viewModel::onNextClicked}" /> </LinearLayout> </layout> ``` FirstFragment.java: ```java public class FirstFragment extends Fragment { private FirstViewModel viewModel; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 使用DataBinding绑定布局文件 FirstFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.first_fragment, container, false); // 创建ViewModel实例 viewModel = ViewModelProviders.of(this).get(FirstViewModel.class); // 将ViewModel与布局文件中的变量绑定 binding.setViewModel(viewModel); // 设置LifecycleOwner,以便LiveData知道何时更新UI binding.setLifecycleOwner(this); return binding.getRoot(); } } ``` 下面是FirstViewModel.java: ```java public class FirstViewModel extends ViewModel { private MutableLiveData<String> textLiveData = new MutableLiveData<>(); public FirstViewModel() { // 初始化LiveData的默认值 textLiveData.setValue("Hello, World!"); } public LiveData<String> getText() { return textLiveData; } public void onNextClicked() { // 更新LiveData的值 textLiveData.setValue("Next Clicked!"); } } ``` 最后,创建另一个名为SecondFragmentFragment,并在MainActivity中添加一个方法来加载它: second_fragment.xml: ```xml <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{viewModel.text}" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Previous" android:onClick="@{viewModel::onPreviousClicked}" /> </LinearLayout> </layout> ``` SecondFragment.java: ```java public class SecondFragment extends Fragment { private SecondViewModel viewModel; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { // 使用DataBinding绑定布局文件 SecondFragmentBinding binding = DataBindingUtil.inflate(inflater, R.layout.second_fragment, container, false); // 创建ViewModel实例 viewModel = ViewModelProviders.of(this).get(SecondViewModel.class); // 将ViewModel与布局文件中的变量绑定 binding.setViewModel(viewModel); // 设置LifecycleOwner,以便LiveData知道何时更新UI binding.setLifecycleOwner(this); return binding.getRoot(); } } ``` 下面是SecondViewModel.java: ```java public class SecondViewModel extends ViewModel { private MutableLiveData<String> textLiveData = new MutableLiveData<>(); public SecondViewModel() { // 初始化LiveData的默认值 textLiveData.setValue("Goodbye, World!"); } public LiveData<String> getText() { return textLiveData; } public void onPreviousClicked() { // 更新LiveData的值 textLiveData.setValue("Previous Clicked!"); } } ``` 最后,在MainActivity中添加一个方法来加载SecondFragment: ```java private void loadSecondFragment() { getSupportFragmentManager().beginTransaction() .replace(R.id.container, new SecondFragment()) .commit(); } ``` 现在,你的MVVM Android项目就完成了!你可以使用LiveData和ViewModel来管理数据,并使用DataBinding将数据绑定到UI上。Lifecycle组件可确保UI在活动和片段之间正确地进行管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值