1. 程式人生 > >MVVM + data-binding 快速入門

MVVM + data-binding 快速入門

前言

  1. 簡書上data-binding 的文章不少,但真正用來實現MVVM架構的文章不多。有些是官方的guide(https://developer.android.com/topic/libraries/data-binding/index.html) 的翻譯版本,且官方的guide的架構主要採用 data-binding + mvp 的形式。 本文講述一個快速入門的data- binding + mvvm架構。

    基本配置

    詳見官方
    gradle ,當然 gradle版本需要在1.5.0-alpha1 or higher
android {
    ....
    dataBinding {
        enabled = true
} }

快速demo入門

  1. 具體就是用了個gank.io的介面展示了一組妹子圖片。不多說,看結果:

    ezgif.com-gif-maker.gif
  2. gradle配置
     compile fileTree(include: ['*.jar'], dir: 'libs')
     compile 'com.android.support:appcompat-v7:24.1.1'
     compile 'com.squareup.retrofit2:retrofit:2.1.0'
     compile 'com.github.bumptech.glide:glide:3.7.0'
     compile 'com.squareup.retrofit2:converter-gson:2.1.0'
    compile 'com.android.support:cardview-v7:24.1.1'
    這裡本來demo不應該引入其他的庫的,但為了方便寫demo,故添加了retrofit來請求資料,glide來載入圖片。
  3. 資料請求封裝Model 省略getter和setter
/**
 * @Description: error: false,
 * results: [
 * {
 * _id: "57bc5238421aa9125fa3ed70",
 * createdAt: "2016-08-23T21:40:08.159Z",
 * desc: "8.24",
 * publishedAt: "2016-08-24T11:38:48.733Z",
 * source: "chrome",
 * type: "福利",
 * url: "http://ww3.sinaimg.cn/large/610dc034jw1f740f701gqj20u011hgo9.jpg",
 * used: true,
 * who: "daimajia"
 * },
 * ]
 */
public class MeiZiModel implements Serializable{ private String error; private List<Result> results; public static class Result{ private String _id; private String desc; private String publishedAt; private String createdAt; private String source; private String url; private String used; private String who; } }
  1. 使用retrofit 請求資料如下:
public interface MeiZiService {
    @GET("api/data/福利/{page}/{number}")
    Call<MeiZiModel> getMeiZi(@Path("page") int page, @Path("number") int number);
}

簡單解釋下,@GET當然是指get的方式請求,後面是具體的路徑。{page}/{number}這個是我隨便填的引數名,測試了下gank的介面,確實可以通過修改這兩個引數請求不同頁面的資料。
@path 就是path路徑上的變數。

  1. 回撥獲取返回結果:
public class ServiceGenerator {

    public static final String API_BASE_URL = "http://gank.io";

    private static Retrofit retrofit =
            new Retrofit.Builder()
                    .baseUrl(API_BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

    public static void getMeiZi(int page, int number, final MeiZiCallBack callBack) {
        MeiZiService service = retrofit.create(MeiZiService.class);
        Call<MeiZiModel> meiziCall = service.getMeiZi(page, number);
        meiziCall.enqueue(new Callback<MeiZiModel>() {
            @Override
            public void onResponse(Call<MeiZiModel> call, Response<MeiZiModel> response) {
                if (callBack != null) {
                    callBack.onSuccess(response.body().getResults());
                }
            }

            @Override
            public void onFailure(Call<MeiZiModel> call, Throwable t) {
                if (callBack != null) {
                    callBack.onFail(t.getMessage());
                }
            }
        });
    }
}

注意整個url 為http://gank.io/ api/data/福利/{page}/{number} 後面的兩個為需要傳入的引數,然後把retrofit請求結果回撥即可。

6 MainActivity

public class MainActivity extends AppCompatActivity {
    private ListView mListView;
    private MeiziAdapter mMeiziAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mListView = (ListView) findViewById(R.id.list);
        mMeiziAdapter = new MeiziAdapter(this);

        ServiceGenerator.getMeiZi(11, 2, new MeiZiCallBack() {
            @Override
            public void onSuccess(List<MeiZiModel.Result> result) {
                mMeiziAdapter.setDatas(result);
                mListView.setAdapter(mMeiziAdapter);
            }

            @Override
            public void onFail(String error) {
               //TODO 沒處理失敗的情況。
            }
        });
    }
}

很簡單,將資料塞給adpter。

ViewModel 和View

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

    <data>
        <import type="android.view.View"/>
        <variable
            name="viewModel"
            type="com.example.xxx.mvvmdemo.ViewModel.ItemViewModel">
        </variable>
    </data>

    <android.support.v7.widget.CardView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <com.example.xxx.mvvmdemo.View.CustomImage
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            bind:load = "@{viewModel.imageUrl}"
            android:onClick="@{viewModel::onItemClick}"
            />

        <TextView
            android:layout_width="60dp"
            android:layout_height="wrap_content" 
            android:visibility="@{viewModel.isVisibility ? View.VISIBLE : View.GONE }"
            android:text="@{viewModel.text}"
            />

    </android.support.v7.widget.CardView>
</layout>

這裡需要注意的幾點:
(1) 先看整個頭部,添加了 layout 和 data 部分。 data 部分import 匯入需要使用的包,如textView的 visibility需要 View.VISIBLE 常數,存在於 android.view.View類。
(2) 引入自定義的viewModel 注意,這裡變數name 為viewModel ,type具體的類。
(3) 針對簡單的TextView 的text 直接使用@{viewModel.text},viewModel.text 這個預設回去訪問text的getter方法getText(),但找不到時,會去找text的公共屬性text。
(4) data- binding 支援三元運算子 @{viewModel.isVisibility ? View.VISIBLE : View.GONE } 還支援邏輯運算等等,詳見
https://developer.android.com/topic/libraries/data-binding/index.html#expression_language
(5) 特殊情形,如這裡是imageView,我想讓其自動載入圖片。這裡是使用三方庫Glide載入,怎麼辦呢?使用BindingMethod 。自定義 bind:load = "@{viewModel.imageUrl}"
load方法時,我們通過url引數,直接在網上下載該圖片。具體見CustomImageAdapter類通過註解 @BindingAdapter("load") 來指明需要動態載入的圖片url。這裡還有點特殊,imageView沒有使用Glide載入圖片的介面,故擴充套件了load方法,詳見CustomImage。

public class ItemViewModel {
    private final ObservableBoolean isVisibility = new ObservableBoolean(false);
    private final ObservableField<String> mImageUrl = new ObservableField<>();
    private final ObservableField<String> text = new ObservableField<>();


    public Context mContext;
    public String mUrl;


    public ItemViewModel(Context mContext) {
        this.mContext = mContext;
    }
    public void setData(MeiZiModel.Result result,boolean isShowText){
        if(result == null ){
            return;
        }
        mImageUrl.set(result.getUrl());
        mUrl = result.getUrl();
        text.set(result.getDesc());
        isVisibility.set(isShowText);
    }

    public ObservableBoolean getIsVisibility() {
        return isVisibility;
    }
    public ObservableField<String> getImageUrl() {
        return mImageUrl;
    }
    public ObservableField<String> getText() {
        return text;
    }

}
public class MeiziAdapter extends BaseAdapter {

    private Context mContext;
    private List<MeiZiModel.Result> mDatas;

    public void setDatas(List<MeiZiModel.Result> mDatas) {
        this.mDatas = mDatas;
    }

    public MeiziAdapter(Context mContext) {
        this.mContext = mContext;
    }

    @Override
    public int getCount() {
        return mDatas == null ? 0 : mDatas.size();
    }

    @Override
    public Object getItem(int i) {
        return mDatas == null || mDatas.size() == 0 ? null : mDatas.get(i);
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ItemViewModel viewModel;
        if (convertView == null) {
            ItemViewBinding binding = DataBindingUtil.inflate((LayoutInflater) mContext.getApplicationContext().getSystemService
                    (Context.LAYOUT_INFLATER_SERVICE), R.layout.item_view, null, false);
            viewModel = new ItemViewModel(mContext);
            binding.setViewModel(viewModel);
            convertView = binding.getRoot();
            convertView.setTag(viewModel);
        } else {
            viewModel = (ItemViewModel) convertView.getTag();
        }
        viewModel.setData(mDatas.get(position),position % 2 == 0);
        return convertView;
    }
}

這裡需要注意ItemViewBinding 類 是data-binding給我們生成的,採用我們佈局的xml下劃線改為駝峰命名,最後加上Binding作為區分。 其實這裡跟holder類似, 不過這裡的優點是可以重用ViewModel的邏輯。

public class CustomImage extends ImageView {
    public CustomImage(Context context) {
        super(context);
    }

    public CustomImage(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomImage(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    public void load(String url){
        Glide.with(this.getContext().getApplicationContext()).load(url).fitCenter().into(this);
    }
}
public class CustomImageAdapter {
    @BindingAdapter("load")
    public static void load(CustomImage imageView,String url){
        imageView.load(url);
    }
}

more

整個工程結構講完了,這裡mvvm的形式,為什麼比一般的mvc更好呢?
答案是ViewModel和View的弱耦合,這裡就可以多次重用。比如重用xml或重用ViewModel或者整個重用。最關鍵的是這樣減輕了Activity的工作量,可以模組化去拆分每個頁面的邏輯,做到更多的複用。

相關推薦

MVVM + data-binding 快速入門

前言 簡書上data-binding 的文章不少,但真正用來實現MVVM架構的文章不多。有些是官方的guide(https://developer.android.com/topic/libraries/data-binding/index.html) 的翻譯版本,且官

spring-data-jpa快速入門(一)——

快速 span ron blank support bubuko body lan -s 一、概述   官網:https://projects.spring.io/spring-data-jpa/   1.什麽是spring-data-jpa   Spring D

spring-data-jpa快速入門(二)——簡單查詢

ref spa data mail domain event cif open 寫實 一、方法名解析   1.引言     回顧HelloWorld項目中的dao接口 public interface GirlRepository extends JpaRepos

Android---Data Binding使用入門

一直沒使用過Data Binding,今天就來學習一下,有興趣的朋友可以直接看官網的介紹Data Binding 準備工作 首先要在build.gradle中新增一句 dataBinding {         enabled = true &nb

Android之MVC、MVP、MVVM(Data Binding)

本文旨在記錄最近總結的Android客戶端架構,作為學習筆記,歡迎批評指正。 一、概述 MVC:Model - View - Controller MVP: Model - View - Presenter MVVC:Model - View - V

android Data Binding 入門

本文參考databinding官方文件整理。官方文件連結地址https://developer.android.com/topic/libraries/data-binding/index.html#data_binding_layout_files Data Binding框架作為官方推薦的M

Android開發利器之Data Binding Compiler V2 —— 搭建Android MVVM完全體的基礎

原創宣告: 該文章為原創文章,未經博主同意嚴禁轉載。 前言: Android常用的架構有:MVC、MVP、MVVM,而MVVM是唯一一個官方提供支援元件的架構,我們可以通過Android lifecycle系列元件、DataBinding或者通過組合兩者的形式來打造一個強大的MVVM架構。而D

WPF快速入門系列(8)——MVVM快速入門

http://www.cnblogs.com/zhili/p/MVVMDemo.html 一、引言   在前面介紹了WPF一些核心的內容,其中包括WPF佈局、依賴屬性、路由事件、繫結、命令、資源樣式和模板。然而,在WPF還衍生出了一種很好的程式設計框架,即WVV

WPF/MVVM 快速入門教程

簡介 先假設大家對c#已經有一定的瞭解了,並且很容易接受一些關於WPF的知識。因為下面的知識是通過WPF為例的。 我前段時間開始研究了一下WPF,但是找不到什麼有幫助的關於MVVM的教程。因此我希望這篇文章能讓你眼前一亮。 當我們剛開始學一門新技術時,我們得益於前人的積

Android-MVVM架構-Data Binding的使用

專案整體效果: 什麼是MVVM , 為什麼需要MVVM? MVVM是Model-View-ViewModel的簡寫. 它是有三個部分組成:Model、View、ViewModel。 Model:資料模型層。包含業務邏輯和校驗邏輯。 View:螢

solr快速入門的地址,spring-data/solr

http://lucene.apache.org/solr/quickstart.htmlhttp://docs.spring.io/spring-data/solr/docs/1.2.0.RC1/re

Android-architecture之MVC、MVP、MVVMData-Binding

傳送門 MVC 結構簡介 例項分析 Controller控制器式 public class MainActivity extends ActionBarActivity implements OnWeatherLi

day39-Spring 12-Spring的JDBC模板:快速入門

pri 哪些 困難 ces 5.0 使用 只需要 common commons Spring AOP的關鍵是它的底層的原理和思想,配置和使用並不是十分困難.AOP本身就是一個思想,是面向對象的延伸,不是用來替換面向對象的,而是用來解決面向對象中的一些問題的.在最初的時候提出

vuex2快速入門

for nbsp mar lin ext mac os cnblogs value san #建立store.jsimport Vue from ‘vue‘; import Vuex from ‘vuex‘; Vue.use(Vuex) export d

快速入門系列--WCF--07傳輸安全、授權與審核

最大的 緩存 ims cut 常見 曾經 strong 這一 set 這部分主要涉及企業級應用的安全問題,一般來說安全框架主要提供3個典型的安全行為:認證、授權和審核。除了典型的安全問題,對於一個以消息作為通信手段的分布式應用,還需要考慮消息保護(Message Prote

快速入門系列

body 現在 安全 behavior 需求 discovery 中心 驗證 溝通 最後一章將進行WCF擴展和新特性的學習,這部分內容有一定深度,有一個基本的了解即可,當需要自定義一個完整的SOA框架時,可以再進行細致的學習和實踐。 服務端架構體系的構建主要包含接下來

快速入門系列--WCF--02消息、會話與服務寄宿

abc align bsp 不同的 cpu .org 程序 伸縮 網絡 經過WCF基礎的ABC學習,已經可以構建簡單的WCF的服務,使用不同的服務地址和綁定類型,根據業務提供所需的服務契約。但不禁想問,服務所使用的消息報文是什麽樣的形式麽?蘊含什麽樣內容呢?WCF服務是否支

python 基本語法速覽,快速入門

我們 method adding ger monk use gre 數據類型 struct https://zhuanlan.zhihu.com/p/24536868 學習參考於這個博文。 我做一個筆記。 關於python一些常用的語法快速的預覽,適合已經掌握一門編程語

Django REST framework 的快速入門教程

ret turn ads 使用 blog 所有 定義 想去 cti CRM-API項目搭建 序列器(Serializers) 首先,我們來定義一些序列器。我們來創建一個新的模塊(module)叫做 crm/rest_searializer.py ,這是我們用來描述數據是如何

Celery 分布式任務隊列快速入門

ade sunday reat 失敗 繼續 complete port 機器 single Celery介紹和基本使用 在項目中如何使用celery 啟用多個workers Celery 定時任務 與django結合 通過django配置celery period