众所周知知乎日报的api是公开的,所以我就想做个自己的知乎日报来玩一下
主要用到下列库
glide:3.5.2
如果不熟悉rxjava怎么用的话可以先到这里看一下(我这次只用到map)http://blog.csdn.net/lzyzsd/article/details/41833541/
首先要看一下知乎日报的api
我这次主要用到了几个获取今日日报,无聊日报,互联网安全,体育日报和每个新闻的详细信息的api
因为这个api返回的是json数据,推荐http://www.bejson.com/json2javapojo/
可将json转换为java类,但是有些只是字符串(生成后有些类是空白的)它都设为一个类,复制到自己java文件时,可以把不要那个类,改为String类型
带着大家简单分析一下api
以最新消息为例
http://news-at.zhihu.com/api/4/news/latest
响应实例:
{
date: "20140523",
stories: [
{
title: "中国古代家具发展到今天有两个高峰,一个两宋一个明末(多图)",
ga_prefix: "052321",
images: [
"http://p1.zhimg.com/45/b9/45b9f057fc1957ed2c946814342c0f02.jpg"
],
type: 0,
id: 3930445
},
...
],
top_stories: [
{
title: "商场和很多人家里,竹制家具越来越多(多图)",
image: "http://p2.zhimg.com/9a/15/9a1570bb9e5fa53ae9fb9269a56ee019.jpg",
ga_prefix: "052315",
type: 0,
id: 3930883
},
...
]
}
分析:
date
: 日期stories
: 当日新闻 title
: 新闻标题images
: 图像地址(官方 API 使用数组形式。目前暂未有使用多张图片的情形出现,曾见无 images
属性的情况,请在使用中注意 )ga_prefix
: 供 Google Analytics 使用type
: 作用未知id
: url
与 share_url
中最后的数字(应为内容的 id)multipic
: 消息是否包含多张图片(仅出现在包含多图的新闻中)top_stories
: 界面顶部 ViewPager 滚动显示的显示内容(子项格式同上)并生成他的getter和setter
public class RootEntity {
private ArrayList stories ;
public void setStories(ArrayList stories){
this.stories = stories;
}
public ArrayList getStories(){
return this.stories;
}
}
public class StoriesEntity {
private int id;
private String title;
private List images;
public void setId(int id) {
this.id = id;
}
public void setTitle(String title) {
this.title = title;
}
public void setImages(List images) {
this.images = images;
}
public int getId() {
return id;
}
public String getTitle() {
return title;
}
public List getImages() {
return images;
}
}
public class StoryDetailsEntity {
private String body;
private String image_source;
private String title;
private String image;
public void setBody(String body) {
this.body = body;
}
public void setImage_source(String image_source) {
this.image_source = image_source;
}
public void setTitle(String title) {
this.title = title;
}
public void setImage(String image) {
this.image = image;
}
public String getBody() {
return body;
}
public String getImage_source() {
return image_source;
}
public String getTitle() {
return title;
}
public String getImage() {
return image;
}
}
然后每个Fragment都只有一个ListView
实现的内容都差不多,所以我们用一个BaseFragment
public class BaseFragment extends Fragment {
private String baseUrl="http://news-at.zhihu.com";//baseUrl一定要设为这个
public ZhiHuService service;//要靠他来获取消息,子Fragment都要用
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
service=getService();
return inflater.inflate(R.layout.fragment_base, container, false);
}
public ZhiHuService getService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
service=retrofit.create(ZhiHuService.class);
return service;
}
public void loadDataSetLis(Observable rootEntityObservable, final ListView listView){
rootEntityObservable.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.map(new Func1>() {
@Override
public ArrayList call(RootEntity rootEntity) {
return rootEntity.getStories();
}
})
.subscribe(new Subscriber>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(final ArrayList storiesEntities) {
listView.setAdapter(new NewsAdapter(storiesEntities,getContext()));
//点击item跳转到详细页面
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
Intent intent=new Intent(getActivity(),StoryDetailActivity.class);
intent.putExtra("id",storiesEntities.get(position).getId());
startActivity(intent);
}
});
}
});
}
}
public class InterestFragment extends BaseFragment {
private ListView lv;
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
lv= (ListView) view.findViewById(R.id.lvnews);
loadDataSetLis(service.getInterest(),lv);
}
}
每个item的布局只有一个TextView和ImageView
代码如下
public class NewsAdapter extends BaseAdapter {
private List newsList;
private LayoutInflater mInflater;
private Context context;
public NewsAdapter(ArrayList newsList, Context context){
this.newsList=newsList;
this.context=context;
mInflater=LayoutInflater.from(context);
}
@Override
public int getCount() {
return newsList.size();
}
@Override
public Object getItem(int position) {
return newsList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
Holder holder=null;
if (convertView==null){
convertView=mInflater.inflate(R.layout.news_item,null);
holder=new Holder(convertView);
convertView.setTag(holder);
}
holder= (Holder) convertView.getTag();
StoriesEntity news=newsList.get(position);
TextView tvnews=holder.tvnews;
ImageView ivnews=holder.ivnews;
tvnews.setText(news.getTitle());
//记得判空,不然后会空指针异常
if (news.getImages()==null){
ivnews.setVisibility(View.GONE);
}else {
ivnews.setVisibility(View.VISIBLE);
//用Glide根据URL加载图片
Glide.with(context).load(news.getImages().get(0)).into(ivnews);
}
return convertView;
}
private class Holder {
ImageView ivnews;
TextView tvnews;
public Holder(View view){
ivnews= (ImageView) view.findViewById(R.id.ivnews);
tvnews= (TextView) view.findViewById(R.id.tvnews);
}
}
}
public interface ZhiHuService {
//今日头条
@GET("/api/4/news/latest")
Observable getLatestNews();
//互联网安全
@GET("/api/4/theme/10")
Observable getSafety();
//不准无聊
@GET("/api/4/theme/11")
Observable getInterest();
//体育日报
@GET("/api/4/theme/8")
Observable getSport();
//传入id查看详细信息
@GET("/api/4/news/{id}")
Observable getNewsDetails(@Path("id") int id);
}
详细页面只是一个webview,可里面的css来自assets,不然的话页面会很难看
还需要一个HtmlUtil来生成完成的html代码
代码如下
public class HtmlUtils {
public static String structHtml(StoryDetailsEntity storyDetailsEntity) {
StringBuilder sb = new StringBuilder();
sb.append("")
.append(""
)
.append(storyDetailsEntity.getTitle()).append("")
.append("")
.append(storyDetailsEntity.getImage_source()).append("")
.append(").append(storyDetailsEntity.getImage())
.append("\" alt=\"\">")
.append("");
//news_content_style.css和news_header_style.css都是在assets里的
String mNewsContent = ""
+ ""
+ storyDetailsEntity.getBody().replace("", sb.toString());
return mNewsContent;
}
}
接下来是详细页面的activity的代码
public class StoryDetailActivity extends AppCompatActivity {
private WebView wv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_story_detail);
//左上角出现小箭头
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
wv= (WebView) findViewById(R.id.webView);
Intent intent=getIntent();
String baseUrl="http://news-at.zhihu.com";
int id=intent.getIntExtra("id",0);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
ZhiHuService service=retrofit.create(ZhiHuService.class);
service.getNewsDetails(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.map(new Func1() {
@Override
public String call(StoryDetailsEntity storyDetailsEntity) {
return HtmlUtils.structHtml(storyDetailsEntity);
}
})
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.e("error",e.toString());
}
@Override
public void onNext(String s) {
//加载asset里的css
wv.loadDataWithBaseURL("file:///android_asset/", s, "text/html", "UTF-8", null);
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
//点击小箭头返回
if(item.getItemId() == android.R.id.home)
{
finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
那两个css是在assets里的,我这里就不展示了,所有文件我都放在github里了
源码下载