MVVM-DataBinding

1.MVC

Model View Controller模式,数据结构和Controller逻辑与UI进行解耦。
MVC逻辑图

  1. View:视图层,XML,它的职责是负责显示从Controller上获取的数据,一般由Activity承担其处理;
  2. Controller:控制层,一般也是Activity处理的比较多,从模型层获取数据,然后将数据绑定到view上,还需要对用户行为(输入等)进行监听;
  3. Model:模型层,对数据库的操作、网络的操作、文件的操作等耗时操作都应该在model层进行处理,承担的一般是一些业务逻辑业务处理等。

2.MVP

Model,View and Presenter模式,是由MVC演变而来,将Controller层变成了Presenter。
MVP逻辑图
相较于MVC,MVP中model层不再与view层进行直接通讯,Presenter起到了桥梁的作用,三者直接的关系:
1.View接收用户的交互请求
2. view将请求转发给Presenter
3. Presenter再转交给Model进行数据处理
4. 数据处理完后,Model再通过接口形式通知Presenter数据已经发生改变
5. Presenter层收到Model的通知后,更新至View层

Model:与MVC一样都是处理业务逻辑的
Viwe:一般是Activity、Fragment,它会持有Presenter的实例,来调用Presenter层的方法
Presenter:起到桥梁的作用,接收model层数据进而转交给View层处理

3.MVVM

随着业务逻辑的增加,UI层改变很多的情况下,View接口可能也会变得很庞大,如果能够实现数据与UI的双向绑定那该多好而MVVM就是解决这个问题的。
Model View ViewModel模式
MVVM

  • 用户直接跟View进行交互
  • View和ViewModel一对多
  • View有ViewModel的引用,但是ViewModel没有任何关联View的信息,也无法直接操作view,它们之间通过DataBinding进行双向数据绑定

从设计图上看其实跟MVP也差不多,其实差别还挺大,MVP中View和Presenter相互持有才能互相调用,而MVVM中View和ViewModel则是使用DataBinding进行绑定关联,使用起来更加的方便、解耦。

3.1DataBinding的使用

在app build.gradle中声明以下即可

 dataBinding {
        enabled = true
    }

首先先对布局进行改造处理

<layout>
   <import type=""/>//需导包时添加
   <varible         //变量参数
           name=""
           type="" />

    <布局><布局/>
</layout>

接着进行rebuild操作,就会自动生成一个Binding类,生成结构为layout文件名的结构

   MainMvvmBinding   binding = DataBindingUtil.setContentView(this, R.layout.main_mvvm);

绑定数据格式为 ·@{" “},双向绑定@={” "}
目前支持双向绑定的控件如下

AbsListView android:selectedItemPosition 
CalendarView android:date 
CompoundButton android:checked 
DatePicker android:year, android:month, android:day 
NumberPicker android:value 
RadioGroup android:checkedButton 
RatingBar android:rating 
SeekBar android:progress 
TabHost android:currentTab 
TextView android:text 
TimePicker android:hour, android:minute

简单使用

效果

布局部分

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <import type="android.view.View" />
        <variable
            name="user"
            type="com.wxx.androiddemo.bean.User" />
            
        <variable
            name="presenter"
            type="com.wxx.androiddemo.databinding.MainActivity.Presenter" />

        <variable
            name="showTextView"
            type="boolean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <EditText
            android:id="@+id/user"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="20dp"
            android:hint="用户名"
            android:onTextChanged="@{presenter.onTextChanged}"
            android:text="@={user.name}" />

        <EditText
            android:id="@+id/psw"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:layout_marginBottom="20dp"
            android:hint="密码"
            android:text="@={user.content}" />

        <Button
            android:id="@+id/btn_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginRight="20dp"
            android:background="@drawable/press"
            android:onClick="@{presenter.onClick}"
            android:text="@{@string/app_name(user.name)}"
            android:textColor="@color/colorWhite"
            android:textSize="18sp" />

        <Button
            android:id="@+id/btn_2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="5dp"
            android:layout_marginRight="20dp"
            android:background="@{@drawable/press}"
            android:onClick="@{presenter.onClick}"
            android:text='@{user.map["ite_2"]}'
            android:textColor="@color/colorWhite"
            android:textSize="18sp"
            android:visibility="@{user.visable? View.GONE : View.VISIBLE}" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <CheckBox
                android:id="@+id/isChecked"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="选择了CheckBox我就隐身给你看!!!"
                android:visibility="@{isChecked.checked?View.GONE:View.VISIBLE}" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <CheckBox
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:onCheckedChanged="@{presenter.onCheckedChanged}" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="选择了CheckBox我就隐身给你看!!!"
                android:visibility="@{showTextView?View.GONE:View.VISIBLE}" />
        </LinearLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:orientation="horizontal">

            <Button
                android:id="@+id/add"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@drawable/press"
                android:onClick="@{presenter.onClick}"
                android:text="增加"
                android:textColor="@color/colorWhite" />

            <View
                android:layout_width="1dp"
                android:layout_height="match_parent"
                android:background="@color/colorWhite" />

            <Button
                android:id="@+id/remove"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:background="@drawable/press"
                android:onClick="@{presenter.onClick}"
                android:text="移除"
                android:textColor="@color/colorWhite" />
        </LinearLayout>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </LinearLayout>
