近段时间一些事情原因没有坚持写博客了,内心很纠结,本来很想就databinding来写一些自己的感想,但是一直拖到现在,心累,好了,不多说,别忘记重要事情
其实databinding的出现简洁了xml文件,顾名思义,就是数据绑定。那么如何来实现数据绑定呢?它又是怎么样去工作的呢?
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="android.view.View" /> <variable name="customer" type="com.example.wh.databindingdemo.Customer" /> <variable name="presenter" type="com.example.wh.databindingdemo.MainActivity.Presenter" /> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.example.wh.databindingdemo.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="@{()-> presenter.changeName()}" android:text="@{customer.name}" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:onTextChanged="@{presenter.onTextChanged}" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@{customer.id}" android:onTextChanged="@{presenter.onTextChanged}" /> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:onTextChanged="@{presenter.onTextChanged}" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:onClick="@{()-> presenter.StarActivity()}" /> </LinearLayout> </layout>
这个事一个简单的布局,跟常规布局就是多了外层跟布局layout,那么这个layout是干什么的呢?其实类似H5标签的头标签,描述当前布局信息的,variable其实就是一个属性描述,简单来讲,就是当前数据绑定到类中的属性。
下面我们看看怎么绑定数据的,首先看看Java代码中怎么去设置,
ActivityMainBinding mBinding; Customer customer = new Customer("wh","9527"); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mBinding = DataBindingUtil.setContentView(this,R.layout.activity_main); mBinding.setPresenter(new Presenter()); mBinding.setCustomer(customer); } public class Presenter{ public void changeName(){ Toast.makeText(MainActivity.this,"点击了"+customer.getName(),Toast.LENGTH_SHORT).show(); } public void onTextChanged(CharSequence s, int start, int before, int count) { customer.setName(s.toString()); mBinding.setCustomer(customer); if (s.toString().equals("")){ customer.setId("6666"); mBinding.setCustomer(customer); } } public void StarActivity(){ Intent intent = new Intent(MainActivity.this,ListActivity.class); startActivity(intent); } }
当我们设置了databinding后,系统默认为我们生成了当前activity对应的databinding,就是代码里的ActivityMainDataBinding,在xml中我们分别在TextView和edittext中设置显示了customer的ID和name,那么数据是怎么绑定的呢?下面我们来看看源码。
@NonNull private final android.widget.TextView mboundView1; @NonNull private final android.widget.EditText mboundView2; @NonNull private final android.widget.TextView mboundView3; @NonNull private final android.widget.EditText mboundView4; @NonNull private final android.widget.Button mboundView5;
这是databinding默认生成对应布局文件的几个控件,那么怎么设置到对应控件上面的呢?继续看源码
@Override protected void executeBindings() { long dirtyFlags = 0; synchronized(this) { dirtyFlags = mDirtyFlags; mDirtyFlags = 0; } com.example.wh.databindingdemo.MainActivity.Presenter presenter = mPresenter; java.lang.String customerId = null; android.databinding.adapters.TextViewBindingAdapter.OnTextChanged presenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged = null; java.lang.String customerName = null; com.example.wh.databindingdemo.Customer customer = mCustomer; if ((dirtyFlags & 0x5L) != 0) { if (presenter != null) { // read presenter::onTextChanged presenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged = (((mPresenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged == null) ? (mPresenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged = new OnTextChangedImpl()) : mPresenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged).setValue(presenter)); } } if ((dirtyFlags & 0x6L) != 0) { if (customer != null) { // read customer.id customerId = customer.getId(); // read customer.name customerName = customer.getName(); } } // batch finished if ((dirtyFlags & 0x4L) != 0) { // api target 1 this.mboundView1.setOnClickListener(mCallback1); this.mboundView5.setOnClickListener(mCallback2); } if ((dirtyFlags & 0x6L) != 0) { // api target 1 android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView1, customerName); android.databinding.adapters.TextViewBindingAdapter.setText(this.mboundView3, customerId); } if ((dirtyFlags & 0x5L) != 0) { // api target 1 android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView2, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)presenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, (android.databinding.InverseBindingListener)null); android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView3, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)presenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, (android.databinding.InverseBindingListener)null); android.databinding.adapters.TextViewBindingAdapter.setTextWatcher(this.mboundView4, (android.databinding.adapters.TextViewBindingAdapter.BeforeTextChanged)null, (android.databinding.adapters.TextViewBindingAdapter.OnTextChanged)presenterOnTextChangedAndroidDatabindingAdaptersTextViewBindingAdapterOnTextChanged, (android.databinding.adapters.TextViewBindingAdapter.AfterTextChanged)null, (android.databinding.InverseBindingListener)null); } }dirtyFlag这个事一个标志,对应的是当前控件的一个标志,我们可以看到在这段代码中可以找到settext,分别传入的控件和数据,
这段代码其实就是绑定数据和监听事件来操作数据显示的。
回头捋一下思路,首先,我们在XML文件中设置variable,这个指代的是我么需要的数据来源和类型,然后在控件上去绑定数据源,底层databinding会根据我们给的数据源去settext,将数据显示出来,
那如果我在输入框上改变文字,然后将文字再重新显示呢?其实databinding早就做了这个,这个就是数据的双向绑定,在presenter中我们看到设置监听事件,这个时候回,当我们改变输入框的文本,获取到文本,将文本赋给类中的属性,然后TextView在重新获取类中新的属性值。这就是数据的双向绑定,view-->model---->view
@Override public boolean setVariable(int variableId, @Nullable Object variable) { boolean variableSet = true; if (BR.presenter == variableId) { setPresenter((com.example.wh.databindingdemo.MainActivity.Presenter) variable); } else if (BR.customer == variableId) { setCustomer((com.example.wh.databindingdemo.Customer) variable); } else { variableSet = false; } return variableSet; }
在这段代码中databinding做的操作是,判断variableId对应的是哪一个?那么这个variableId怎么来的呢?其实回到xml文件中可以看到,每一个variable都对应一个name,这个name其实就是对应源码中的ID,这个过程就是根据XML文件来设置variable的,那么这个又有什么用的呢?现在我们想在列表中去使用databinding,那么怎么去使用呢?
这就必须涉及了adapter,
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data> <variable name="item" type="com.example.wh.databindingdemo.User"/> </data> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{item.company}" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{item.position}" android:layout_marginLeft="20dp" /> </LinearLayout> </layout>
/** * 作者 ;Created by ${wh} on 2018/3/25. */ public class UserAdapter extends RecyclerView.Adapter<BindingViewHolder> { private List<User> users; private Context mContext; private LayoutInflater layoutInflater; private OnItemClickLisener lisener; public interface OnItemClickLisener{ void onItemClick(int p); }; public UserAdapter(Context mContext) { this.mContext = mContext; users = new ArrayList<>(); layoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @Override public BindingViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { ViewDataBinding binding; binding = DataBindingUtil.inflate(layoutInflater,R.layout.list_item,parent,false); return new BindingViewHolder(binding); } @Override public void onBindViewHolder(BindingViewHolder holder, final int position) { User user = users.get(position); holder.getmBinding().setVariable(BR.item,user); holder.getmBinding().executePendingBindings(); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (lisener!=null){ lisener.onItemClick(position); } } }); } @Override public int getItemCount() { return users.size(); } public void setLisener(OnItemClickLisener lisener) { this.lisener = lisener; } public void addUser( User u){ int p = new Random().nextInt(4); users.add(p,u); notifyItemInserted(users.size()); } public void addAll( List<User> u){ users.addAll(u); notifyItemInserted(users.size()); } public void remove(int p){ if (users.size()>0){ users.remove(p); } notifyItemRemoved(p); } }
在onBindViewHolder中填充数据的时候,我们user实体对象添加到variable中去,在源码中我们可以看到,databinding会根据xml文件去自己将数据显示出来,这样节省了之前我们不断去根据xml中控件ID来设置settext。
以上是自己结合源码对databinding的工作流程做一个大概的描述,这个demo也只是说明一下源码,后续会在项目给大家展示databinding更多的用法