Android DataBinding 的进阶用法

来源:互联网 发布:淘宝oppo手机 编辑:程序博客网 时间:2024/05/21 01:58

一、前言

上一篇文章中我们讲解了关于 Android DataBinding的基本用法,但是只是讲到关于数据的绑定的使用,对于监听器的使用等都还没有讲解,例如 Button 的 onClick()、onLongClick() 等事件用 DataBinding 改怎么用呢?还有在 Fragment 中怎么使用,用到 ListView 该怎么用等等,那么这一篇就讲解下 Databinding 的进阶使用。

二、导入类

在 data 标签里是可以用 import 的,就是说用了 import 的类在写 variable 的时候 type 就不要写全包名,写个类名就好了,这和在 Java 里使用是一样的,lang 包里的也是不用 import 的,例如 用 User 类的时候,本来是这样的:

<variable    name="user"    type="com.ce.listener.bean.User" />

如果用 import 就可以这样写:

<import type="com.ce.listener.bean.User" />        <variable            name="user"            type="User" />

那如果两个重名类怎么办,这就要用到 alias 属性了,别名嘛。

<import type="com.ce.advanced.bean.User" alias="User1"/>        <variable            name="user"            type="User1" />

二、绑定监听器

(一)给 Button 添加 onClick 事件

上一篇文章我们还有个 Login 的 Button 还没用到,现在我们给这个 Button 添加个 onClick 事件,添加 onClick 有两种方式,一种是直接调用方法的,另一种是绑定监听器的。

1.直接调用方法

就像以前在 onClick 里写个方法名,然后在 Activity 里实现那个方法一样,比如 onClick=”onLogin”,在 Activity 里就得实现 public void onLogin(View pView){},在这里也是差不多的,对于 onLongClick 等也是适用的,但要注意返回值要对应,比如 onLongClick 的返回值是 boolean 类型的,我们写的方法的返回值就得是 boolean 类型,我们新建个 EventHandlers 类:

import android.view.View;import android.widget.Toast;public class EventHandlers {    public void onLogin(View pView) {        Toast.makeText(pView.getContext(), "on Login Click", Toast.LENGTH_SHORT).show();    }}

这EventHandlers里面有个 onLogin 方法,如果调用了 onLogin 方法就弹个 Toast, 在 layout 中我们可以这样用:
在 data 里标签里定义个 EventHandlers 类变量

<variable    name="event"    type="com.ce.advanced.event.EventHandlers" />

给 Button 设置上 onClick,@{变量名::方法}

<Button    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center_horizontal"    android:onClick="@{event::onLogin}"    android:text="Login" />

在 Activity 中设置 EventHandlers 进去

EventHandlers _Event = new EventHandlers();_ViewDataBinding.setEvent(_Event);

运行结果截图

2.绑定监听器

我们现在是要做 login 操作,但上面的方法没有办法获取到 EditText 中的数据,那怎么办,怎么才能把 User 类也传进方法中呢?那就得用到绑定监听器的方法了,新建个Presenter类。

public class Presenter {    public void onLogin(User pUser) {        Log.v("Presenter", "UserName = " + pUser.getUserName() + ", Password = " + pUser.getPassword());    }}

在 data 标签里定义一个 Presenter 类变量

<variable    name="presenter"    type="com.ce.advanced.event.Presenter" />

给 Button 设置上 onClick,@{()->变量名.方法名(参数)}。

<Button    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center_horizontal"    android:onClick="@{() -> presenter.onLogin(user)}"    android:text="Login" />

在 Activity 中设置

_ViewDataBinding.setPresenter(new Presenter());

运行结果截图

那我如果要用到当前 View,如何把当前 View 也传进去呢?这也简单,在 Presenter 类中添加一个 onLogin 重载方法。

public void onLogin(View pView, User pUser) {    Toast.makeText(pView.getContext(), pUser.getUserName() + " " +ss pUser.getPassword(), Toast.LENGTH_SHORT).show();    }

在给 Button 设置 onClick 的时候就要给前面的括号一个参数,@{(当前 View 变量的名称) -> 变量名.方法名(当前 View 变量的名称, 参数)}。