</layout>
public class User extends BaseObservable {
    private int id;
    private int age;
    private String name = "default";
    private String content = "xxx";
    private String headImgUrl;
    private ObservableBoolean isVisable;
    public ObservableArrayMap<String, String> map = new ObservableArrayMap<>();
    public ObservableArrayList<String> list = new ObservableArrayList<>();

    public User( boolean isVisable, String name, String content) {
        this.name = name;
        this.content = content;
        this.isVisable = new ObservableBoolean(isVisable);
    }

    public User(String name, String content) {
        this.name = name;
        this.content = content;
        isVisable = new ObservableBoolean(false);
        map.put("ite_1", "Click_1 参数");
        map.put("ite_2", "Click_2 参数");
        map.put("ite_3", "Click_3 参数");

        list.add("Click_1");
        list.add("Click_1");
        list.add("Click_1");
    }
    
  
    @Bindable
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }

    @Bindable
    public String getContent() {
        return content;
    }
  ………………
  ………………
}

也可以这样声明,就无需进行@Bindable了

public class BindingFile<T> {
    public ObservableField<Object> name = new ObservableField<>();
    public ObservableMap<Object, Object> map = new ObservableArrayMap<>();
    public ObservableArrayList<T> list = new ObservableArrayList<>();
    public ObservableBoolean isOk = new ObservableBoolean(false);
}

Activity

public class MainActivity extends AppCompatActivity {
    User user = new User("test", "why");
    MainBindingBinding binding;
    MyAdapter mAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.main_binding);
        binding.setUser(user);
        binding.setPresenter(new Presenter());

        binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
        mAdapter = new MyAdapter(this);
        binding.recyclerView.setAdapter(mAdapter);
        mAdapter.setmListener(new MyAdapter.OnItemClickListener() {
            @Override
            public void onUserClick(int position, User user) {
                Toast.makeText(MainActivity.this, "" + position, Toast.LENGTH_SHORT).show();
            }
        });

        //添加动画
        binding.addOnRebindCallback(new OnRebindCallback() {
            @Override
            public boolean onPreBind(ViewDataBinding binding) {
                ViewGroup root = (ViewGroup) binding.getRoot();
                TransitionManager.beginDelayedTransition(root);
                return true;
            }
        });
    }

    String[] languages = new String[]{"Android", "Java", "Html", "RN", "Python"};

    String[] urls = new String[]{
            "https://avatar.csdnimg.cn/2/4/7/1_wu996489865.jpg",
            "https://avatars3.githubusercontent.com/u/15843806?s=400&u=717f48864122fbee961992272ab18665f3969718&v=4",
            "https://error"
    };

    public class Presenter {
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.btn_1:
                    int i = (int) (Math.random() * 1000 + 1);
                    user.setName(String.valueOf(i));
                    user.setIsVisable(!user.isVisable().get());
                    break;
                case R.id.add:
                    boolean isVisable = new Random().nextBoolean();
                    String language = languages[new Random().nextInt(5)];
                    user = new User(isVisable, language, language + "是这个世界上最好用的语言!");
                    user.setHeadImgUrl(urls[new Random().nextInt(3)]);
                    mAdapter.add(user);
                    break;
                case R.id.remove:
                    mAdapter.remove();
                    break;
                case R.id.btn_2:
                    user.setContent("随机变化:" + System.currentTimeMillis());
                    break;
                default:
                    break;
            }
        }

        public void onTextChanged(CharSequence s, int start, int before, int count) {
            binding.user.setSelection(s.length());
        }

        /**
         * CheckBox事件
         *
         * @param view      .
         * @param isChecked .
         */
        public void onCheckedChanged(View view, boolean isChecked) {
            binding.setShowTextView(isChecked);
        }
    }
}

mvvm Demo构建
效果图

首先是view层
主布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <data>
        <variable
            name="articleViewModel"
            type="com.wxx.androiddemo.databinding.mvvmdemo.ArticleViewModel" />
    </data>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:refresh="@{articleViewModel.refreshing}">
        
        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:items="@{articleViewModel.items}" />
    </android.support.v4.widget.SwipeRefreshLayout>
</layout>

item布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="article"
            type="com.wxx.androiddemo.bean.ArticleBean.DataBean.DatasBean" />
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorWhite"
        android:orientation="vertical">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:lines="2"
            android:padding="5dp"
            android:text="@{article.title}"
            android:textSize="18sp"
            android:textColor="@color/colorBlack"
            android:textStyle="bold" />

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:padding="5dp">

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:ellipsize="end"
                android:maxLength="10"
                android:text="@{article.author}"
                android:textColor="@color/colorGray" />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="3dp"
                android:layout_marginRight="3dp"
                android:text="." />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="3dp"
                android:layout_marginRight="3dp"
                android:ellipsize="end"
                android:maxLength="10"
                android:text="@{article.superChapterName}" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginRight="3dp"
                android:gravity="end"
                android:text="@{article.publishTimeStr}" />
        </LinearLayout>
    </LinearLayout>
