前言
ViewBinding是一款新的视图绑定框架, 是findViewById的替代方案。相比于DataBinding对Xml的注入式绑定,ViewBinding显的更为高效与简洁。
ViewBinding简介
当你在build.grade中添加如下代码即可开启ViewBinding机制。
buildFeatures {
viewBinding = true
}
ViewBinding将根据你所有的XML布局文件生成对应的ViewBinding派生类。如下是我们的MainActivity。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.id.layout_main);
}
}
MainActivity的布局layout_main生成的ViewBinding类会自动命名为ActivityMainBinding,而我们通过ActivityMainBinding的静态方法-inflate(),并给该方法传递一个LayoutInflater就可获得ActivityMainBinding对象,并通过getRoot()获得布局。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
}
}
在XML中我们有一个TextView,id为tv_show,而ViewBinding自动将它绑定给了tvShow,我们像下面这样访问它。
binding.tvShow.setText("Android");
对ViewBinding“抽象”化
每一个Activity将对应一个ViewBinding对象,如果我们像上面那样每个Activity做这样的操作会显的很麻烦,我们直接建一个抽象类来完成模板复用。
public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
public T binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Type sup = getClass().getGenericSuperclass();
Class<?> cls = (Class<?>) ((ParameterizedType) sup).getActualTypeArguments()[0];
try {
Method mInflate = cls.getDeclaredMethod("inflate", LayoutInflater.class);
binding = (T) mInflate.invoke(null, getLayoutInflater());
setContentView(binding.getRoot());
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
Toast.makeText(this, e.toString() + "启动遇到了错误!", Toast.LENGTH_SHORT).show();
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
}
}
这里我们首先使用反射来完成ViewBinding的“抽象”,使用时添加对应的泛型即可。
public class ChatActivity extends BaseActivity<ActivityChatBinding>{}
反射虽然好用,但是它会有一定的耗时,大概在500ms以内。追求的页面的响应速度,我提供如下的解决方式。
public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
public T binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = getViewBinding();
setContentView(binding.getRoot());
}
protected abstract T getViewBinding();
}
使用时需要添加对应的泛型,并实现抽象方法getViewBinding();
public class ChatActivity extends BaseActivity<ActivityChatBinding> {
@Override
protected ActivityChatBinding getViewBinding() {
return ActivityChatBinding.inflate(getLayoutInflater());
}
}
较完善的Activity基类
public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
public T binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = getViewBinding();
setContentView(binding.getRoot());
if (!onFullscreen() && !onImmersion())
StatusBarUtil.setStatusLightTheme(this, true);//设置状态栏为白底黑字样式
if (adaptVirtualNavigation()) {
//适配虚拟导航按键
if (VirtualNavigationUtil.checkDeviceHasNavigationBar(this)) {
VirtualNavigationUtil.assistActivity(findViewById(android.R.id.content));
}
}
//当然Butterknife已经被Google搞废了,但暂时还能用
//ButterKnife.bind(this);
initData();
getParentIntent();
initView();
initListener();
initBroadcast();
}
@Override
protected void onStart() {
super.onStart();
initAnimation();
}
@Override
protected void onResume() {
super.onResume();
startAnimation();
}
protected abstract T getViewBinding();
protected abstract void initView();
protected void initAnimation() {}
protected void startAnimation() {}
/**
* 开启全屏
*/
protected boolean onFullscreen() {
return false;
}
/**
* 开启沉浸式
*/
protected boolean onImmersion() {
return false;
}
/**
* 适配虚拟导航键
*/
protected boolean adaptVirtualNavigation() {
return false;
}
protected void initListener() {}
protected void getParentIntent() {}
protected void initData() {}
protected void initBroadcast() {}
protected InputMethodManager getInputMethodManager() {
return DisplayUtil.getInputMethodManager(this);
}
/**
* 重载startActivity()
*/
protected void startActivity(Context context, Class<?> cls) {
startActivity(context, cls, null, null, null);
}
protected void startActivity(Context context, Class<?> cls, String action) {
startActivity(context, cls, action, null, null);
}
protected void startActivity(Context context, Class<?> cls, String bundleKey, Bundle bundle) {
startActivity(context, cls, null, bundleKey, bundle);
}
protected void startActivity(Context context, Class<?> cls, String action, String bundleKey, Bundle bundle) {
Intent intent = new Intent(context, cls);
if (action != null)
intent.setAction(action);
if (bundle != null)
intent.putExtra(bundleKey, bundle);
startActivity(intent);
}
/**
* 开启新activity并结束本身
*/
protected void finishStartActivity(Context context, Class<?> cls) {
startActivity(context, cls, null, null, null);
finish();
}
protected void finishStartActivity(Context context, Class<?> cls, String action) {
startActivity(context, cls, action, null, null);
finish();
}
protected void finishStartActivity(Context context, Class<?> cls, String bundleKey, Bundle bundle) {
startActivity(context, cls, null, bundleKey, bundle);
finish();
}
}
模板中我提供了沉浸式导航栏与虚拟按键的适配。沉浸式的开启只需要在需要的Activity中重写onImmersion()方法即可。
public class MainActivity extends BaseActivity<ActivityMainBinding> {
@Override
protected ActivityMainBinding getViewBinding() {
return ActivityMainBinding.inflate(getLayoutInflater());
}
@Override
protected boolean onImmersion() {
return StatusBarUtil.setImmersion(this);
}
@Override
protected void initView() {}
}
这里我把用到的一些工具类包括StatusBarUtil和VirtualNavigationUtil都放在gitee上了,需要的自提吧。
链接: wrmzd