<Button    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center_horizontal"    android:onClick="@{(pView) -> presenter.onLogin(pView, user)}"    android:text="Login" />

运行结果截图

参数可以是0个至多个的,layout 中传入的参数要和方法的参数要对应,当然方法的返回值也要和事件的返回值要对应,比如:

public void onLogin() {    Log.v("Presenter", "onLogin");}
<Button    android:layout_width="wrap_content"    android:layout_height="wrap_content"    android:layout_gravity="center_horizontal"    android:onClick="@{() -> presenter.onLogin()}"    android:text="Login" />

运行结果截图

如果我想要监听 EditText 的输入变化呢,这也是可以的,我们知道在 Java 代码中是用到 addTextChangedListener 这个方法,参数是 TextWatcher 接口。

public void addTextChangedListener(TextWatcher watcher) {    if (mListeners == null) {        mListeners = new ArrayList<TextWatcher>();    }    mListeners.add(watcher);}

在 Presenter 中写个内部类实现 TextWatcher:

public final TextWatcher OnUserNameChange = new TextWatcher() {        @Override        public void beforeTextChanged(CharSequence s, int start, int count, int after) {            Log.v("Presenter", "beforeTextChanged = " + s);        }        @Override        public void onTextChanged(CharSequence s, int start, int before, int count) {            Log.v("Presenter", "onTextChanged = " + s);        }        @Override        public void afterTextChanged(Editable s) {            Log.v("Presenter", "afterTextChanged = " + s);        }    };

在 Layout 中可以这样使用

<EditText    android:layout_width="match_parent"    android:layout_height="wrap_content"    android:hint="User name"    android:addTextChangedListener="@{presenter.OnUserNameChange}"    android:text="@{user.userName}" />

好了,其他监听器该怎么用如果用到的话再慢慢摸索吧,也是差不多的。
运行结果截图

三、双向绑定

不知道大伙儿有没有发现现在在 EditText 里改变文字后,点 Login 按钮会发现还是之前设置的 Text 。
运行结果截图

那怎么办,我们现在要对 User 类改造一番,让 User 类继承 BaseObservable 这个类,BaseObservable 类有个 notifyPropertyChanged 方法

/**     * Notifies listeners that a specific property has changed. The getter for the property     * that changes should be marked with {@link Bindable} to generate a field in     * <code>BR</code> to be used as <code>fieldId</code>.     *     * @param fieldId The generated BR id for the Bindable field.     */    public void notifyPropertyChanged(int fieldId) {        if (mCallbacks != null) {            mCallbacks.notifyCallbacks(this, fieldId, null);        }    }

看这个方法的注释就知道它会通知 listeners 属性已经改变了,有个条件是 getter 是要用到 Bindable 注解的,传入的参数是用 BR 这个类获取 fieldId,这里是要先对 getter 用 Bindable 注解,如果 BR 类没有那个 fieldId 的话就点 Build -> Rebuild Project 就有了,然后在 setter 调用 notifyPropertyChanged,把 BR 类生成的 fieldId 传进去,对 User 类改造如下。

public class User extends BaseObservable {    private String userName;    private String password;    public User() {    }    public User(String userName, String password) {        this.userName = userName;        this.password = password;    }    @Bindable    public String getUserName() {        return userName;    }    public void setUserName(String userName) {        this.userName = userName;        notifyPropertyChanged(BR.userName);    }    @Bindable    public String getPassword() {        return password;    }    public void setPassword(String password) {        this.password = password;        notifyPropertyChanged(BR.password);    }}

在 Layout 使用的时候呢就多了个 = ,

<LinearLayout xmlns:tools="http://schemas.android.com/tools"        android:id="@+id/activity_main"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical"        android:paddingBottom="@dimen/activity_vertical_margin"        android:paddingLeft="@dimen/activity_horizontal_margin"        android:paddingRight="@dimen/activity_horizontal_margin"        android:paddingTop="@dimen/activity_vertical_margin"        tools:context="com.ce.advanced.activity.MainActivity">        <EditText            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:hint="User name"            android:addTextChangedListener="@{presenter.OnUserNameChange}"            android:text="@={user.userName}" />        <EditText            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:hint="Password"            android:text="@={user.password}" />        <Button            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal"            android:onClick="@{(pView) -> presenter.onLogin(pView, user)}"            android:text="Login" /></LinearLayout>

