.fallback(R.drawable.wallpaper_bg) //url为空的时候,显示的图片
.error(R.mipmap.ic_loading_failed);//图片加载失败后,显示的图片
将这个值配置进去,如下图所示:
下面我们运行一下看是什么效果。
效果还可以的,下面进入主页面的代码编写。
当到了每日壁纸页面时,我们需要再提供一个入口可以进入下一个页面,现在的每日壁纸页面不能算是真正意义上的主页面,因此我们写一个入口,可以在MainActivity中增加一个浮动按钮,页面上下滑动时控制按钮的显示和消失。下面在activity_main.xml中增加如下布局代码:
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id=“@+id/fab_home”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_gravity=“end|bottom”
android:layout_margin=“20dp”
android:background=“@color/purple_500”
android:onClick=“toHome”
android:src=“@mipmap/ic_home”
app:backgroundTint=“@color/purple_500”
app:fabSize=“auto”
tools:ignore=“UsingOnClickInXml”
android:contentDescription=“主页” />
添加的位置如下,这里的图标到我的源码里拿就好,白色的放出来也看不见。
下面回到MainActivity中,继承BaseActivity,在initView方法中增加如下代码:
//页面上下滑动监听
dataBinding.scrollView.setOnScrollChangeListener((NestedScrollView.OnScrollChangeListener) (v, scrollX, scrollY, oldScrollX, oldScrollY) -> {
if (scrollY > oldScrollY) {
//上滑
dataBinding.fabHome.hide();
} else {
//下滑
dataBinding.fabHome.show();
}
});
然后也增加一个方法,当点击时跳转到HomeActivity,我们将在这个HomeActivity中显示Fragment,现在还没有,下面会创建的。
public void toHome(View view) {
jumpActivity(HomeActivity.class);
}
代码添加位置如下图所示:
下面在activity包下创建一个HomeActivity,对应的布局是activity_home.xml,在改动之前我们先做好准备的工作。
在res包下新建一个navigation包,包下新建一个nav_graph.xml,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><navigation 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:id=“@+id/nav_graph”
app:startDestination=“@id/news_fragment”>
<fragment
android:id=“@+id/news_fragment”
android:name=“com.llw.mvvm.ui.fragment.NewsFragment”
android:label=“news_fragment”
tools:layout=“@layout/news_fragment” />
<fragment
android:id=“@+id/video_fragment”
android:name=“com.llw.mvvm.ui.fragment.VideoFragment”
android:label=“video_fragment”
tools:layout=“@layout/video_fragment” />
这里就是将Fragment配置到Navigation中,app:startDestination表示显示的第一个Fragment。那么这一步就完成了,下面是另一个操作,就是通过点击底部导航栏菜单去进行Fragment的切换。我们在res下新建一个menu包,包下新建一个navigation_menu.xml,里面的代码如下:
<?xml version="1.0" encoding="utf-8"?><item
android:id=“@+id/news_fragment”
android:icon=“@mipmap/ic_hot_news”
android:title=“新闻” />
<item
android:id=“@+id/video_fragment”
android:icon=“@mipmap/ic_hot_video”
android:title=“视频” />
这里有两个图标,同样是白色的。
下面我们回到activity_home.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”>
<RelativeLayout
android:layout_width=“match_parent”
android:layout_height=“match_parent”
tools:context=“.ui.activity.HomeActivity”>
<com.google.android.material.appbar.MaterialToolbar
android:id=“@+id/toolbar”
android:layout_width=“match_parent”
android:layout_height=“?attr/actionBarSize”
android:background=“@color/purple_500”>
<TextView
android:id=“@+id/tv_title”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_gravity=“center”
android:text=“头条新闻”
android:textColor=“@color/white”
android:textSize=“18sp”
android:textStyle=“bold” />
</com.google.android.material.appbar.MaterialToolbar>
<fragment
android:id=“@+id/nav_host_fragment”
android:name=“androidx.navigation.fragment.NavHostFragment”
android:layout_width=“match_parent”
android:layout_height=“match_parent”
android:layout_above=“@+id/bottom_navigation”
android:layout_below=“@+id/toolbar”
app:navGraph=“@navigation/nav_graph” />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id=“@+id/bottom_navigation”
android:layout_width=“match_parent”
android:layout_height=“?attr/actionBarSize”
android:layout_alignParentBottom=“true”
android:background=“#FFF”
app:menu=“@menu/navigation_menu” />
这里分为三个部分,一个是标题栏、一个是装载Fragment的容器,另一个是控制Fragment切换的。
下面我们进入到HomeActivity页面,修改代码如下:
public class HomeActivity extends BaseActivity {
private ActivityHomeBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_home);
initView();
}
/**
- 初始化
*/
private void initView() {
//获取navController
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
binding.bottomNavigation.setOnNavigationItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.news_fragment:
binding.tvTitle.setText(“头条新闻”);
navController.navigate(R.id.news_fragment);
break;
case R.id.video_fragment:
binding.tvTitle.setText(“热门视频”);
navController.navigate(R.id.video_fragment);
break;
default:
}
return true;
});
}
}
下面在fragment包下创建一个BaseFragment,里面的代码如下:
public class BaseFragment extends Fragment {
protected AppCompatActivity context;
@Override
public View onCreateView(@NonNull @NotNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onViewCreated(@NonNull @NotNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
@Override
public void onAttach(@NonNull @NotNull Context context) {
super.onAttach(context);
if(context instanceof AppCompatActivity){
this.context = (AppCompatActivity) context;
}
}
@Override
public void onDetach() {
super.onDetach();
context = null;
}
protected void showMsg(String msg) {
Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();
}
}
然后修改NewsFragment中的代码:
public class NewsFragment extends BaseFragment {
private NewsFragmentBinding binding;
public static NewsFragment newInstance() {
return new NewsFragment();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.news_fragment, container, false);
return binding.getRoot();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
NewsViewModel mViewModel = new ViewModelProvider(this).get(NewsViewModel.class);
}
}
再修改VideoFragment的代码:
public class VideoFragment extends BaseFragment {
private VideoFragmentBinding binding;
public static VideoFragment newInstance() {
return new VideoFragment();
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.video_fragment, container, false);
return binding.getRoot();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
VideoViewModel mViewModel = new ViewModelProvider(this).get(VideoViewModel.class);
}
}
这里我们在点击底部导航栏时切换Fragment并且更改一下标题栏的文字,下面运行一下。
详细的使用说明可以看看这篇文章:Android Navigation + Fragment 制作APP主页面导航(步骤 + 源码),看完后你了解的也许会更多。
这里我们使用聚合的API数据,聚合API,点击进入完成注册登录,然后可以申请数据API。
申请免费的API,每天有一百次请求,因此我们可以把数据请求一次之后保存到本地数据库中。
① NetworkApi
这两个API的接口是不同的地址,修改一下NetworkApi中的setUrlType方法。
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;
case 2:
//聚合API 1
BASE_URL = “http://v.juhe.cn”;
break;
case 3:
//聚合API 2
BASE_URL = “http://apis.juhe.cn”;
break;
default:
break;
}
}
这里两个接口分别是用于请求新闻数据和视频数据的。
② ApiService
在ApiService中增加两个接口,代码如下所示:
/**
- 聚合新闻数据
*/
@GET(“/toutiao/index?type=&page=&page_size=&is_filter=&key=99d3951ed32af2930afd9b38293a08a2”)
Observable news();
/**
- 聚合热门视频数据
*/
@GET(“/fapig/douyin/billboard?type=hot_video&size=20&key=a9c49939cae34fc7dae570b1a4824be4”)
Observable video();
针对这个情况我们同样需要对数据库进行一次升级,这一次我们增加两个表。
③ 数据库升级
首先在bean包下新建两个实体,News和Video。里面的内容都是我根据接口返回的数据制作的,News里的代码如下:
@Entity(tableName = “news”)
public class News {
@PrimaryKey(autoGenerate = true)
private int uid;
private String uniquekey;
private String title;
private String date;
private String category;
private String author_name;
private String url;
private String thumbnail_pic_s;
private String is_content;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getUniquekey() {
return uniquekey;
}
public void setUniquekey(String uniquekey) {
this.uniquekey = uniquekey;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public String getAuthor_name() {
return author_name;
}
public void setAuthor_name(String author_name) {
this.author_name = author_name;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getThumbnail_pic_s() {
return thumbnail_pic_s;
}
public void setThumbnail_pic_s(String thumbnail_pic_s) {
this.thumbnail_pic_s = thumbnail_pic_s;
}
public String getIs_content() {
return is_content;
}
public void setIs_content(String is_content) {
this.is_content = is_content;
}
public News() {}
@Ignore
public News(String uniquekey, String title, String date, String category, String author_name, String url, String thumbnail_pic_s, String is_content) {
this.uniquekey = uniquekey;
this.title = title;
this.date = date;
this.category = category;
this.author_name = author_name;
this.url = url;
this.thumbnail_pic_s = thumbnail_pic_s;
this.is_content = is_content;
}
}
Video的代码如下:
@Entity(tableName = “video”)
public class Video {
@PrimaryKey(autoGenerate = true)
private int uid;
private String title;
private String share_url;
private String author;
private String item_cover;
private String hot_words;
public int getUid() {
return uid;
}
public void setUid(int uid) {
this.uid = uid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getShare_url() {
return share_url;
}
public void setShare_url(String share_url) {
this.share_url = share_url;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getItem_cover() {
return item_cover;
}
public void setItem_cover(String item_cover) {
this.item_cover = item_cover;
}
public String getHot_words() {
return hot_words;
}
public void setHot_words(String hot_words) {
this.hot_words = hot_words;
}
@Ignore
public Video(String title, String share_url, String author, String item_cover, String hot_words) {
this.title = title;
this.share_url = share_url;
this.author = author;
this.item_cover = item_cover;
this.hot_words = hot_words;
}
public Video() {}
}
然后是Dao类,在dao包下新建一个NewsDao和VideoDao的接口,NewsDao代码如下:
@Dao
public interface NewsDao {
@Query(“SELECT * FROM news”)
Flowable<List> getAll();
@Insert(onConflict = OnConflictStrategy.REPLACE)
Completable insertAll(List news);
@Query(“DELETE FROM news”)
Completable deleteAll();
}
VideoDao代码如下:
@Dao
public interface VideoDao {
@Query(“SELECT * FROM video”)
Flowable<List> getAll();
@Insert(onConflict = OnConflictStrategy.REPLACE)
Completable insertAll(List videos);
@Query(“DELETE FROM video”)
Completable deleteAll();
}
最后我们进入AppDatabase中,对数据库进行升级迁移,在AppDatabase中新增如下代码:
/**
- 版本升级迁移到3 新增新闻表和视频表
*/
static final Migration MIGRATION_2_3 = new Migration(2, 3) {
@Override
public void migrate(SupportSQLiteDatabase database) {
//创建新闻表
database.execSQL("CREATE TABLE news
" +
"(uid INTEGER NOT NULL, " +
"uniquekey TEXT, " +
"title TEXT, " +
“date TEXT,” +
“category TEXT,” +
“author_name TEXT,” +
“url TEXT,” +
“thumbnail_pic_s TEXT,” +
“is_content TEXT,” +
“PRIMARY KEY(uid
))”);
//创建视频表
database.execSQL("CREATE TABLE video
" +
"(uid INTEGER NOT NULL, " +
“title TEXT,” +
“share_url TEXT,” +
“author TEXT,” +
“item_cover TEXT,” +
“hot_words TEXT,” +
“PRIMARY KEY(uid
))”);
}
};
然后再增加两个抽象方法,就是之前的两个数据操作类,我们这样写了之后通过Room的编译时技术会对这两个抽象类中的接口进行一个实现,不需要我们去管它。
public abstract NewsDao newsDao();
public abstract VideoDao videoDao();
下面就是对数据库进行升级了,如下图所示:
注意我标注的地方,少一个都会出现升级不成功,或者你直接都编译不成功或者程序运行闪退的情况。
现在我们的数据库有了,接下来要做的就是数据的或者和保存了。
④ 数据存储库
下面就是Repository了,我们在repository包下新建NewsRspository和VideoRepository两个类,然后为了方便管理数据的请求方式,我们同样需要在Constant中增加几个常量来保存当天是否有请求网络接口数据,在Constant中增加如下代码:
/**
- 今日是否请求了聚合新闻数据
*/
public static final String IS_TODAY_REQUEST_NEWS = “isTodayRequestNews”;
/**
- 今日请求聚合新闻数据的时间戳
*/
public static final String REQUEST_TIMESTAMP_NEWS = “newsRequestTimestamp”;
/**
- 今日是否请求了聚合视频数据
*/
public static final String IS_TODAY_REQUEST_VIDEO = “isTodayRequestVideo”;
/**
- 今日请求聚合视频数据的时间戳
*/
public static final String REQUEST_TIMESTAMP_VIDEO = “videoRequestTimestamp”;
然后我们再来编辑NewsRepository的代码:
@SuppressLint(“CheckResult”)
public class NewsRepository {
private static final String TAG = NewsRepository.class.getSimpleName();
final MutableLiveData news = new MutableLiveData<>();
public final MutableLiveData failed = new MutableLiveData<>();
/**
-
获取新闻数据
-
@return news
*/
public MutableLiveData getNews() {
//今日此接口是否已经请求
if (MVUtils.getBoolean(Constant.IS_TODAY_REQUEST_NEWS)) {
if (DateUtil.getTimestamp() <= MVUtils.getLong(Constant.REQUEST_TIMESTAMP_NEWS)) {
getNewsForLocalDB();
} else {
getNewsForNetwork();
}
} else {
getNewsForNetwork();
}
return news;
}
/**
- 从本地数据库获取新闻
*/
private void getNewsForLocalDB() {
Log.d(TAG, “getNewsForLocalDB: 从本地数据库获取 新闻数据”);
NewsResponse newsResponse = new NewsResponse();
NewsResponse.ResultBean resultBean = new NewsResponse.ResultBean();
List<NewsResponse.ResultBean.DataBean> dataBeanList = new ArrayList<>();
Flowable<List> listFlowable = BaseApplication.getDb().newsDao().getAll();
CustomDisposable.addDisposable(listFlowable, newss -> {
for (News news1 : newss) {
NewsResponse.ResultBean.DataBean dataBean = new NewsResponse.ResultBean.DataBean();
dataBean.setUniquekey(news1.getUniquekey());
dataBean.setTitle(news1.getTitle());
dataBean.setDate(news1.getDate());
dataBean.setAuthor_name(news1.getAuthor_name());
dataBean.setCategory(news1.getCategory());
dataBean.setThumbnail_pic_s(news1.getThumbnail_pic_s());
dataBean.setIs_content(news1.getIs_content());
dataBeanList.add(dataBean);
}
resultBean.setData(dataBeanList);
newsResponse.setResult(resultBean);
news.postValue(newsResponse);
});
}
/**
- 从网络获取壁纸数据
*/
private void getNewsForNetwork() {
Log.d(TAG, “getNewsForNetwork: 从网络获取 热门壁纸”);
NetworkApi.createService(ApiService.class, 2).
news().compose(NetworkApi.applySchedulers(new BaseObserver() {
@Override
public void onSuccess(NewsResponse newsResponse) {
if (newsResponse.getError_code() == 0) {
//保存本地数据
saveNews(newsResponse);
news.setValue(newsResponse);
} else {
failed.postValue(newsResponse.getReason());
}
}
@Override
public void onFailure(Throwable e) {
failed.postValue("News Error: " + e.toString());
}
}));
}
/**
- 保存热门壁纸数据
*/
private void saveNews(NewsResponse newsResponse) {
MVUtils.put(Constant.IS_TODAY_REQUEST_NEWS, true);
MVUtils.put(Constant.REQUEST_TIMESTAMP_NEWS, DateUtil.getMillisNextEarlyMorning());
Completable deleteAll = BaseApplication.getDb().newsDao().deleteAll();
CustomDisposable.addDisposable(deleteAll, () -> {
Log.d(TAG, “saveNews: 删除数据成功”);
List newsList = new ArrayList<>();
for (NewsResponse.ResultBean.DataBean dataBean : newsResponse.getResult().getData()) {
newsList.add(new News(dataBean.getUniquekey(),dataBean.getTitle(),dataBean.getDate(),dataBean.getCategory(),
dataBean.getAuthor_name(),dataBean.getUrl(),dataBean.getThumbnail_pic_s(),dataBean.getIs_content()));
}
//保存到数据库
Completable insertAll = BaseApplication.getDb().newsDao().insertAll(newsList);
Log.d(TAG, “saveNews: 插入数据:” + newsList.size() + “条”);
//RxJava处理Room数据存储
CustomDisposable.addDisposable(insertAll, () -> Log.d(TAG, “saveNews: 新闻数据保存成功”));
});
}
}
这里的代码和之前WallPaperRepository中的代码神似,逻辑上基本一致,只不过是不同的接口和不同的数据表,同事我在当前的这个Repository中增加了一个异常信息的LiveData,因为请求接口你可能会需要很多情况,最理想的时能获取到数据,但是也有其他情况,例如接口地址错误访问不到、请求返回的数据为空,请求次数达到上限等一些异常。因此我们有必要做一个异常信息的处理,然后传递到ViewModel中,最终在Activity中对这个异常进行观察,及时通知到页面上。不然我光打印日志,用户是看不到的。
VideoRepository的代码如下:
@SuppressLint(“CheckResult”)
public class VideoRepository {
public static final String TAG = VideoRepository.class.getSimpleName();
final MutableLiveData video = new MutableLiveData<>();
public final MutableLiveData failed = new MutableLiveData<>();
/**
-
获取视频数据
-
@return video
*/
public MutableLiveData getVideo() {
//今日此接口是否已经请求
if (MVUtils.getBoolean(Constant.IS_TODAY_REQUEST_VIDEO)) {
if (DateUtil.getTimestamp() <= MVUtils.getLong(Constant.REQUEST_TIMESTAMP_VIDEO)) {
getVideoForLocalDB();
} else {
getVideoForNetwork();
}
} else {
getVideoForNetwork();
}
return video;
}
/**
- 从本地数据库获取新闻
*/
private void getVideoForLocalDB() {
Log.d(TAG, “getVideoForLocalDB: 从本地数据库获取 视频数据”);
VideoResponse videoResponse = new VideoResponse();
List<VideoResponse.ResultBean> dataBeanList = new ArrayList<>();
Flowable<List> listFlowable = BaseApplication.getDb().videoDao().getAll();
CustomDisposable.addDisposable(listFlowable, videos -> {
for (Video video : videos) {
VideoResponse.ResultBean resultBean = new VideoResponse.ResultBean();
resultBean.setTitle(video.getTitle());
resultBean.setShare_url(video.getShare_url());
resultBean.setAuthor(video.getAuthor());
resultBean.setHot_words(video.getHot_words());
resultBean.setItem_cover(video.getItem_cover());
dataBeanList.add(resultBean);
}
videoResponse.setResult(dataBeanList);
video.postValue(videoResponse);
});
}
/**
- 从网络获取壁纸数据
*/
private void getVideoForNetwork() {
Log.d(TAG, “getVideoForNetwork: 从网络获取 热门壁纸”);
NetworkApi.createService(ApiService.class, 3)
.video().compose(NetworkApi.applySchedulers(new BaseObserver() {
@Override
public void onSuccess(VideoResponse videoResponse) {
if (videoResponse.getError_code() == 0) {
//保存本地数据
saveVideo(videoResponse);
video.postValue(videoResponse);
} else {
failed.postValue(videoResponse.getReason());
}
}
@Override
public void onFailure(Throwable e) {
failed.postValue("Video Error: " + e.toString());
}
}));
}
/**
- 保存热门壁纸数据
*/
private void saveVideo(VideoResponse videoResponse) {
MVUtils.put(Constant.IS_TODAY_REQUEST_VIDEO, true);
MVUtils.put(Constant.REQUEST_TIMESTAMP_VIDEO, DateUtil.getMillisNextEarlyMorning());
Completable deleteAll = BaseApplication.getDb().videoDao().deleteAll();
CustomDisposable.addDisposable(deleteAll, () -> {
Log.d(TAG, “saveVideo: 删除数据成功”);
List videoList = new ArrayList<>();
for (VideoResponse.ResultBean resultBean : videoResponse.getResult()) {
videoList.add(new Video(resultBean.getTitle(),resultBean.getShare_url(),resultBean.getAuthor(),
resultBean.getItem_cover(), resultBean.getHot_words()));
}
//保存到数据库
Completable insertAll = BaseApplication.getDb().videoDao().insertAll(videoList);
Log.d(TAG, “saveVideo: 插入数据:” + videoList.size() + “条”);
//RxJava处理Room数据存储
CustomDisposable.addDisposable(insertAll, () -> Log.d(TAG, “saveVideo: 视频数据保存成功”));
});
}
}
这里面的代码也是类似的。如果我们每一个ViewModel中都要有一个failed,那么我们可以定义一个基础ViewModel,然后所有的ViewModel去继承它,这样就会更好一些。
⑤ BaseViewModel
在viewmodels包下新建一个BaseViewModel,里面的代码如下:
public class BaseViewModel extends ViewModel {
public LiveData failed;
}
哦豁,就这么点代码吗?是的,目前就这些,可以根据实际的需求后面再进行添加,不着急。下面我们修改一下NewsViewModel中的代码,如下所示:
public class NewsViewModel extends BaseViewModel {
public LiveData news;
public void getNews() {
NewsRepository newsRepository = new NewsRepository();
failed = newsRepository.failed;
news = newsRepository.getNews();
}
}
然后再修改一下VideoViewModel的代码:
public class VideoViewModel extends BaseViewModel {
public LiveData video;
public void getVideo() {
VideoRepository videoRepository = new VideoRepository();
failed = videoRepository.failed;
video = videoRepository.getVideo();
}
}
另外其他的ViewModel也这样修改一下,我就不重复贴代码了,不了解的看源码对着改一下就好。
前面做了这么多都是做准备工作,最重要的是要显示数据在Fragment上,下面我们写两个适配器,还有两个xml文件。
下面先创建xml文件,在layout下新建item_news.xml文件,里面的代码如下:
① item布局
<?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=“news”
type=“com.llw.mvvm.model.NewsResponse.ResultBean.DataBean” />
<RelativeLayout
android:background=“@color/white”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:paddingStart=“12dp”
android:paddingTop=“12dp”
android:paddingEnd=“12dp”>
<TextView
android:id=“@+id/tv_title”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignParentStart=“true”
android:layout_toStartOf=“@id/image”
android:text=“@{news.title}”
android:textColor=“@color/black”
android:textSize=“14sp” />
<com.llw.mvvm.view.CustomImageView
android:id=“@+id/image”
android:layout_marginStart=“12dp”
networkUrl=“@{news.thumbnail_pic_s}”
android:layout_width=“140dp”
android:layout_height=“80dp”
android:layout_alignParentEnd=“true”
android:scaleType=“centerCrop”
app:shapeAppearanceOverlay=“@style/roundedImageStyle_6” />
<TextView
android:id=“@+id/tv_author”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_above=“@+id/tv_date”
android:layout_below=“@+id/tv_title”
android:layout_marginTop=“4dp”
android:text=“@{news.author_name}”
android:textSize=“12sp”
tools:ignore=“NestedWeights” />
<TextView
android:id=“@+id/tv_date”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignBottom=“@id/image”
android:text=“@{news.date}”
android:textSize=“12sp” />
<View
android:layout_width=“match_parent”
android:layout_height=“0.5dp”
android:layout_below=“@id/image”
android:layout_marginTop=“12dp”
android:background=“@color/line” />
这里用的颜色值line,是#EEEEEE,自行在colors.xml中添加就好了。
然后在layout下创建一个item_video.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=“video”
type=“com.llw.mvvm.model.VideoResponse.ResultBean” />
<RelativeLayout
android:background=“@color/white”
android:layout_width=“match_parent”
android:layout_height=“wrap_content”
android:paddingStart=“12dp”
android:paddingTop=“12dp”
android:paddingEnd=“12dp”>
<TextView
android:id=“@+id/tv_title”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignParentEnd=“true”
android:layout_toEndOf=“@id/image”
android:ellipsize=“end”
android:maxLines=“2”
android:text=“@{video.title}”
android:textColor=“@color/black”
android:textSize=“14sp” />
<com.llw.mvvm.view.CustomImageView
android:id=“@+id/image”
networkUrl=“@{video.item_cover}”
android:layout_width=“140dp”
android:layout_height=“80dp”
android:layout_marginEnd=“12dp”
android:scaleType=“centerCrop”
app:shapeAppearanceOverlay=“@style/roundedImageStyle_6” />
<ImageView
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignStart=“@+id/image”
android:layout_alignTop=“@+id/image”
android:layout_alignEnd=“@id/image”
android:layout_alignBottom=“@+id/image”
android:padding=“20dp”
android:src=“@mipmap/ic_play” />
<TextView
android:id=“@+id/tv_author”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_above=“@+id/tv_words”
android:layout_below=“@+id/tv_title”
android:layout_alignParentEnd=“true”
android:layout_marginTop=“4dp”
android:layout_toEndOf=“@id/image”
android:text=“@{video.author}”
android:textSize=“12sp”
tools:ignore=“NestedWeights” />
<TextView
android:id=“@+id/tv_words”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignBottom=“@id/image”
android:layout_alignParentEnd=“true”
android:layout_toEndOf=“@id/image”
android:ellipsize=“end”
android:maxLines=“1”
android:text=“@{video.hot_words}”
android:textSize=“12sp” />
<View
android:layout_width=“match_parent”
android:layout_height=“0.5dp”
android:layout_below=“@id/image”
android:layout_marginTop=“12dp”
android:background=“@color/line” />
最后
感谢您的阅读,在文末给大家准备一个福利。本人从事Android开发已经有十余年,算是一名资深的移动开发架构师了吧。根据我的观察发现,对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
所以在此将我十年载,从萌新小白一步步成长为Android移动开发架构师的学习笔记,从Android四大组件到手写实现一个架构设计,我都有一一的对应笔记为你讲解。
当然我也为你们整理好了百度、阿里、腾讯、字节跳动等等互联网超级大厂的历年面试真题集锦。这也是我这些年来养成的习惯,一定要学会把好的东西,归纳整理,然后系统的消化吸收,这样才能极大的提高学习效率和成长进阶。碎片、零散化的东西,我觉得最没有价值的。就好比你给我一张扑克牌,我只会觉得它是一张废纸,但如果你给我一副扑克牌,它便有了它的价值。这和我们收集资料就要收集那些系统化的,是一个道理。
最后,赠与大家一句诗,共勉!
不驰于空想,不骛于虚声。不忘初心,方得始终。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
CustomImageView
android:id=“@+id/image”
networkUrl=“@{video.item_cover}”
android:layout_width=“140dp”
android:layout_height=“80dp”
android:layout_marginEnd=“12dp”
android:scaleType=“centerCrop”
app:shapeAppearanceOverlay=“@style/roundedImageStyle_6” />
<ImageView
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignStart=“@+id/image”
android:layout_alignTop=“@+id/image”
android:layout_alignEnd=“@id/image”
android:layout_alignBottom=“@+id/image”
android:padding=“20dp”
android:src=“@mipmap/ic_play” />
<TextView
android:id=“@+id/tv_author”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_above=“@+id/tv_words”
android:layout_below=“@+id/tv_title”
android:layout_alignParentEnd=“true”
android:layout_marginTop=“4dp”
android:layout_toEndOf=“@id/image”
android:text=“@{video.author}”
android:textSize=“12sp”
tools:ignore=“NestedWeights” />
<TextView
android:id=“@+id/tv_words”
android:layout_width=“wrap_content”
android:layout_height=“wrap_content”
android:layout_alignBottom=“@id/image”
android:layout_alignParentEnd=“true”
android:layout_toEndOf=“@id/image”
android:ellipsize=“end”
android:maxLines=“1”
android:text=“@{video.hot_words}”
android:textSize=“12sp” />
<View
android:layout_width=“match_parent”
android:layout_height=“0.5dp”
android:layout_below=“@id/image”
android:layout_marginTop=“12dp”
android:background=“@color/line” />
最后
感谢您的阅读,在文末给大家准备一个福利。本人从事Android开发已经有十余年,算是一名资深的移动开发架构师了吧。根据我的观察发现,对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。
所以在此将我十年载,从萌新小白一步步成长为Android移动开发架构师的学习笔记,从Android四大组件到手写实现一个架构设计,我都有一一的对应笔记为你讲解。
当然我也为你们整理好了百度、阿里、腾讯、字节跳动等等互联网超级大厂的历年面试真题集锦。这也是我这些年来养成的习惯,一定要学会把好的东西,归纳整理,然后系统的消化吸收,这样才能极大的提高学习效率和成长进阶。碎片、零散化的东西,我觉得最没有价值的。就好比你给我一张扑克牌,我只会觉得它是一张废纸,但如果你给我一副扑克牌,它便有了它的价值。这和我们收集资料就要收集那些系统化的,是一个道理。
[外链图片转存中…(img-Hj1E1zcI-1714397445551)]
最后,赠与大家一句诗,共勉!
不驰于空想,不骛于虚声。不忘初心,方得始终。
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!