基于 Material Design 的 Gank IO 客户端(内含妹子图)

版权声明:本文为博主原创文章,未经博主允许不得转载。
微博:厉圣杰
源码:GankIO
文中如有纰漏,欢迎大家留言指出。

去年 RxJava + Retrofit 很火,但是自己一直没有接触,周末闲来无事,使用 MVP + RxJava + Retrofit + ButterKnife 写了一个简单的基于 GankIO 提供的 API 的 Material Design 的客户端。不过,不得不自己吐槽一下,每日精选自己设计的真的很丑,特别是显示日期的 Item。

先上个效果图:


gank.gif

项目架构

简单说下项目架构,项目分为 appcorelibrary 三个 module。其中,library 是第三方的下拉刷新库,可以先忽略。

基于 Material Design 的 Gank IO 客户端(内含妹子图)_第1张图片
w300

core 是与业务逻辑无关的库,包含一些基类,如:BaseActivityBaseFragment等,这里封装好了 ButterKnife ,不得不说,使用注解真的可以极大的加快开发效率。
manager 包下的 ActivityManager 用于管理应用的 Activity 栈,但此类与系统的类重名,有待商榷
BaseImage 是临时封装 ImageViewGlide 的,封装的很差,待改进。
utils 包下是一些常见的工具类。

app 是基于 GankIO 的客户端,涉及到具体的业务逻辑,其项目结构大致如下:
![屏幕快照 2017-03-21 下午10.37.28 w300](http://odsdowehg.bkt.clouddn.com/屏幕快照 2017-03-21 下午10.37.28.png)

其中,modules 包下是客户端的界面,由于界面不多,所以没有细分。

contract 是 MVP 模式的契约类,具体可以参考 Google 开源的 MVP 项目:android-architecture。就 MVP 模式来说,Google 的这种方式不得不说的确很不错,在契约类中包含了 Model 、View 、 Presenter 三层的契约,查看起整体逻辑会方便很多,在一定程度上也能减少使用 MVP 模式导致的类爆炸问题。

presenter 包下则是 P 层的实现,只涉及业务、数据,不涉及 UI ,UI 的操作全部封装在 V 层。

model 包下则是 Model 层的实现,该层只负责数据请求,如果数据不是很复杂,完全可以将其与 P 层合并。个人觉得,模式这种东西提供的是一种思路,要善于变通

如果对 MVP 模式不是很理解,可以切换到 master 分支,检出 tag 为 v1.0.0 的提交记录,该项目最初版本为非 MVP 模式实现。

项目中碰到的问题小记

SwipeRefreshLayout 与 WebView 的滑动冲突

v1.0.0 版本中,使用 WebView 加载页面时,存在将网页上滑到顶部时,会与 SwipeRefreshLayout 的下拉事件存在冲突,这里采用了一种侵入式的解决方法,代码如下:

mWebView.setOnScrollChangedListener(new ScrollWebView.OnScrollChangedListener() {
       @Override
       public void onScrollChanged(int l, int t) {
           Log.d(TAG, "l = " + l + ";t = " + t);
           if (mWebView.getScrollY() == 0) {
               mSwipeRefreshLayout.setEnabled(true);
           } else {
               mSwipeRefreshLayout.setEnabled(false);
           }
       }
   });

WebView 页面自适应

细心的朋友可能已经发现,图片加载跟网页加载一样,都是使用 WebView 实现,但是 WebView 加载图片时并不会自适应,所以这里需要对 WebView 进行设置。这里给出项目中对 WebView 进行的所以设置

private void initWebViewSettings() {
   WebSettings settings = mWebView.getSettings();
   // 开启 JS 支持
   settings.setJavaScriptEnabled(true);
   // 支持屏幕缩放
   settings.setSupportZoom(true);
   // 设置出现缩放工具
   settings.setBuiltInZoomControls(true);
   // 不显示webview缩放按钮
   settings.setDisplayZoomControls(false);
   // 扩大比例的缩放
   settings.setUseWideViewPort(true);
   //自适应屏幕
   settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
   settings.setLoadWithOverviewMode(true);
}

ViewPager + TabLayout + Fragment的状态保存

相信这个问题许多人使用 ViewPager 时都碰到过,那就是有多个 Pager 的时候,比方从第 1 个页面切换到第 5 个页面,然后再切回去,会发现 Pager 又会重新初始化一次。这就会造成一种不是很好的用户体验。其实处理这个问题很简单,在 onSaveInstanceState 保存你需要保存的数据,再次初始化时判断 savedInstanceState 是否为空即可,代码如下:

@Override
public void onSaveInstanceState(Bundle outState) {
   mPresenter.onSaveInstanceState(outState);
   //计算出当前RecyclerView垂直方向的偏移量
   outState.putInt("offset", mRecyclerView.computeVerticalScrollOffset());
   super.onSaveInstanceState(outState);
}

@Override
protected void initData(Bundle savedInstanceState) {
   mPresenter = new CategoryPresenter(this);
   if (savedInstanceState != null) {
       //恢复之前的RecyclerView的偏移量
       //调用RecyclerView.offsetChildrenVertical可滚动指定偏移量
       mOffset = savedInstanceState.getInt("offset");
   }
}

欢迎 Star 和 Fork:GankIO

你可能感兴趣的:(基于 Material Design 的 Gank IO 客户端(内含妹子图))