前言 最开始学习写应用的时候,发现类聚合数据这个平台可以提供一些免费数据接口,于是写了个人的第一个应用—– JuheNews ,当时的知识储备稍显粗糙,虽然现在的知识也不咋滴,但是相对之前而言还是有些进步的,所以决定将应用重构一下,具体参考我的第二个个人开发的应用— GankIOClient ,采用类似的技术思路,重构后两个应用在代码结构上是很相似的。
技术选型 1. 下拉刷新 + 加载更多 采用BGARefreshLayout-Android ,支持的下拉刷新样式基本可以满足我的需求,使用起来也比较简单,实现两个接口即可设置刷新动作和加载更多的动作。这位卓友的其他开源库也很好用,有兴趣的可以去看下。
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 private void initBGALayout () { bgaRefreshLayout.setDelegate(this ); BGANormalRefreshViewHolder refreshViewHolder = new BGANormalRefreshViewHolder(getContext(), true ); refreshViewHolder.setLoadingMoreText("加载更多" ); refreshViewHolder.setLoadMoreBackgroundColorRes(R.color.white); refreshViewHolder.setRefreshViewBackgroundColorRes(R.color.white); bgaRefreshLayout.setRefreshViewHolder(refreshViewHolder); }
1 2 3 4 5 6 7 8 9 10 @Override public void onBGARefreshLayoutBeginRefreshing(BGARefreshLayout refreshLayout) { //执行下拉刷新操作 } @Override public boolean onBGARefreshLayoutBeginLoadingMore(BGARefreshLayout refreshLayout) { //执行加载更多操作,返回false代表不支持加载更多 return false; }
2. 网络请求 Retrofit + RxJava2,这个就不用多做介绍了,最开始学习这两个内容的时候读过的文章分享一下:
RxJava 与 Retrofit 结合的最佳实践 给 Android 开发者的 RxJava 详解 我的一个RxJava2文章收藏集
这位朋友写的通俗易懂,不看Rxjava2的官文应该也能很快的了解Rxjava升级到2之后的变化。
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public interface JuheApi { @GET Observable<NewsBean> getNews (@Url String url) ; @GET Observable<FunnyBean> getFunny (@Url String url) ; @GET Observable<JokeBean> getJoke (@Url String url) ; @GET Observable<HistoryBean> getTodayInHistory (@Url String url) ; @GET Observable<QueryNewsBean> getQueryNews (@Url String url) ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 public class Retrofitance { public static final String BASE_URL = "http://gank.io/api/" ; private static final int DEFAULT_TIMEOUT = 5 ; private Retrofit retrofit; private JuheApi mJuheApi; private OkHttpClient mOkHttpClient; private Retrofitance () { OkHttpClient.Builder httpClientBuilder = new OkHttpClient.Builder(); httpClientBuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS); mOkHttpClient = httpClientBuilder.build(); retrofit = new Retrofit.Builder().client(mOkHttpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(BASE_URL) .build(); mJuheApi = retrofit.create(JuheApi.class); } public static Retrofitance getInstance () { return SingletonHolder.INSTANCE; } public void getNews (Observer<NewsBean> subscriber, String type) { String URL = "" ; commonOp(mJuheApi.getNews(URL),subscriber); } public void getFunny (Observer<FunnyBean> subscriber, int pagenum) { String URL ="" ; commonOp(mJuheApi.getFunny(URL), subscriber); } public void getHistory (Observer<HistoryBean> subscriber) { Calendar now = Calendar.getInstance(); String URL = "" ; commonOp(mJuheApi.getTodayInHistory(URL),subscriber); } public void getJoke (Observer<JokeBean> subscriber, int pagenum) { String URL ="" ; commonOp(mJuheApi.getJoke(URL), subscriber); } public void getQueryNews (Observer<QueryNewsBean> subscriber, String keyword) { String URL = "" ; commonOp(mJuheApi.getQueryNews(URL), subscriber); } private void commonOp (Observable observable, Observer subscriber) { observable.subscribeOn(Schedulers.io()) .unsubscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(subscriber); } private static class SingletonHolder { private static final Retrofitance INSTANCE = new Retrofitance(); } }
3. 响应式编程 不二选择,RxJava2 + RxAndoid,告别Thread和AsyncTask,不用写Handler了。
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public void getContent (int pagenum) { Observer<FunnyBean> observer = new Observer<FunnyBean>() { @Override public void onComplete () { endLoading(); } @Override public void onError (Throwable e) { e.printStackTrace(); endLoading(); onNetworkError(); } @Override public void onSubscribe (Disposable d) { } @Override public void onNext (FunnyBean funnyBean) { if (bgaRefreshLayout.isLoadingMore()) { } else { mVisitableList.clear(); } if (funnyBean.getResult() == null || funnyBean.getResult().getData() == null || funnyBean.getResult().getData().size() == 0 ) { onDataEmpty(); } else { mVisitableList.addAll(funnyBean.getResult().getData()); } mMultiRecyclerAdapter.setData(mVisitableList); } }; Retrofitance.getInstance().getFunny(observer, pagenum); }
4. 多类型RecyclerView Item实现 参考博文RecyclerView多类型Item的正确实现姿势 当然你也可以选择一些开源库,只是我习惯了使用这种方式,用起来也比较顺手。思路和实现方式也比较简单,使用接口化的数据和泛型,抽取抽象类,结构分明,扩展性强,聪明的你应该一看就会懂。 具体使用方法参考项目代码或者是上面这篇博文,涉及的代码比较多,主要是稍微有点结构化,不便列举。
5. 注解 butterknife,告别findViewById,但是有了Kotlin,我觉得我们也可以告别butterknife了,毕竟一把小刀。 使用方法:
1 2 3 4 5 6 7 8 9 10 11 @BindView (R.id.tl_web)Toolbar tlWeb; @BindView (R.id.wv_content)WebView wvContent; @BindView (R.id.activity_web)LinearLayout activityWeb; @BindView (R.id.progressbar)ProgressBar progressbar; .... ButterKnife.bind(this );
配合插件使用效果会更好喔。
6. 图片加载 我首选Glide,因为我有GIF的需求,Picasso不支持GIF,虽然体量比较小。 Glide的使用方法参考官文,最近Glide有大版本升级,改动比较多,有兴趣的可以关注下 使用方法:
1 Glide.with(itemView.getContext()).load(pic1path).placeholder(R.mipmap.empty_data).into(imageView);
Glide有更高阶的使用方法,根据需求学习吧。
7. 数据解析 Gson,Google的开源库,基本可以满足我的开发需求,暂时没有尝试过其他的。 使用的过程中配合Retrofit使用
1 compile 'com.squareup.retrofit2:converter-gson:2.1.0'
1 2 3 4 5 retrofit = new Retrofit.Builder().client(mOkHttpClient) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .baseUrl(BASE_URL) .build();
配合插件使用效果会更好:GsonFormat
8. 界面布局 采用TabHost + Framgnet可以满足日常需求,当然使用开源库FlycoTabLayout 更是可以构建出炫酷的Tab页面,配合上Fragment,基本可以满足需求。 使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 switch (i) { case 0 : textView.setText("资讯" ); imageView.setImageResource(R.drawable.news); tabHost.addTab(tabHost.newTabSpec("1" ).setIndicator(view).setContent( R.id.frag_news)); break ; case 1 : textView.setText("笑话" ); imageView.setImageResource(R.drawable.joke); tabHost.addTab(tabHost.newTabSpec("2" ).setIndicator(view).setContent( R.id.frag_joke)); break ; case 2 : textView.setText("趣图" ); imageView.setImageResource(R.drawable.funny); tabHost.addTab(tabHost.newTabSpec("3" ).setIndicator(view).setContent( R.id.frag_funny)); break ; case 3 : textView.setText("历史" ); imageView.setImageResource(R.drawable.history); tabHost.addTab(tabHost.newTabSpec("4" ).setIndicator(view).setContent( R.id.frag_history)); break ; default : break ; }
FlycoTabLayout的使用方法参考官文或者网上如海水般的博文。
9. 内容搜索 自定义Toolbar这个时候就显示出来威力。简单的一个文本框和一个若隐若现的搜索按钮,满足需求。开源库也有一些,但是适合我自己的不太多,所以基本上我都是如上实现。
使用方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <android.support.v7.widget.Toolbar android:id ="@+id/toolbar_search" android:layout_width ="match_parent" android:layout_height ="40dp" android:fitsSystemWindows ="true" android:background ="@color/colorToolbar" > <LinearLayout android:layout_width ="match_parent" android:layout_height ="match_parent" > <EditText android:id ="@+id/et_search" android:layout_width ="0dp" android:layout_height ="match_parent" android:layout_marginBottom ="8dp" android:layout_marginTop ="8dp" android:layout_weight ="6" android:background ="@drawable/search_edittext_bg" android:padding ="2dp" android:textColor ="@color/colorBlack" android:textSize ="12sp" /> <Button android:id ="@+id/bt_search" android:layout_width ="0dp" android:layout_height ="match_parent" android:layout_weight ="2" android:clickable ="false" android:gravity ="center" android:text ="搜索" android:background ="@color/colorToolbar" android:textColor ="@color/colorToolbar" android:textSize ="14sp" /> </LinearLayout > </android.support.v7.widget.Toolbar >
10. 版本更新 第三方服务 。我使用的是Fir.im,相对比较好用,利用提供的版本接口检测版本更新。然后本地下载或者跳转到浏览器下载应用,完成安装,都是不错的选择。类似的平台还有很多,可以网上搜索一下。