</layout>

Model层
model层其实就是网络数据的请求

public class ArticleModel {
    private IArticle article;
    private JsonRequest request;
    private int what;
    public void setArticle(IArticle article) {
        this.article = article;
    }
    public void fetch(int what, int page, final RequestType type) {
        this.what = what;
        request = new JsonRequest(AppUtil.getUrl(page), RequestMethod.GET);
        request.setCacheMode(CacheMode.REQUEST_NETWORK_FAILED_READ_CACHE);
        CallServer.getHttpclient().add(what, request, new HttpListener<JSONObject>() {
            @Override
            public void success(int what, Response<JSONObject> response) {
                ArticleBean articleBean = new Gson().fromJson(response.get().toJSONString(), ArticleBean.class);
                if (article != null) {
                    article.onSuccess(type,articleBean);
                }
            }

            @Override
            public void fail(int what, String e) {
                if (article != null) {
                    article.onFail(what, e);
                }
            }
        });
    }
    public void cancel() {
        if (request != null) {
            CallServer.getHttpclient().cancelWhat(what);
        }
    }
}

ViewModel层
数据绑定处理

public class ArticleViewModel implements IArticle {
    //数据源
    public ObservableField<ArticleBean> items = new ObservableField<>();
    //是否刷新
    public ObservableBoolean refreshing = new ObservableBoolean(true);
    private ArticleModel model;

    public ArticleViewModel() {
        model = new ArticleModel();
        model.setArticle(this);
    }

    /**
     * 
     * @param what 请求what
     * @param page 页数
     * @param type 请求类型
     */
    public void fetch(int what, int page,RequestType type) {
        refreshing.set(true);
        model.fetch(what, page,type);
    }

    @Override
    public void onSuccess(RequestType type, ArticleBean article) {
        article.setType(type);
        items.set(article);
        refreshing.set(false);
    }

    @Override
    public void onFail(int what, String errorMsg) {
        refreshing.set(false);
    }
}

Adapter适配器

public class ArticleAdapter extends RecyclerView.Adapter<BindingViewHolder> {
    private LayoutInflater inflater;
    private List<ArticleBean.DataBean.DatasBean> mList;
    private Context mContext;

    public ArticleAdapter(Context mContext) {
        mList = new ArrayList<>();
        this.mContext = mContext;
        inflater = LayoutInflater.from(mContext);
    }

    @NonNull
    @Override
    public BindingViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
        ViewDataBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_article, viewGroup, false);
        return new BindingViewHolder(binding);
    }

    @Override
    public void onBindViewHolder(@NonNull BindingViewHolder bindingViewHolder, int i) {
        ArticleBean.DataBean.DatasBean datasBean = mList.get(i);
        datasBean.setPublishTimeStr(AppUtil.longTime2StringTime(datasBean.getPublishTime()));
        //设置绑定数据
        bindingViewHolder.getmBinding().setVariable(BR.article, datasBean);
        //必须调用,当绑定的数据发生改变刷新视图
        bindingViewHolder.getmBinding().executePendingBindings();
    }

    @Override
    public int getItemCount() {
        return mList != null ? mList.size() : 0;
    }

    public void addAll(List<ArticleBean.DataBean.DatasBean> list) {
        mList.addAll(list);
        notifyDataSetChanged();
    }

    public void loadRefresh(List<ArticleBean.DataBean.DatasBean> list) {
        mList.clear();
        mList.addAll(list);
        notifyDataSetChanged();
    }
}

接下来就是Activity

public class DemoMainActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {
    private MainMvvmBinding binding;
    private ArticleAdapter mAdapter;
    private ArticleViewModel viewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.main_mvvm);
        binding.recyclerView.setLayoutManager(new LinearLayoutManager(this));
        binding.refresh.setOnRefreshListener(this);
        mAdapter = new ArticleAdapter(getApplicationContext());
        binding.recyclerView.setAdapter(mAdapter);
        DividerItemDecoration decoration = new DividerItemDecoration(this, DividerItemDecoration.VERTICAL);
        binding.recyclerView.addItemDecoration(decoration);

        viewModel = new ArticleViewModel();
        binding.setVariable(BR.articleViewModel, viewModel);
        binding.refresh.post(new Runnable() {
            @Override
            public void run() {
                viewModel.fetch(0, 0,RequestType.FIRST_LOAD);
            }
        });
    }

    @Override
    public void onRefresh() {
        viewModel.fetch(0, 0,RequestType.DOWN_REFRESH_LOAD);
    }
}

可以看到视图层跟Model层实现了真正的解耦,而且数据源的变化我们也不用处理了都交给了DataBinding去处理。

我是源码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

吴唐人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值