Android mvvm架构demo(DataBinding+LiveData+ViewModel+ Repository)

1.实现效果

实现页面加载Bing每日一图的功能

2.项目结构

image(忽略没有按分类创建).png

3.实现过程

1.注入依赖
//ViewModel与LiveData
implementation "android.arch.lifecycle:extensions:1.1.1"
//图片加载
implementation 'com.github.bumptech.glide:glide:4.9.0'
//网络请求
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'


同时需要启用DataBinding
android {
        ..........
    dataBinding {
        enabled = true
    }
}
2.接口请求

https://cn.bing.com/HPImageArchive.aspx?format=js&idx=1&n=1
其中 format为请求格式,包含JSON、XML等,
idx为请求ID,n为每次请求的个数
(在AndroidManifest.xml文件内添加网络权限)
接口返回参数样式如下

{"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":"上一个图像","next":"下一个图像","walle":"此图片不能下载用作壁纸。","walls":"下载今日美图。仅限用作桌面壁纸。"}}

利用AS内GsonFormat插件自动生成ImageBean实体类

3.创建基础接受类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.搭建基础网络请求框架

采用了Retrofit+Rxjava作为网络访问框架(都是些基础使用)

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模块即具体的网络请求模块
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, "已经是最后一张啦"));
            return imageBean1;
        }
        ++idx;
        return getBingImage();
    }

    /**
     * 获取上一张图片
     */
    public MutableLiveData<BaseData<ImageBean>> getPreviousImage() {
        if (idx < 1) {
            imageBean1.setValue(new BaseData<ImageBean>(null, "已经是第一张啦"));
            return imageBean1;
        }
        --idx;
        return getBingImage();
    }
}

即使Repository模块看起来没有必要,它也有着重要的作用。它从应用程序的其余部分提取数据源。现在我们的ViewModel不知道数据是由NetUtil获取的,这意味着我们可以根据需要将其交换为其他实现。所以下一步就是创建对应的ViewModel

6.创建对应的ViewModel
public class DemoActivityViewModel extends AndroidViewModel {

    private ImageRepository imageRepository ;
     //存放从网络上获取的图片信息
    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内使用
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);
       //绑定点击事件
        activityDemoBinding.setEventListener(new OnEventListener());
        progressDialog = new ProgressDialog(DemoActivity.this);
        progressDialog.setMessage("加载中。。。。");
       //获取第一张网络图片
        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;
            }
        }
    }

}

与其对应的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内自定义了一个url属性,此时需使用BindingAdapter去自定义实现,实现起来很简单:

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

至此一个很简单的MVVM架构的小demo就实现了。

推荐一个方便在github上阅读源码的谷歌浏览器插件:Sourcegraph。这个插件还支持搜索文件、代码等,反正比正常阅读要方便太多了。

效果.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 懒得处理样式了, 将就着看吧. 官网地址: https://developer.android.com/topic...
    Reddington_604e阅读 1,640评论 0 1
  • 原文链接:http://www.cnblogs.com/zqlxtt/p/6895717.html APP架构师整...
    passiontim阅读 590评论 0 3
  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,084评论 1 32
  • mvvm与mvp类似也分为三层 Model 层,主要负责数据的提供。 Model 层提供业务逻辑的数据结构(比如,...
    Chris_Li阅读 3,360评论 0 1
  • 今天下午小刺猬、小兔子、小乌龟正在广场上读书,读着读着小刺猬就坐在了小兔子的旁边,可是身上的刺不小心刺到了...
    徐振恒a阅读 163评论 0 0