Android DataBinding 的进阶用法

一、前言

上一篇文章中我们讲解了关于 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;
@Override
public 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:

@Override
public 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 是不是觉得简单吧。

@Override
public 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 方法里设置

@Override
public 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 方法,

@Override
public 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:

@Override
public 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);
}

运行结果截图

八、后记

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

源码地址

发布了29 篇原创文章 · 获赞 47 · 访问量 11万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览