使用MVP+Retrofit+Rxjava实现一个知乎日报客户端,界面基于Material design,还没有全部完成orz,,放假太懒
效果图
开源项目
name | introduction |
---|---|
butterknife | Annotate fields with @BindView and a view ID for Butter Knife to find and automatically cast the corresponding view in your layout. |
MaterialTabs | Custom Tabs with Material Design animations for pre-Lollipop devices |
materialdatetimepicker | Pick a date or time on Android in style |
agendacalendarview | This library replicates the basic features of the Calendar and Agenda views from the Sunrise Calendar (now Outlook) app, coupled with some small design touch from the Google Calendar app. |
使用Material Design
可以参考我的另外一篇文章Android-Material Design的使用
列表使用的为recyclerView,这个就不解释了
tabs标题栏的实现采用了开源项目neokree/MaterialTabs
需和ViewPager一起使用。在ViewPager中添加Fragment
Activity需要 implements MaterialTabListener
fragmentList = new ArrayList<>();
fragmentList.add(new NewsFragment());
fragmentList.add(new TestFragment());
fragmentList.add(new TestFragment());
tabHost = (MaterialTabHost) this.findViewById(R.id.materialTabHost);
pager = (ViewPager) this.findViewById(R.id.viewpager);
// init view pager
fragmentPagerAdapter = new MyFragmentPagerAdapter(getSupportFragmentManager(), fragmentList);
pager.setAdapter(fragmentPagerAdapter);
pager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// when user do a swipe the selected tab change
tabHost.setSelectedNavigationItem(position);
}
});
// insert all tabs from pagerAdapter data
List<String> tabsNames = new ArrayList<>();
tabsNames.add("zhihu");
tabsNames.add("schedule");
tabsNames.add("timeTable");
for (int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
tabHost.addTab(
tabHost.newTab()
.setText(tabsNames.get(i))
.setTabListener(this)
);
}
点击fab出现日历来选择时间,用来查询过往的日报
日历的实现来自wdullaer/MaterialDateTimePicker
返回的日期需要格式化,使用在知乎日报的API中
String date = String.format("%d%02d%02d", year, monthOfYear + 1, dayOfMonth);
知乎日报的数据
来自知乎日报 API 分析
最新消息
URL: http://news-at.zhihu.com/api/4/news/latest
过往消息
URL: http://news-at.zhihu.com/api/4/news/before/20131119
响应为
{
date: "20140523",
stories: [
{
title: "中国古代家具发展到今天有两个高峰,一个两宋一个明末(多图)",
ga_prefix: "052321",
images: [
"http://p1.zhimg.com/45/b9/45b9f057fc1957ed2c946814342c0f02.jpg"
],
type: 0,
id: 3930445
},
...
],
...
}
消息内容获取与离线下载
URL: http://news-at.zhihu.com/api/4/news/{id}
利用Android studio的插件GsonFormat,来解析返回的json数据
MVP+Retrofit+Rxjava
MVP
可以参考
Retrofit
Android网络请求库 - Say hello to retrofit
Rxjava
定义接口
public interface ZhiHuService {
@GET("api/4/news/latest")
Observable<LatestNews> getLatestNews();
@GET("api/4/news/before/{date}")
Observable<LatestNews> getBeforeNews(@Path("date") String dateString);
@GET("api/4/news/{id}")
Observable<News> getNews(@Path("id") int id);
@GET("api/4/story/{id}/long-comments")
Observable<Comment> getComments(@Path("id") int id);
@GET("api/4/story-extra/{id}")
Observable<StoryExtra> getStroyExtra(@Path("id") int id);
}
基础url
public class Config {
public final static String ZHIHU_URL = "http://news-at.zhihu.com/";
}
进行封装
public class ZhiHuApi {
private static final int DEFAULT_TIMEOUT = 5;
private ZhiHuService zhiHuService;
private static ZhiHuApi zhiHuApi;
private Retrofit retrofit;
private ZhiHuApi() {
//设置超时时间
OkHttpClient.Builder httpcientBuilder = new OkHttpClient.Builder();
Retrofit retrofit = new Retrofit.Builder()
.client(httpcientBuilder.build())//
.baseUrl(Config.ZHIHU_URL)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.build();
zhiHuService = retrofit.create(ZhiHuService.class);
}
public static ZhiHuApi getInstance(){
if (zhiHuApi == null) {
synchronized (ZhiHuApi.class){
if (zhiHuApi == null){
zhiHuApi = new ZhiHuApi();
}
}
}
return zhiHuApi;
}
public void getLatestNews(Subscriber<LatestNews> subscriber){
zhiHuService.getLatestNews()
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(subscriber);
}
...
}
把列表的界面作为例子
定义Contract接口
public interface NewsContract {
interface View{
void refreshRecyclerVew(List<LatestNews.StoriesBean> storiesList);
}
interface Presenter{
void getBeforeNews(String date);
void getLatestNews();
}
interface model{
void getBeforeNews(CallBackLatestNews callback, String date);
void getLatestNews(CallBackLatestNews callback);
}
}
View层
public class NewsFragment extends Fragment implements NewsContract.View, DatePickerDialog.OnDateSetListener {
private static final String TAG = "NewsFragment";
RecyclerView recyclerView;
private NewsContract.Presenter presenter;
public NewsFragment() {
presenter = new NewsPresenter(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_zhihu_daily, container, false);
FloatingActionButton fab = (FloatingActionButton) view.findViewById(R.id.fab);
fab.setImageResource(R.drawable.add);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//显示日历选项
Calendar now = Calendar.getInstance();
DatePickerDialog dpd = DatePickerDialog.newInstance(
NewsFragment.this,
now.get(Calendar.YEAR),
now.get(Calendar.MONTH),
now.get(Calendar.DAY_OF_MONTH)
);
dpd.show(getActivity().getFragmentManager(), "Datepickerdialog");
}
});
recyclerView = (RecyclerView) view.findViewById(R.id.latest_news_recyclerview);
//StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
recyclerView.setLayoutManager(layoutManager);
presenter.getLatestNews();
return view;
}
@Override
public void refreshRecyclerVew(List<LatestNews.StoriesBean> storiesList) {
Log.d(TAG, "refreshRecyclerVew: ");
NewsSummaryAdapter adapter = new NewsSummaryAdapter(storiesList);
recyclerView.setAdapter(adapter);
}
@Override
public void onDateSet(DatePickerDialog view, int year, int monthOfYear, int dayOfMonth) {
//String date = "You picked the following date: "+dayOfMonth+"/"+(monthOfYear+1)+"/"+year;
String date = String.format("%d%02d%02d", year, monthOfYear + 1, dayOfMonth);
presenter.getBeforeNews(date);
}
}
定义一个回调接口
public interface CallBackLatestNews {
public void result(List<LatestNews.StoriesBean> list);
}
Presenter层
public class NewsPresenter implements NewsContract.Presenter {
private static final String TAG = "NewsPresenter";
private NewsContract.View view;
private NewsContract.model model;
public NewsPresenter(NewsContract.View view) {
this.view = view;
model = new NewsModel();
}
@Override
public void getBeforeNews(String date) {
model.getBeforeNews(new CallBackLatestNews() {
@Override
public void result(List<LatestNews.StoriesBean> list) {
view.refreshRecyclerVew(list);
}
}, date);
}
@Override
public void getLatestNews() {
Log.d(TAG, "getLatestNews: ");
model.getLatestNews(new CallBackLatestNews() {
@Override
public void result(List<LatestNews.StoriesBean> list) {
view.refreshRecyclerVew(list);
}
});
}
}
model层
public class NewsModel implements NewsContract.model {
private static final String TAG = "NewsModel";
@Override
public void getBeforeNews(final CallBackLatestNews callback, String date) {
Subscriber subscriber = new Subscriber<LatestNews>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(LatestNews latestNews) {
callback.result(latestNews.getStories());
}
};
ZhiHuApi.getInstance().getBeforeNews(subscriber, date);
}
@Override
public void getLatestNews(final CallBackLatestNews callback) {
Subscriber subscriber = new Subscriber<LatestNews>() {
@Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(LatestNews latestNews) {
Log.d(TAG, "onNext: ");
callback.result(latestNews.getStories());
}
};
ZhiHuApi.getInstance().getLatestNews(subscriber);
}
}
暂时只实现了知乎日报的功能,schedule和timetable还未实现。
github:https://github.com/linsawako/oneDay
时间: 2024-09-30 09:03:01