Android mvvm架構(gòu)demo(DataBinding+LiveData+ViewModel+ Repository)

1.實(shí)現(xiàn)效果

實(shí)現(xiàn)頁(yè)面加載Bing每日一圖的功能

2.項(xiàng)目結(jié)構(gòu)

image(忽略沒(méi)有按分類創(chuàng)建).png

3.實(shí)現(xiàn)過(guò)程

1.注入依賴
//ViewModel與LiveData
implementation "android.arch.lifecycle:extensions:1.1.1"
//圖片加載
implementation 'com.github.bumptech.glide:glide:4.9.0'
//網(wǎng)絡(luò)請(qǐng)求
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
//GSON解析
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'


同時(shí)需要啟用DataBinding
android {
        ..........
    dataBinding {
        enabled = true
    }
}
2.接口請(qǐng)求

https://cn.bing.com/HPImageArchive.aspx?format=js&idx=1&n=1
其中 format為請(qǐng)求格式绰播,包含JSON、XML等,
idx為請(qǐng)求ID,n為每次請(qǐng)求的個(gè)數(shù)
(在AndroidManifest.xml文件內(nèi)添加網(wǎng)絡(luò)權(quán)限)
接口返回參數(shù)樣式如下

{"images":[{"startdate":"20190724","fullstartdate":"201907241600","enddate":"20190725","url":"/th?id=OHR.CathedralMountBuffalo_ZH-CN4341947983_1920x1080.jpg&rf=LaDigue_1920x1080.jpg&pid=hp","urlbase":"/th?id=OHR.CathedralMountBuffalo_ZH-CN4341947983","copyright":"清晨暴雨中的Cathedral,澳大利亞布法羅山 (? Ilya Genkin/Alamy)","copyrightlink":"https://www.bing.com/search?q=Cathedral&form=hpcapt&mkt=zh-cn","title":"","quiz":"/search?q=Bing+homepage+quiz&filters=WQOskey:%22HPQuiz_20190724_CathedralMountBuffalo%22&FORM=HPQUIZ","wp":true,"hsh":"5c4b7b7d11456918494bc1ecea07951c","drk":1,"top":1,"bot":1,"hs":[]}],"tooltips":{"loading":"正在加載...","previous":"上一個(gè)圖像","next":"下一個(gè)圖像","walle":"此圖片不能下載用作壁紙。","walls":"下載今日美圖。僅限用作桌面壁紙件余。"}}

利用AS內(nèi)GsonFormat插件自動(dòng)生成ImageBean實(shí)體類

3.創(chuàng)建基礎(chǔ)接受類BaseData
public class BaseData<T> {
    private T data;
    private String message;

    public BaseData() {
    }

