使用MVP+Retrofit+Rxjava实现一个知乎日报客户端,界面基于Material design,还没有全部完成orz,,放假太懒
可以参考我的另外一篇文章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) ); } 12345678910111213141516171819202122232425262728293031点击fab出现日历来选择时间,用来查询过往的日报
日历的实现来自wdullaer/MaterialDateTimePicker
返回的日期需要格式化,使用在知乎日报的API中
String date = String.format("%ddd", year, monthOfYear + 1, dayOfMonth); 1来自知乎日报 API 分析
最新消息
URL: http://news-at.zhihu.com/api/4/news/latest 12过往消息
URL: http://news-at.zhihu.com/api/4/news/before/20131119 12响应为
{ date: "20140523", stories: [ { title: "中国古代家具发展到今天有两个高峰,一个两宋一个明末(多图)", ga_prefix: "052321", images: [ "http://p1.zhimg.com/45/b9/45b9f057fc1957ed2c946814342c0f02.jpg" ], type: 0, id: 3930445 }, ... ], ... } 12345678910111213141516消息内容获取与离线下载
URL: http://news-at.zhihu.com/api/4/news/{id} 12利用Android studio的插件GsonFormat,来解析返回的json数据
可以参考 java-mvp模式简单实现 浅谈Andorid开发中的MVP模式
Android网络请求库 - Say hello to retrofit RxJava 与 Retrofit 结合的最佳实践
给 Android 开发者的 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); } 12345678910111213141516基础url
public class Config { public final static String ZHIHU_URL = "http://news-at.zhihu.com/"; } 123进行封装
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); } ... } 123456789101112131415161718192021222324252627282930313233343536373839404142把列表的界面作为例子
定义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); } } 1234567891011121314151617View层
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("%ddd", year, monthOfYear + 1, dayOfMonth); presenter.getBeforeNews(date); } } 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758定义一个回调接口
public interface CallBackLatestNews { public void result(List<LatestNews.StoriesBean> list); } 123Presenter层
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); } }); } } 12345678910111213141516171819202122232425262728293031323334model层
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); } } 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051暂时只实现了知乎日报的功能,schedule和timetable还未实现。
添加了便签的功能,具体实现可以看 Android-使用LitePal实现一个便笺功能
github:https://github.com/linsawako/oneDay