运行结果截图

我的 bean 类已经是有父类的了,不能再继承 BaseObservable,还有你这样做也好麻烦,有没有简单点的?当然有,Google 给我们提供了一系列 ObservableFields。

ObservableFields截图

有对应的类型就用对应的,如果没有的话就用 ObservableField 这个类,其实这些大部分也是继承 BaseObservable 的,我们可以看下继承自 BaseObservable 的子类有哪些

ObservableFields截图

再新建个 User2 类,

public class User2 {    public final ObservableField<String> userName = new ObservableField<>();    public final ObservableField<String> password = new ObservableField<>();    public User2() {    }    public User2(String userName, String password) {        this.userName.set(userName);        this.password.set(password);    }}

不是吧?就这么简单?是的,就这么简单,我们在 Presenter 里增加个 User2 类型的参数的 onLogin 方法,

public void onLogin(View pView, User2 pUser) {    Toast.makeText(pView.getContext(), "User2 " + pUser.userName.get() + " " + pUser.password.get(), Toast.LENGTH_SHORT).show();}

在 Layout 中使用是完全一样的,在 data 标签里再定义一个 User2 类变量

<variable    name="user2"    type="com.ce.advanced.bean.User2" />
<LinearLayout xmlns:tools="http://schemas.android.com/tools"        android:id="@+id/activity_main"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="vertical"        android:paddingBottom="@dimen/activity_vertical_margin"        android:paddingLeft="@dimen/activity_horizontal_margin"        android:paddingRight="@dimen/activity_horizontal_margin"        android:paddingTop="@dimen/activity_vertical_margin"        tools:context="com.ce.advanced.activity.MainActivity">        <EditText            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:hint="User name"            android:addTextChangedListener="@{presenter.OnUserNameChange}"            android:text="@={user2.userName}" />        <EditText            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:hint="Password"            android:text="@={user2.password}" />        <Button            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="center_horizontal"            android:onClick="@{(pView) -> presenter.onLogin(pView, user2)}"            android:text="Login" />    </LinearLayout>

在 Activity 中把 User2 设置进去,

_ViewDataBinding.setUser2(new User2("CE", "123456"));

运行结果截图

四、在 Fragment 中使用

在 Fragment 中使用也简单,新建个 fragment_user 布局,

<layout    xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    xmlns:app="http://schemas.android.com/apk/res-auto">    <data>    </data>    <android.support.design.widget.CoordinatorLayout        android:layout_width="match_parent"        android:layout_height="match_parent"        android:fitsSystemWindows="true"        tools:context="com.ce.advanced.activity.UserActivity">        <android.support.design.widget.AppBarLayout            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:theme="@style/AppTheme.AppBarOverlay">            <android.support.v7.widget.Toolbar                android:id="@+id/toolbar"                android:layout_width="match_parent"                android:layout_height="?attr/actionBarSize"                android:background="?attr/colorPrimary"                app:popupTheme="@style/AppTheme.PopupOverlay"                app:title="@string/users_fragment"/>        </android.support.design.widget.AppBarLayout>        <RelativeLayout            android:id="@+id/content_test"            android:layout_width="match_parent"            android:layout_height="match_parent"            android:paddingBottom="@dimen/activity_vertical_margin"            android:paddingLeft="@dimen/activity_horizontal_margin"            android:paddingRight="@dimen/activity_horizontal_margin"            android:paddingTop="@dimen/activity_vertical_margin"            app:layout_behavior="@string/appbar_scrolling_view_behavior">            <TextView                android:layout_width="wrap_content"                android:layout_height="wrap_content"                android:text="@string/users_fragment"/>        </RelativeLayout>        <android.support.design.widget.FloatingActionButton            android:id="@+id/fab"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:layout_gravity="bottom|end"            android:layout_margin="@dimen/fab_margin"            app:srcCompat="@drawable/ic_add_white_24dp" />    </android.support.design.widget.CoordinatorLayout></layout>