    public BaseData(T data, String message) {
        this.data = data;
        this.message = message;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
4.搭建基礎(chǔ)網(wǎng)絡(luò)請(qǐng)求框架

采用了Retrofit+Rxjava作為網(wǎng)絡(luò)訪問(wèn)框架(都是些基礎(chǔ)使用)

public class NetUtil {

    private Retrofit retrofit;

    public NetUtil() {
        retrofit = new Retrofit.Builder()
                .baseUrl("https://cn.bing.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }

    public interface ImageService {
        @GET("HPImageArchive.aspx")
        Observable<ImageBean> getBingImage(@Query("format") String format,
                                     @Query("idx") int idx,
                                     @Query("n") int n);
    }

    public Observable<ImageBean> getBingImage(String format, int idx, int n) {
        return retrofit.create(ImageService.class).getBingImage(format, idx, n);
    }

}
5.搭建Repository模塊即具體的網(wǎng)絡(luò)請(qǐng)求模塊
public class ImageRepository {

    private static final String TAG = "ImageRepository";

    private NetUtil netUtil = new NetUtil();
    private MutableLiveData<BaseData<ImageBean>> imageBean1 = new MutableLiveData<>();

    private int idx = 1;

    /**
     * 獲取圖片
     */
    public MutableLiveData<BaseData<ImageBean>> getBingImage() {
        netUtil.getBingImage("js", idx, 1)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new Observer<ImageBean>() {
                    @Override
                    public void onSubscribe(Disposable d) {

                    }

                    @Override
                    public void onNext(ImageBean imageBean) {
                        imageBean1.setValue(new BaseData<>(imageBean, null));
                    }

                    @Override
                    public void onError(Throwable e) {

                    }

                    @Override
                    public void onComplete() {

                    }
                });
        return imageBean1;
    }

    /**
     * 獲取下一張圖片
     */
    public MutableLiveData<BaseData<ImageBean>> getNextImage() {
        if (idx > 6) {
            imageBean1.setValue(new BaseData<ImageBean>(null, "已經(jīng)是最后一張啦"));
            return imageBean1;
        }
        ++idx;
        return getBingImage();
    }

    /**
     * 獲取上一張圖片
     */
    public MutableLiveData<BaseData<ImageBean>> getPreviousImage() {
        if (idx < 1) {
            imageBean1.setValue(new BaseData<ImageBean>(null, "已經(jīng)是第一張啦"));
            return imageBean1;
        }
        --idx;
        return getBingImage();
    }
}

即使Repository模塊看起來(lái)沒(méi)有必要,它也有著重要的作用遭居。它從應(yīng)用程序的其余部分提取數(shù)據(jù)源√淦鳎現(xiàn)在我們的ViewModel不知道數(shù)據(jù)是由NetUtil獲取的,這意味著我們可以根據(jù)需要將其交換為其他實(shí)現(xiàn)俱萍。所以下一步就是創(chuàng)建對(duì)應(yīng)的ViewModel

6.創(chuàng)建對(duì)應(yīng)的ViewModel
public class DemoActivityViewModel extends AndroidViewModel {

    private ImageRepository imageRepository ;
     //存放從網(wǎng)絡(luò)上獲取的圖片信息
    private MutableLiveData<BaseData<ImageBean>> imageBean ;

    public DemoActivityViewModel(@NonNull Application application) {
        super(application);
        imageRepository=new ImageRepository();
        imageBean=new MutableLiveData<>();
    }

    public MutableLiveData<BaseData<ImageBean>> getImageBean() {
        return imageBean;
    }

    /**
     * 獲取圖片
     */
    public void getImage() {
        imageBean = imageRepository.getBingImage();
    }

    /**
     * 獲取下一張
     */
    public void getNextImage() {
        imageBean = imageRepository.getNextImage();
    }

    /**
     * 獲取上一張
     */
    public void getPreviousImage() {
        imageBean = imageRepository.getPreviousImage();
    }
}
7.Activity內(nèi)使用
public class DemoActivity extends AppCompatActivity {
    private ActivityDemoBinding activityDemoBinding;
    private DemoActivityViewModel demoActivityViewModel;
    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        activityDemoBinding = DataBindingUtil.setContentView(this, R.layout.activity_demo);
        demoActivityViewModel = ViewModelProviders.of(this).get(DemoActivityViewModel.class);
       //綁定ViewModel
        activityDemoBinding.setViewModel(demoActivityViewModel);
       //綁定點(diǎn)擊事件
        activityDemoBinding.setEventListener(new OnEventListener());
        progressDialog = new ProgressDialog(DemoActivity.this);
        progressDialog.setMessage("加載中端壳。。枪蘑。损谦。");
       //獲取第一張網(wǎng)絡(luò)圖片
        demoActivityViewModel.getImage();
        progressDialog.show();
        demoActivityViewModel.getImageBean().observe(this, new Observer<BaseData<ImageBean>>() {
            @Override
            public void onChanged(BaseData<ImageBean> imageBeanBaseData) {
                if (imageBeanBaseData.getData() != null) {
                    //1.直接代碼賦值
                    Glide.with(DemoActivity.this)
                            .load("https://www.bing.com" + imageBeanBaseData.getData().getImages().get(0).getUrl())
                            .into(activityDemoBinding.ivImg);
                    activityDemoBinding.tvContent.setText(imageBeanBaseData.getData().getImages().get(0).getCopyright());

                    //2.利用DataBinding直接賦值
                    activityDemoBinding.setImage(imageBeanBaseData.getData().getImages().get(0));
                } else {
                    Toast.makeText(DemoActivity.this, imageBeanBaseData.getMessage(), Toast.LENGTH_SHORT).show();
                }
                progressDialog.dismiss();
            }
        });
    }

    public class OnEventListener {
        public void onClick(View view) {
            progressDialog.show();
            switch (view.getId()) {
                case R.id.tv_previous:
                    //獲取上一張
                    demoActivityViewModel.getPreviousImage();
                    break;
                case R.id.tv_next:
                   //獲取下一張
                    demoActivityViewModel.getNextImage();
                    break;
                default:
                    break;
            }
        }
    }

}

與其對(duì)應(yīng)的xml文件為:

<?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"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.example.myapplication.demo.DemoActivityViewModel" />

        <variable
            name="image"
            type="com.example.myapplication.demo.ImageBean.ImagesBean" />

        <variable
            name="eventListener"
            type="com.example.myapplication.demo.DemoActivity.OnEventListener" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".demo.DemoActivity">

        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:gravity="center"
            android:text="Bing每日一圖"
            android:textColor="#000000"
            app:layout_constraintBottom_toTopOf="@id/iv_img" />

        <ImageView
            android:id="@+id/iv_img"
            android:layout_width="match_parent"
            android:layout_height="200dp"
            url="@{"https://www.bing.com"+image.url}"       //此處需自定義BindingAdapter
            app:layout_constraintTop_toBottomOf="@id/tv" />

        <TextView
            android:id="@+id/tv_content"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_margin="15dp"
            android:textColor="#000000"
            android:textSize="15sp"
            android:text="@{image.copyright}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/iv_img" />

        <Button
            android:id="@+id/tv_previous"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_marginStart="15dp"
            android:layout_marginTop="15dp"
            android:gravity="center"
            android:text="上一張"
            android:onClick="@{eventListener::onClick}"
            app:layout_constraintEnd_toStartOf="@id/tv_next"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@id/tv_content" />

        <Button
            android:id="@+id/tv_next"
            android:layout_width="0dp"
            android:layout_height="50dp"
            android:layout_marginStart="15dp"
            android:layout_marginTop="15dp"
            android:layout_marginEnd="15dp"
            android:gravity="center"
            android:text="下一張"
            android:onClick="@{eventListener::onClick}"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@id/tv_previous"
            app:layout_constraintTop_toBottomOf="@id/tv_content" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

在ImageView內(nèi)自定義了一個(gè)url屬性,此時(shí)需使用BindingAdapter去自定義實(shí)現(xiàn)岳颇,實(shí)現(xiàn)起來(lái)很簡(jiǎn)單:

public class DataBindingUtil {
    @BindingAdapter(value = {"url"})
    public static void loadImage(ImageView view, String url) {
        Glide.with(view.getContext()).load(url).into(view);
    }
}

至此一個(gè)很簡(jiǎn)單的MVVM架構(gòu)的小demo就實(shí)現(xiàn)了照捡。

推薦一個(gè)方便在github上閱讀源碼的谷歌瀏覽器插件:Sourcegraph。這個(gè)插件還支持搜索文件话侧、代碼等栗精,反正比正常閱讀要方便太多了。

效果.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瞻鹏,一起剝皮案震驚了整個(gè)濱河市悲立,隨后出現(xiàn)的幾起案子赢赊,更是在濱河造成了極大的恐慌,老刑警劉巖级历,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異叭披,居然都是意外死亡寥殖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門涩蜘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)嚼贡,“玉大人,你說(shuō)我怎么就攤上這事同诫≡敛撸” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵误窖,是天一觀的道長(zhǎng)叮盘。 經(jīng)常有香客問(wèn)我,道長(zhǎng)霹俺,這世上最難降的妖魔是什么柔吼? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮丙唧,結(jié)果婚禮上愈魏,老公的妹妹穿的比我還像新娘。我一直安慰自己想际,他們只是感情好培漏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著胡本,像睡著了一般牌柄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侧甫,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天友鼻,我揣著相機(jī)與錄音,去河邊找鬼闺骚。 笑死彩扔,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的僻爽。 我是一名探鬼主播虫碉,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼胸梆!你這毒婦竟也來(lái)了敦捧?” 一聲冷哼從身側(cè)響起须板,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎兢卵,沒(méi)想到半個(gè)月后习瑰,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡秽荤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年甜奄,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窃款。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡课兄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出晨继,到底是詐尸還是另有隱情烟阐,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布紊扬,位于F島的核電站蜒茄,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏餐屎。R本人自食惡果不足惜扩淀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望啤挎。 院中可真熱鬧驻谆,春花似錦、人聲如沸庆聘。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)伙判。三九已至象对,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間宴抚,已是汗流浹背勒魔。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留菇曲,地道東北人冠绢。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像常潮,于是被迫代替她去往敵國(guó)和親弟胀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 懶得處理樣式了, 將就著看吧. 官網(wǎng)地址: https://developer.android.com/topic...
    Reddington_604e閱讀 1,659評(píng)論 0 1
  • 原文鏈接:http://www.cnblogs.com/zqlxtt/p/6895717.html APP架構(gòu)師整...
    passiontim閱讀 594評(píng)論 0 3
  • Swift1> Swift和OC的區(qū)別1.1> Swift沒(méi)有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,097評(píng)論 1 32
  • mvvm與mvp類似也分為三層 Model 層,主要負(fù)責(zé)數(shù)據(jù)的提供孵户。 Model 層提供業(yè)務(wù)邏輯的數(shù)據(jù)結(jié)構(gòu)(比如萧朝,...
    Chris_Li閱讀 3,363評(píng)論 0 1
  • 今天下午小刺猬、小兔子夏哭、小烏龜正在廣場(chǎng)上讀書(shū)检柬,讀著讀著小刺猬就坐在了小兔子的旁邊,可是身上的刺不小心刺到了...
    徐振恒a閱讀 166評(píng)論 0 0