前情回顾:
1.WanAndroid实战——首页Banner
前面完成了首页Banner的实现,本次继续来完成首页文章的获取和显示。
按照惯例,先上实现过的效果图。
1.相关布局文件
首页文章的展示使用的是recycleView,每一项使用的是cardview来进行,因此,先在gradle中引入需要的依赖。
//cardview
implementation 'com.android.support:cardview-v7:28.0.0'
//recycleview
implementation 'com.android.support:recyclerview-v7:28.0.0'
在activity_main.xml
文件中添加recycleView的相关内容,修改后的文件如下:
article_item.xml
文件作为每一项的布局,这里使用cardView作为跟布局,文件内容和布局效果如下:
- 使用tools命名空间,用来展示布局效果,并且编译的时候不会编译进去,包括去掉一些警告,如ImageView的contentDescription属性。
- 使用Guideline,用来保证textView不会因为内容过长而遮挡其它控件的显示。
2.准备adapter
根据玩Android的开放API接口,我们知道首页数据的格式
https://www.wanandroid.com/article/list/0/json
方法:GET
参数:页码,拼接在连接中,从0开始。
点击查看示例:https://www.wanandroid.com/article/list/1/json。
可以直接复制示例内容后通过GsonFormat自动生成,具体的可以查看我的上一篇文章。生成的类名为MainArticleBean.java
,记得加上toString方法,方便自己调试。
ArticleAdapter.java
package com.tom.wanandroid.adapter;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import com.tom.wanandroid.R;
import com.tom.wanandroid.bean.MainArticleBean;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Title: ArticleAdapter
* Description: 文章适配器
*
* @author tom
* @date 2019/3/9 10:15
**/
public class ArticleAdapter extends RecyclerView.Adapter {
private Context mContext;
private List mBeans;
public void setBeans(MainArticleBean beans) {
mBeans = beans.getData().getDatas();
}
public ArticleAdapter(RecyclerView recyclerView) {
mContext = recyclerView.getContext();
}
@NonNull
@Override
public ArticleHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(mContext).inflate(R.layout.article_item, parent, false);
return new ArticleHolder(view);
}
@Override
public void onBindViewHolder(@NonNull ArticleHolder articleHolder, int i) {
if (mBeans != null) {
MainArticleBean.DataBean.DatasBean bean = mBeans.get(i);
articleHolder.mArticleTitle.setText(Html.fromHtml(bean.getTitle(), Html.FROM_HTML_MODE_COMPACT));
articleHolder.mArticleAuthor.setText(String.format(mContext.getResources().getString(R.string.article_author),bean.getAuthor()));
articleHolder.mArticleTime.setText(bean.getNiceDate());
String category = String.format(mContext.getResources().getString(R.string.article_category),
bean.getSuperChapterName(), bean.getChapterName());
articleHolder.mArticleClassify.setText(Html.fromHtml(category, Html.FROM_HTML_MODE_COMPACT));
}
}
@Override
public int getItemCount() {
return mBeans != null ? mBeans.size() : 0;
}
class ArticleHolder extends RecyclerView.ViewHolder {
@BindView(R.id.article_title)
TextView mArticleTitle;
@BindView(R.id.article_author)
TextView mArticleAuthor;
@BindView(R.id.article_classify)
TextView mArticleClassify;
@BindView(R.id.article_collection)
ImageView mArticleCollection;
@BindView(R.id.article_time)
TextView mArticleTime;
ArticleHolder(@NonNull View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
这里有一点需要说明,因为获取的数据为网络数据,且部分数据会带有html中的标签并且使用unicode编码,如果不处理的话会直接显示出unicode码,不好看。这里使用的是Html.fromHtml
方法,里面的参数官网上面有说明。
3.在接口中增加相关的内容
分别增加MVP层的接口,比较简单,直接给出代码Contract.java
。
package com.tom.wanandroid.contract;
import com.tom.wanandroid.bean.BannerBean;
import com.tom.wanandroid.bean.MainArticleBean;
import io.reactivex.Observable;
/**
* Title: Contract
* Description:
*
* @author tom
* @date 2019/3/7 10:13
**/
public class Contract {
public interface IMainModel {
/**
* 获取banner数据
* @return banner数据
*/
Observable loadBanner();
/**
* 获取首页文章
* @param number 页码
* @return 首页文章
*/
Observable loadArticle(int number);
}
public interface IMainView {
/**
* View 获取到数据后进行显示
* @param bean banner的数据
*/
void loadBanner(BannerBean bean);
/**
* 展示首页文章信息
* @param bean 首页文章数据
*/
void loadArticle(MainArticleBean bean);
}
public interface IMainPresenter{
/**
* 首页banner
*/
void loadBanner();
/**
* 加载首页文章
*/
void loadArticle();
}
}
因为使用retorfit来获取网络数据,首先还是要增加接口。
IRetrofitData.java
这里需要传入页码作为参数。
/**
* 获取首页文章数据
* @param number 页码
* @return 返回首页文章
*/
@GET("article/list/{number}/json")
Observable loadMainArticle(@Path("number") int number);
4.实现接口
接口加完以后,就来处理报错吧,哪里报错改哪里,so easy!
MainModel.java
和之前获取首页数据一样的套路。
@Override
public Observable loadArticle(int number) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
IRetrofitData retrofitData = retrofit.create(IRetrofitData.class);
return retrofitData.loadMainArticle(number);
}
MainPresenter.java
增加首页文章的相关内容,这里固定获取第一页的数据(传入参数为0)。
@Override
public void loadArticle() {
mModel.loadArticle(0)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer() {
@Override
public void onSubscribe(Disposable d) {
mCompositeDisposable.add(d);
}
@Override
public void onNext(MainArticleBean bean) {
if (isViewAttached()) {
getView().loadArticle(bean);
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
@Override
public void onComplete() {
}
});
}
MainActivity.java
,这里有一点小修改,将之前初始化数据的方法由init中放到了onresume中。
@Override
protected void onResume() {
super.onResume();
mPresenter.loadBanner();
mPresenter.loadArticle();
}
@Override
public void loadArticle(MainArticleBean bean) {
mArticleContent.setLayoutManager(new LinearLayoutManager(this));
if (bean.getErrorCode() == 0) {
ArticleAdapter adapter = new ArticleAdapter(mArticleContent);
adapter.setBeans(bean);
mArticleContent.setAdapter(adapter);
}
}
到这里为止,关键内容基本都OK了,程序跑起来就可以看到前面的效果图了。
这里给出用到的资源文件
colors.xml
#008577
#00574B
#D81B60
#F0F0F0
dimens.xml
200dp
12dp
15dp
10dp
10dp
15dp
30dp
strings.xml
中文和英文是两个夹子的,这里写在一起了。
WanAndroid
author:%1$s
category:%1$s/%2$s
玩Android
作者:%1$s
分类:%1$s/%2$s
用到的图标:
后续更新:
3.WanAndroid实战——内容显示
4.WanAndroid实战——刷新加载
5.WanAndroid实战——网络判断