版权声明:本文为博主原创文章,未经博主允许不得转载。
微博:厉圣杰
源码:GankIO
文中如有纰漏,欢迎大家留言指出。
去年 RxJava + Retrofit 很火,但是自己一直没有接触,周末闲来无事,使用 MVP + RxJava + Retrofit + ButterKnife 写了一个简单的基于 GankIO 提供的 API 的 Material Design 的客户端。不过,不得不自己吐槽一下,每日精选自己设计的真的很丑,特别是显示日期的 Item。
先上个效果图:
项目架构
简单说下项目架构,项目分为 app 、 core 和 library 三个 module。其中,library 是第三方的下拉刷新库,可以先忽略。
core 是与业务逻辑无关的库,包含一些基类,如:BaseActivity
、BaseFragment
等,这里封装好了 ButterKnife
,不得不说,使用注解真的可以极大的加快开发效率。
manager
包下的 ActivityManager
用于管理应用的 Activity 栈,但此类与系统的类重名,有待商榷
BaseImage
是临时封装 ImageView
和 Glide
的,封装的很差,待改进。
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