自从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);
//...
}
}
每次都要写一遍inflate
或setContentView
调用并设置一个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
我们还可以用这种方法来完成Fragment
和DataBinding
/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;
}
}