接着再新建个 UsersFragment 类,

private FragmentUsersBinding mUsersBinding;@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container,                             Bundle savedInstanceState) {        setHasOptionsMenu(true);        // Inflate the layout for this fragment        mUsersBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_user, container, false);        return mUsersBinding.getRoot();    }

在 onCreateView 里用 DataBindingUtil 的 inflate,里面也是用的是 LayoutInflater 去 inflate的,FragmentUsersBinding.getRoot() 返回的就是 LayoutInflater 的 inflate 返回的 View,看一下源码吧:

DataBindingUtil.java

public static <T extends ViewDataBinding> T inflate(LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent, boolean attachToParent) {    return inflate(inflater, layoutId, parent, attachToParent, sDefaultComponent);}public static <T extends ViewDataBinding> T inflate(            LayoutInflater inflater, int layoutId, @Nullable ViewGroup parent,            boolean attachToParent, DataBindingComponent bindingComponent) {    final boolean useChildren = parent != null && attachToParent;    final int startChildren = useChildren ? parent.getChildCount() : 0;    final View view = inflater.inflate(layoutId, parent, attachToParent);    if (useChildren) {        return bindToAddedViews(bindingComponent, parent, startChildren, layoutId);    } else {        return bind(bindingComponent, view, layoutId);    }}

attachToParent 是 false,所以 useChildren 是 false 走的下边的 bind 方法。

static <T extends ViewDataBinding> T bind(DataBindingComponent bindingComponent, View root,            int layoutId) {    return (T) sMapper.getDataBinder(bindingComponent, root, layoutId);}

接下来进入 DataBinderMapper 的 getDataBinder 方法中。

DataBinderMapper.java

public android.databinding.ViewDataBinding getDataBinder(android.databinding.DataBindingComponent bindingComponent, android.view.View view, int layoutId) {    switch(layoutId) {        case com.ce.advanced.R.layout.activity_main:            return com.ce.advanced.databinding.ActivityMainBinding.bind(view, bindingComponent);        case com.ce.advanced.R.layout.activity_user:              return com.ce.advanced.databinding.ActivityUserBinding.bind(view, bindingComponent);        case com.ce.advanced.R.layout.fragment_user:              return com.ce.advanced.databinding.FragmentUserBinding.bind(view, bindingComponent);        }        return null;    }

这里我们传进去的是 fragment_user,所以进入到 FragmentUserBinding.bind 方法中。

FragmentUserBinding.java

public static FragmentUserBinding bind(android.view.View view, android.databinding.DataBindingComponent bindingComponent) {    if (!"layout/fragment_user_0".equals(view.getTag())) {        throw new RuntimeException("view tag isn't correct on view:" + view.getTag());    }    return new FragmentUserBinding(bindingComponent, view);}public FragmentUserBinding(android.databinding.DataBindingComponent bindingComponent, View root) {    super(bindingComponent, root, 0);    final Object[] bindings = mapBindings(bindingComponent, root, 3, sIncludes, sViewsWithIds);    this.fab = (android.support.design.widget.FloatingActionButton) bindings[2];    this.mboundView0 = (android.support.design.widget.CoordinatorLayout) bindings[0];    this.mboundView0.setTag(null);    this.toolbar = (android.support.v7.widget.Toolbar) bindings[1];    setRootTag(root);    // listeners    invalidateAll();}

FragmentUserBinding 的 bind 方法里 new 了个 FragmentUserBinding 对象返回,所以我们在 onCreateView 用 FragmentUsersBinding 去接收,我们在看看 FragmentUsersBinding 的父类 ViewDataBinding 的构造方法。

ViewDataBinding.java

protected ViewDataBinding(DataBindingComponent bindingComponent, View root, int localFieldCount) {    this.mBindingComponent = bindingComponent;    this.mLocalFieldObservers = new ViewDataBinding.WeakListener[localFieldCount];    this.mRoot = root;    if(Looper.myLooper() == null) {        throw new IllegalStateException("DataBinding must be created in view\'s UI Thread");    } else {        if(USE_CHOREOGRAPHER) {            this.mChoreographer = Choreographer.getInstance();            this.mFrameCallback = new FrameCallback() {                public void doFrame(long frameTimeNanos) {                    ViewDataBinding.this.mRebindRunnable.run();                }            };        } else {            this.mFrameCallback = null;            this.mUIThreadHandler = new Handler(Looper.myLooper());        }    }}

可以看到 root 是赋给 mRoot 的,好了,源码暂时分析到这里。
再新建个 UserActivity 类继承自 AppCompatActivity,把 UsersFragment 加载进去:

public class UserActivity extends AppCompatActivity implements UsersFragment.OnFragmentInteractionListener{    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        DataBindingUtil.setContentView(this, R.layout.activity_user);        getSupportFragmentManager()                .beginTransaction()                .replace(R.id.content_user, UsersFragment.newInstance(null, null))                .commitAllowingStateLoss();    }    @Override    public void onFragmentInteraction(Uri uri) {    }}

布局 activity_user:

<?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>    </data>    <FrameLayout        android:id="@+id/content_user"        android:layout_width="match_parent"        android:layout_height="match_parent"        tools:context="com.ce.advanced.activity.UserActivity">    </FrameLayout></layout>

修改下 Presenter 的 onLogin 的逻辑,点击了 Login 按钮后就跳转到 UserActivity,

public void onLogin(View pView, User2 pUser) {    if ("CE".equals(pUser.userName.get()) && "123456".equals(pUser.password.get())) {        onSuccess(pView.getContext());    } else {        onFail(pView.getContext());    }}private void onSuccess(Context pContext) {    Toast.makeText(pContext, "Success", Toast.LENGTH_SHORT).show();    Intent _Intent = new Intent(pContext, UserActivity.class);    pContext.startActivity(_Intent);}private void onFail(Context pContext) {    Toast.makeText(pContext, "Fail", Toast.LENGTH_SHORT).show();}

这里跳转后没有 finish 掉当前 Activity,如果要做的话就添加个回调去 finish。

运行结果截图

五、设置 Toolbar

现在的应用很多都有用到 Toolbar 的,这里也用一下,我们在 UsersFragment 中覆盖 onActivityCreated 方法,我们的 Activity 是继承自 AppCompatActivity 的,所以直接强转为 AppCompatActivity:

@Overridepublic void onActivityCreated(@Nullable Bundle savedInstanceState) {    super.onActivityCreated(savedInstanceState);    ((AppCompatActivity)getActivity()).setSupportActionBar(mUsersBinding.toolbar);}

在继承自 AppCompatActivity 的 Activity 中就直接用 setSupportActionBar(当前Activity的 ViewDataBinding.toolbar的id)。

六、基于 BaseAdapter 的使用

看到 Login 后是不是有点失望,没什么好看的,来个列表 (ListView) 吧,在这里说下 ListView 怎么用 Adapter的,以及 Adapter 里要怎么处理 item 的布局,新建个 item 的布局 list_item.xml:

<?xml version="1.0" encoding="utf-8"?><layout    xmlns:android="http://schemas.android.com/apk/res/android">    <data>        <variable            name="user"            type="com.ce.advanced.bean.User2" />    </data>    <LinearLayout        android:orientation="vertical" android:layout_width="match_parent"        android:layout_height="match_parent">        <TextView            android:layout_width="match_parent"            android:layout_height="38dp"            android:gravity="center"            android:text="@{user.userName}"/>    </LinearLayout></layout>

这里用的是 User2 类,然后把 userName 设置到 TextView 里

接着再来个 Adapter 继承自 BaseAdapter 的:

public class UserAdapter extends BaseAdapter {    private Context mContext;    private List<User2> mData;    public UserAdapter(Context pContext, List<User2> pData) {        this.mContext = pContext;        this.mData = pData != null ? pData : new ArrayList<User2>();    }    @Override    public int getCount() {        return mData.size();    }    @Override    public User2 getItem(int position) {        return mData.get(position);    }    @Override    public long getItemId(int position) {        return position;    }    @Override    public View getView(int position, View convertView, ViewGroup parent) {        int _LayoutId = R.layout.list_item;        ViewHolder _Holder= ViewHolder.getViewHolder(mContext, _LayoutId, convertView, parent);        _Holder.mItemBinding.setUser(mData.get(position));        return _Holder.mConvertView;    }    public void addItem(int pPostion, User2 pUser) {        this.mData.add(pPostion, pUser);        notifyDataSetChanged();    }    private static class ViewHolder{        ListItemBinding mItemBinding;        View mConvertView;        private ViewHolder(Context pContext, ViewGroup parent, int pLayoutId) {            mItemBinding = DataBindingUtil.inflate(LayoutInflater.from(pContext), pLayoutId, parent, false);            this.mConvertView = mItemBinding.getRoot();            this.mConvertView.setTag(this);        }        static ViewHolder getViewHolder(Context pContext, int pLayoutId, View convertView, ViewGroup parent) {            if (convertView == null) {                return new ViewHolder(pContext, parent, pLayoutId);            }            return (ViewHolder) convertView.getTag();        }    }}

其实和以前的写法也是差不多的,不过加载的时候用的是 DataBindingUtil 去加载的,然后设置数据也方便了。
在 fragment_user 布局的 data 里添加个 Adapter 变量:

<data>    <import type="com.ce.advanced.adapter.UserAdapter" />    <variable        name="adapter"        type="UserAdapter" />    </data>

把 fragment_user 布局中的 TextView 换成 ListView,把 定义的 adapter 变量设置进 ListView 的属性里。

<ListView    android:layout_width="match_parent"    android:layout_height="match_parent"    android:adapter="@{adapter}"></ListView>

覆盖 UsersFragment 类的 onViewCreated 方法把 UserAdapter 对象设置进去,就完成了,用了 DataBinding 是不是觉得简单吧。

@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {    mUserAdapter = new UserAdapter(getContext(), getData());    mUsersBinding.setAdapter(mUserAdapter);}private List<User2> getData() {    List<User2> _Users = new ArrayList<>();    _Users.add(new User2("Java", "1"));    _Users.add(new User2("C", "2"));    _Users.add(new User2("C++", "3"));    _Users.add(new User2("Python", "4"));    _Users.add(new User2("C#", "5"));    _Users.add(new User2(".NET", "6"));    _Users.add(new User2("JavaScript", "7"));    _Users.add(new User2("Assembly", "8"));    _Users.add(new User2("PHP", "9"));    _Users.add(new User2("Perl", "10"));    _Users.add(new User2("Ruby", "11"));    _Users.add(new User2("VB", "12"));    _Users.add(new User2("Swift", "13"));    _Users.add(new User2("R", "14"));    _Users.add(new User2("Objective-C", "15"));    return _Users;}

运行结果截图

想要对 item 进行增加、删除等操作要怎么做?有没有注意到还有个 FloatingActionButton 没用到?还有 UserAdapter 里是有个 addItem 方法的也没用,那就来个增加 item 的吧,再写个 UserFragmentPresenter 类:

public class UserFragmentPresenter {    public interface OnUserAddListener {        void onUserAdd();    }    private OnUserAddListener mOnUserAddListener;    public void onUserAdd() {        if (mOnUserAddListener != null) {            mOnUserAddListener.onUserAdd();        }    }    public void setOnUserAddListener(OnUserAddListener pOnUserAddListener) {        this.mOnUserAddListener = pOnUserAddListener;    }}

onUserAdd 方法是给 FloatingActionButton 的 onClick 调用的方法,因为 Adapter 在 UsersFragment,所以我们要写个 OnUserAddListener 接口让 UsersFragment 实现,点击 FloatingActionButton 的时候进行回调。
在 fragment_user 布局的 data 里再添加个变量:

<variable      name="presenter"      type="com.ce.advanced.event.UserFragmentPresenter" />

给 FloatingActionButton 设置 onClick:

<android.support.design.widget.FloatingActionButton      android:id="@+id/fab"      android:layout_width="wrap_content"      android:layout_height="wrap_content"      android:layout_gravity="bottom|end"      android:layout_margin="@dimen/fab_margin"      android:onClick="@{() -> presenter.onUserAdd()}"      app:srcCompat="@drawable/ic_add_white_24dp" />

在 UsersFragment 的 onViewCreated 方法里设置

@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {    mUserAdapter = new UserAdapter(getContext(), getData(), R.layout.list_item);    mUsersBinding.setAdapter(mUserAdapter);    UserFragmentPresenter _Presenter = new UserFragmentPresenter();    _Presenter.setOnUserAddListener(this);    mUsersBinding.setPresenter(_Presenter);}

让 UsersFragment 实现 UserFragmentPresenter.OnUserAddListener 接口,覆盖 onUserAdd 方法,

@Overridepublic void onUserAdd() {    List<User2> _Users = getData();    mUserAdapter.addItem(0, _Users.get(new Random().nextInt(_Users.size())));}

这里就用 UserAdapter 的 addItem 方法随机添加点数据进去。

运行结果截图

七、基于RecyclerView.Adapter 的使用

什么?现在 ListView 已经 out 了?都流行用 RecyclerView 了,还在用 ListView。

行行行

好了,说下在 RecyclerView 怎么用吧,先来个 Adapter:

public class UserAdapter extends RecyclerView.Adapter<UserAdapter.MyViewHolder> {    private Context mContext;    private List<User2> mData;    public UserAdapter(Context pContext, List<User2> pData) {        this.mContext = pContext;        this.mData = pData != null ? pData : new ArrayList<User2>();    }    @Override    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {        int _LayoutId = R.layout.list_item;        return MyViewHolder.getViewHolder(mContext, _LayoutId, parent);    }    @Override    public void onBindViewHolder(MyViewHolder holder, int position) {        holder.mItemBinding.setUser(mData.get(position));    }    @Override    public int getItemCount() {        return mData.size();    }    public void addItem(int pPostion, User2 pUser) {        mData.add(pPostion, pUser);        notifyItemInserted(pPostion);    }    static class MyViewHolder extends RecyclerView.ViewHolder {        private ListItemBinding mItemBinding;        private MyViewHolder(ListItemBinding pItemBinding) {            super(pItemBinding.getRoot());            this.mItemBinding = pItemBinding;        }        static MyViewHolder getViewHolder(Context pContext, int pLayoutId, ViewGroup parent) {            ListItemBinding _ItemBinding = DataBindingUtil.inflate(LayoutInflater.from(pContext), pLayoutId, parent, false);            return new MyViewHolder(_ItemBinding);        }    }}

其实这里也没什么好说的了,在 fragment_user 布局中的 data 里添加两个变量

<variable    name="adapter2"    type="com.ce.advanced.recycler.UserAdapter" /><variable    name="layoutManager"    type="android.support.v7.widget.RecyclerView.LayoutManager" />

一个是 Adapter,一个是 LayoutManager,把 ListView 改成 RecyclerView 并将 Adapter 和 LayoutManager 设置进去:

<android.support.v7.widget.RecyclerView    android:layout_width="match_parent"    android:layout_height="match_parent"    android:layoutManager="@{layoutManager}"    android:adapter="@{adapter2}"></android.support.v7.widget.RecyclerView>

把 UsersFragment 的 onViewCreated 的 UserAdapter 换成 RecyclerView 的 Adapter,并设置 LayoutManager,这里用的是 LinearLayoutManager:

@Overridepublic void onViewCreated(View view, @Nullable Bundle savedInstanceState) {    mUserAdapter = new UserAdapter(getContext(), getData());    mUsersBinding.setLayoutManager(new LinearLayoutManager(getContext()));    mUsersBinding.setAdapter2(mUserAdapter);    UserFragmentPresenter _Presenter = new UserFragmentPresenter();    _Presenter.setOnUserAddListener(this);    mUsersBinding.setPresenter(_Presenter);}

运行结果截图

八、后记

其实还有一些还没讲到,但时间安排不来写那么多,像转换器、在布局中使用表达式等,大伙儿用到再去研究,或者以后有空再写一篇。

源码地址