Android-MVP+Retrofit+Rxjava实现一个知乎日报客户端

来源:互联网 发布:四维设计软件 编辑:程序博客网 时间:2024/06/05 09:34

使用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

可以参考
java-mvp模式简单实现
浅谈Andorid开发中的MVP模式

Retrofit

Android网络请求库 - Say hello to retrofit
RxJava 与 Retrofit 结合的最佳实践

Rxjava

给 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);}

基础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还未实现。

添加了便签的功能,具体实现可以看 Android-使用LitePal实现一个便笺功能

这里写图片描述

github:https://github.com/linsawako/oneDay

5 0
原创粉丝点击