问题:
最近使用模块化开发一个新项目,但是Butterknife真是闹心,即使在我成功弄了R2,项目也正常运行之后还是发现很多问题。
- 经常出现的R2爆红,必须重新构建之后才可以找到文件(强迫症表示不服);
- 又偶尔出现xml文件找不到的问题,之后莫名其妙的有可以找到了。我猜想可能和这个R2有关系,真心不好用。
使用视图绑定ViewBinding的优点:简洁、编译安全、编译速度快。
原理:
Google在那个用来编译的gradle插件中增加了新功能,当某个module开启ViewBinding功能后,编译的时候就去扫描此模块下的layout文件,生成对应的binding类。那些你所熟悉的findViewById操作都是在这个自动生成的类中。
使用方法:
1.在要使用ViewBinding的 module 的gradle文件中开启ViewBinding
android {
...
viewBinding {
enabled = true
}
...
}
2.若不想为某个布局文件生成binding类,则可以使用属性viewBindingIgnore添加到布局的根视图中即可:
<androidx.constraintlayout.widget.ConstraintLayout
tools:viewBindingIgnore="true" >
</androidx.constraintlayout.widget.ConstraintLayout>
3.编写XML布局文件:activity_main.xml
<?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="match_parent"
tools:context=".MainActivity"
tools:viewBindingIgnore="true" >
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
对应在Module文件路径:
build\generated\data_binding_base_class_source_out\debug\out\包名\databinding下,生成类文件为ActivityMainBinding.java
4.在Activity中简单使用:
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding activityMainBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置布局文件
activityMainBinding = ActivityMainBinding.inflate(LayoutInflater.from(this));
setContentView(activityMainBinding.getRoot());
//设置文本
activityMainBinding.tvContent.setText("你好");
}
}
5.针对布局中使用include
编写XML布局文件:layout_comment.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_include"
android:text="你好"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
在activity_main.xml布局中引用include布局:
<?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="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<include
android:id="@+id/layout_include"
layout="@layout/layout_comment" />
</androidx.constraintlayout.widget.ConstraintLayout>
若要使用到layout_comment.xml布局中的控件,必须为include标签声明id。
Activity中使用代码:
activityMainBinding.layoutInclude.tvInclude.setText("谢谢");
注:include根布局再添加id时,此时会报错:java.lang.NullPointerException: Missing required view with ID:***。
6.include布局使用merge进行布局优化问题
修改上文的layout_comment.xml布局,根布局使用merge标签,其他不变:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_include"
android:text="你好"
android:gravity="end"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</merge>
Activity中正确使用代码:
LayoutCommentBinding commentBinding = LayoutCommentBinding.bind(activityMainBinding.getRoot());
commentBinding.tvInclude.setText("优化");
7.Fragment中简单使用
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
//使用ViewBinding
FragmentBaseBinding fragmentBaseBinding = FragmentBaseBinding.inflate(inflater);
return fragmentBaseBinding.getRoot();
}
8.自定义Dialog中使用
public class MyDialog extends Dialog {
protected View mView;
protected DialogBottomBinding mBinding;
public MyDialog(@NonNull Context context, @StyleRes int themeResId) {
super(context, themeResId);
//原来的写法
mView = View.inflate(getContext(), getLayoutId(), null);
//使用ViewBinding的写法
mBinding = DialogBottomBinding.inflate(getLayoutInflater());
mView = mBinding.getRoot();
setContentView(mView);
}
}
9.自定义View中使用
编写view_my_layout.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="你好"
android:textSize="50sp" />
</androidx.constraintlayout.widget.ConstraintLayout>
编写自定义view:MyLinearLayout,并添加上面布局。
// 自定义view
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context) {
this(context, null);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// 正常添加布局
ViewMyLayoutBinding binding = ViewMyLayoutBinding.inflate(LayoutInflater.from(getContext()), this, true);
// 针对根标签为merge
ViewMyLayoutMergeBinding binding = ViewMyLayoutMergeBinding.inflate(LayoutInflater.from(getContext()), this);
}
}
注:使用merge标签和不使用merge标签所对应的Binding文件是不同的,所以需要区别对待。
10.Base常见封装
编写布局文件activity_base.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".BaseActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary" />
</LinearLayout>
BaseActivity代码:
public abstract class BaseActivity<T extends ViewBinding> extends AppCompatActivity {
public ActivityBaseBinding baseBinding;
public T viewBinding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
baseBinding = ActivityBaseBinding.inflate(getLayoutInflater());
setContentView(baseBinding.getRoot());
viewBinding = getViewBinding();
}
protected abstract T getViewBinding();
public void setToolbarTitle(CharSequence title) {
baseBinding.toolbar.setTitle(title);
}
public void setToolbarTitle(@StringRes int stringId) {
baseBinding.toolbar.setTitle(stringId);
}
}
MainActivity中使用:
public class MainActivity extends BaseActivity<ActivityMainBinding> {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setToolbarTitle("标题");
viewBinding.tvContent.setText("使用ViewBinding");
}
@Override
protected ActivityMainBinding getViewBinding() {
return ActivityMainBinding.inflate(getLayoutInflater(), baseBinding.getRoot(), true);
}
}