我这里是有数据返回的,通过返回的数据构建一个数据实体,命名为WallPaperResponse,放在model包下,代码如下:
public class WallPaperResponse {
private String msg;
private ResBean res;
private int code;
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public ResBean getRes() {
return res;
}
public void setRes(ResBean res) {
this.res = res;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public static class ResBean {
private List vertical;
public List getVertical() {
return vertical;
}
public void setVertical(List vertical) {
this.vertical = vertical;
}
public static class VerticalBean {
private String preview;
private String thumb;
private String img;
private int views;
private String rule;
private int ncos;
private int rank;
private String source_type;
private String wp;
private boolean xr;
private boolean cr;
private int favs;
private double atime;
private String id;
private String store;
private String desc;
private List cid;
private List<?> tag;
private List<?> url;
public String getPreview() {
return preview;
}
public void setPreview(String preview) {
this.preview = preview;
}
public String getThumb() {
return thumb;
}
public void setThumb(String thumb) {
this.thumb = thumb;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public int getViews() {
return views;
}
public void setViews(int views) {
this.views = views;
}
public String getRule() {
return rule;
}
public void setRule(String rule) {
this.rule = rule;
}
public int getNcos() {
return ncos;
}
public void setNcos(int ncos) {
this.ncos = ncos;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
public String getSource_type() {
return source_type;
}
public void setSource_type(String source_type) {
this.source_type = source_type;
}
public String getWp() {
return wp;
}
public void setWp(String wp) {
this.wp = wp;
}
public boolean isXr() {
return xr;
}
public void setXr(boolean xr) {
this.xr = xr;
}
public boolean isCr() {
return cr;
}
public void setCr(boolean cr) {
this.cr = cr;
}
public int getFavs() {
return favs;
}
public void setFavs(int favs) {
this.favs = favs;
}
public double getAtime() {
return atime;
}
public void setAtime(double atime) {
this.atime = atime;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getStore() {
return store;
}
public void setStore(String store) {
this.store = store;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
public List getCid() {
return cid;
}
public void setCid(List cid) {
this.cid = cid;
}
public List<?> getTag() {
return tag;
}
public void setTag(List<?> tag) {
this.tag = tag;
}
public List<?> getUrl() {
return url;
}
public void setUrl(List<?> url) {
this.url = url;
}
}
}
}
这个API的地址和必应明显是两个地址,那么我们就需要对第二篇文章中所写的网络框架做一些修改,首先我们修改NetworkApi中的代码。
将BASE_URL的默认值改成null,并去掉final关键字,然后我们在NetworkApi中增加一个方法,代码如下:
/**
-
设置访问Url类型
-
@param type 0 必应 1 壁纸列表
*/
private static void setUrlType(int type){
switch (type) {
case 0:
//必应
BASE_URL = “https://cn.bing.com”;
break;
case 1:
//热门壁纸
BASE_URL = “http://service.picasso.adesk.com”;
break;
default:break;
}
}
这里根据输入的类型使用不同的网络服务器地址,然后再修改createService方法,修改后如下:
public static T createService(Class serviceClass, int type) {
//设置Url类型
setUrlType(type);
return getRetrofit(serviceClass).create(serviceClass);
}
那么我们就同样需要修改代码中调用了createService方法的地方,在MainRepository中
这样就可以了,这样做的好处就在于我们既增加了访问API的可拓展性,同时易于修改,还不会对你之前的网络请求很影响。
对于之前的内容改动目前就这些了,下面需要增加新的接口了。在ApiService中增加如下接口。
/**
- 热门壁纸
*/
@GET(“/v1/vertical/vertical?limit=30&skip=180&adult=false&first=0&order=hot”)
Observable wallPaper();
接口有了,下面就是访问的事情了,现在主页面有点太空旷了,所以找个接口的数据访问依然可以在MainRepository中进行请求。打开MainRepository,在里面增加如下代码:
/**
- 热门壁纸数据
*/
final MutableLiveData wallPaper = new MutableLiveData<>();
/**
-
获取壁纸数据
-
@return wallPaper
*/
public LiveData getWallPaper() {
NetworkApi.createService(ApiService.class,1).
wallPaper().compose(NetworkApi.applySchedulers(new BaseObserver() {
@Override
public void onSuccess(WallPaperResponse wallPaperResponse) {
KLog.e("WallPaper: " + new Gson().toJson(wallPaperResponse));
wallPaper.setValue(wallPaperResponse);
}
@Override
public void onFailure(Throwable e) {
KLog.e("WallPaper Error: " + e.toString());
}
}));
return wallPaper;
}
然后进入到MainViewModel中,在里面增加如下代码:
public LiveData wallPaper;
public void getWallPaper() { wallPaper = new MainRepository().getWallPaper(); }
现在访问接口数据这一块就搞定了,下面就是显示出来就可以了。
因为返回的数据比较多,因此通过RecyclerView来进行显示,作为壁纸显示可以通过更改布局管理器,把列表变成纵向两列的形式去显示,首先我们先修改activity_main.xml的布局代码,如下图所示
这里我去掉了页面的居中布局,然后增加了一个RecyclerView,添加了一个id,同事改了一下CustomImageView的scaleType=“fitXY”,这样可以让我们的壁纸完整呈现出来。
这里我需要修改一下CustomImageView类的代码:
其实就是改它所继承的父类,为什么要这么改呢?现在就来说明一下。下面我们写一个列表适配器的item布局,在layout下新建一个item_wall_paper.xml文件,里面的代码我们先不写,先去写一个样式,在themes.xml文件中(老版本的AS中是styles.xml),增加如下样式代码:
这里是设置一个圆角图片的样式代码,那么怎么去使用它呢,下面我们修改一下item_wall_paper.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”>
<variable
name=“wallPaper”
type=“com.llw.mvvm.model.WallPaperResponse.ResBean.VerticalBean” />
<LinearLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:orientation=“vertical”>
<com.llw.mvvm.view.CustomImageView
networkUrl=“@{wallPaper.img}”
android:layout_width=“match_parent”
android:layout_height=“300dp”
android:layout_margin=“5dp”
android:scaleType=“centerCrop”
app:shapeAppearanceOverlay=“@style/roundedImageStyle” />
这里依然是使用DataBinding,因为我们数据是要显示在列表上的,因此直接绑定item就可以了,然后这里我用的是networkUrl的属性,因为你如果使用了biyingUrl会添加一个前缀,而这个API不需要前缀,同时我把刚才写的样式设置了进来,这里就解释了为什么要更改继承的父类,因为之前的那个父类没有这个属性值,这个属性值可以让你的Image图片做很多的形状上的变化,相当Nice! 这样就不用再去导入其他的依赖库或者使用自定义View了,这得力于Material,关于ShapeableImageView更多的介绍可以看一下这一篇文章:Android Material UI控件之ShapeableImageView,如果你感兴趣的话。
好了回到正题,那就是我们现在布局都已经写好了,下面写一个适配器,在com.llw.mvvm包下新建一个adapter包,adapter包下新建一个WallPaperAdapter类,里面的代码如下:
public class WallPaperAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
/**
- 传递过来的数据
*/
private final List<WallPaperResponse.ResBean.VerticalBean> verticalBeans;
public WallPaperAdapter(List<WallPaperResponse.ResBean.VerticalBean> verticalBeans) {
this.verticalBeans = verticalBeans;
}
@NonNull
@NotNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull @NotNull ViewGroup parent, int viewType) {
ItemWallPaperBinding binding = DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()), R.layout.item_wall_paper, parent, false);
return new ViewHolderWallPaper(binding);
}
@Override
public void onBindViewHolder(@NonNull @NotNull RecyclerView.ViewHolder holder, int position) {
ItemWallPaperBinding binding = ((ViewHolderWallPaper) holder).getBinding();
binding.setWallPaper(verticalBeans.get(position));
binding.executePendingBindings();
}
@Override
public int getItemCount() {
return verticalBeans.size();
}
private static class ViewHolderWallPaper extends RecyclerView.ViewHolder {
private ItemWallPaperBinding binding;
public ItemWallPaperBinding getBinding() {
return binding;
}
public void setBinding(ItemWallPaperBinding binding) {
this.binding = binding;
}
public ViewHolderWallPaper(ItemWallPaperBinding inflate) {
super(inflate.getRoot());
this.binding = inflate;
}
}
}
这就是RecyclerView.Adapter的常规使用而已,很简单,其中要注意的就是DataBinding的使用,这个很关键了,它决定了你的数据与xml绑定。下面我们回到MainActivity中,首先增加一个initView()方法,里面的代码如下:
/**
- 初始化
*/
private void initView() {
GridLayoutManager manager = new GridLayoutManager(this, 2);
dataBinding.rv.setLayoutManager(manager);
}
然后在onCreate方法中调用它并且实现数据更新的回调监听。
initView();
//热门壁纸 网络请求
mainViewModel.getWallPaper();
mainViewModel.wallPaper.observe(this, wallPaperResponse -> dataBinding.rv.setAdapter(new WallPaperAdapter(wallPaperResponse.getRes().getVertical())));
下面我们可以开始运行了,效果图如下:
由于这个平台的限制,所以这个动图是标清,建议读者可以直接安装APK去体验,这样会更舒服一些。这个图片展示的效果就很不错,现在我们已经掌握了怎么在MVVM中使用RecyclerView。
当我们需要点击查看图片的时候,就需要先绑定点击事件,然后查看图片,在适配器WallPaperAdapter中增加一个ClickBinding内部类,里面的代码如下:
public static class ClickBinding {
public void itemClick(WallPaperResponse.ResBean.VerticalBean verticalBean, View view) {
Intent intent = new Intent(view.getContext(), PictureViewActivity.class);
intent.putExtra(“img”, verticalBean.getImg());
view.getContext().startActivity(intent);
}
}
这里点击之后是跳转到PictureViewActivity,等会去创建它。
然后修改一下布局item_wall_paper.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”>
<variable
name=“wallPaper”
type=“com.llw.mvvm.model.WallPaperResponse.ResBean.VerticalBean” />
<variable
name=“onClick”
type=“com.llw.mvvm.adapter.WallPaperAdapter.ClickBinding” />
<LinearLayout
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:orientation=“vertical”>
<com.llw.mvvm.view.CustomImageView
android:id=“@+id/image”
networkUrl=“@{wallPaper.img}”
android:layout_width=“match_parent”
android:layout_height=“300dp”
android:layout_margin=“5dp”
android:onClick=“@{() -> onClick.itemClick(wallPaper,image)}”
android:scaleType=“centerCrop”
app:shapeAppearanceOverlay=“@style/roundedImageStyle” />
适配器布局修改好了之后,再回到WallPaperAdapter中,在onBindViewHolder方法中添加对点击事件的绑定,修改代码如下图如下:
然后新增一个PictureViewActivity,对应的布局activity_picture_view.xml代码如下:
<?xml version="1.0" encoding="utf-8"?><androidx.constraintlayout.widget.ConstraintLayout 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”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=“.PictureViewActivity”>
<ImageView
android:id=“@+id/image”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
app:layout_constraintBottom_toBottomOf=“parent”
app:layout_constraintLeft_toLeftOf=“parent”
app:layout_constraintRight_toRightOf=“parent”
app:layout_constraintTop_toTopOf=“parent” />
</androidx.constraintlayout.widget.ConstraintLayout>
然后修改一下PictureViewActivity的代码,在onCreate方法中增加如下代码。
String img = getIntent().getStringExtra(“img”);
if (img != null) {
ImageView imageView = findViewById(R.id.image);
Glide.with(this).load(img).into(imageView);
}
运行效果如下所示:
好的,下面对主页面进行一下美化,现在这个样子确实不好看。
在页面中默认的ActionBar占了无用的控件,我们可以自定义一个样式去替换当前页面的样式,在themes.xml下增加如下代码:
然后修改AndroidManifest.xml中的代码:
设置theme,然后修改activity_main.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”>
<variable
name=“viewModel”
type=“com.llw.mvvm.viewmodels.MainViewModel” />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:fitsSystemWindows=“true”
tools:context=“.MainActivity”>
<com.google.android.material.appbar.AppBarLayout
android:id=“@+id/appbar_layout”
android:layout_width=“match_parent”
android:layout_height=“200dp”
android:fitsSystemWindows=“true”
android:theme=“@style/ThemeOverlay.AppCompat.Dark.ActionBar”>
<com.google.android.material.appbar.CollapsingToolbarLayout
android:id=“@+id/toolbar_layout”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:fitsSystemWindows=“true”
app:collapsedTitleGravity=“center_horizontal”
app:contentScrim=“@color/purple_500”
app:layout_scrollFlags=“scroll|exitUntilCollapsed”
app:title=“MVVM”
app:toolbarId=“@+id/toolbar”>
<com.llw.mvvm.view.CustomImageView
android:id=“@+id/image”
biyingUrl=“@{viewModel.biying.images.get(0).url}”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:fitsSystemWindows=“true”
android:scaleType=“fitXY” />
<androidx.appcompat.widget.Toolbar
android:id=“@+id/toolbar”
android:layout_width=“match_parent”
android:layout_height=“?attr/actionBarSize”
app:contentInsetEnd=“0dp”
app:contentInsetStart=“0dp”
app:layout_collapseMode=“pin”
app:layout_scrollFlags=“scroll|snap” />
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:fillViewport=“true”
android:orientation=“vertical”
android:overScrollMode=“never”
app:layout_behavior=“@string/appbar_scrolling_view_behavior”>
<androidx.recyclerview.widget.RecyclerView
android:id=“@+id/rv”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:padding=“5dp” />
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
然后修改MainActivity中的代码,在initView中增加如下代码:
//伸缩偏移量监听
dataBinding.appbarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
boolean isShow = true;
int scrollRange = -1;
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (scrollRange == -1) {
scrollRange = appBarLayout.getTotalScrollRange();
}
if (scrollRange + verticalOffset == 0) {//收缩时
dataBinding.toolbarLayout.setTitle(“MVVM-Demo”);
isShow = true;
} else if (isShow) {//展开时
dataBinding.toolbarLayout.setTitle(“”);
isShow = false;
}
}
});
运行效果如下:
这样的效果如何呢?不管怎么说比之前的页面好看吧,而且这个用户体验会比较的好。
这里我们依然可以保存热门壁纸的数据,这样可以再第二次打开当前页面的时候使我们的加载效率提高很多,这里我们需要对数据库进行一次升级迁移。
1. Entity
在bean包下新增一个WallPaper类,里面的代码如下:
@Entity(tableName = “wallpaper”)
public class WallPaper {
@PrimaryKey(autoGenerate = true)
private int uid = 0;
private String img;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getImg() {
return img;
}
public void setImg(String img) {
this.img = img;
}
public WallPaper() {}
@Ignore
public WallPaper(String img) {
this.img = img;
}
}
这里我设置了id字段自增,同时我设置了表名为:wallpaper。
2. Dao
在dao包下新建一个WallPaperDao接口,里面的代码如下:
@Dao
public interface WallPaperDao {
@Query(“SELECT * FROM wallpaper”)
Flowable<List> getAll();
@Insert(onConflict = OnConflictStrategy.REPLACE)
Completable insertAll(List wallPapers);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/32ee3c08f264f39f3441dfc34c75dec7.jpeg)
结尾
最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。
首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)
泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter
接下来是资料清单:(敲黑板!!!)
1.数据结构和算法
2.设计模式
3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记
4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)
不论遇到什么困难,都不应该成为我们放弃的理由!共勉~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
droid移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-wgiHUkea-1712613699009)]
[外链图片转存中…(img-kUJmh4qG-1712613699010)]
[外链图片转存中…(img-ee6JsRCO-1712613699010)]
[外链图片转存中…(img-lprxH489-1712613699010)]
[外链图片转存中…(img-0fnjP3mZ-1712613699011)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/32ee3c08f264f39f3441dfc34c75dec7.jpeg)
结尾
最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。
首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)
泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter
[外链图片转存中…(img-n3zSy8Wq-1712613699011)]
接下来是资料清单:(敲黑板!!!)
1.数据结构和算法
[外链图片转存中…(img-Lo6Id0KK-1712613699011)]
2.设计模式
[外链图片转存中…(img-VDTYkVsu-1712613699012)]
3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记
[外链图片转存中…(img-ckAYynto-1712613699012)]
4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)
[外链图片转存中…(img-zvBePNSm-1712613699012)]
不论遇到什么困难,都不应该成为我们放弃的理由!共勉~
如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。
[外链图片转存中…(img-4tBB6U8Q-1712613699012)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!