Skip to content

Commit

Permalink
update readme
Browse files Browse the repository at this point in the history
  • Loading branch information
fashare2015 committed Sep 24, 2017
1 parent c030fe0 commit a633bbc
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 7 deletions.
86 changes: 85 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,85 @@
# MVVM-JueJin
# 高仿掘金App —— 基于 databinding
## 1. 项目初衷
>不同于前端 vue、 react 的火热, 移动端的 databinding 好像不受待见。鉴于 vue、 react 都有各自成熟的生态圈,我希望通过这个项目打磨出一个简单易用的 `databinding 组件库`
### 1.1 data -> view,舍弃 Adapter
之前有不少前辈专门针对 RecyclerView 做了各自的封装,完全省去了 Adapter, 比如:
- [MVVMLight](https://github.com/Kelin-Hong/MVVMLight)
- [binding-collection-adapter](https://github.com/evant/binding-collection-adapter)

### 1.2 更进一步:view -> data -> view
在本项目中,你将会看到一个带有 `下拉刷新` + `上啦加载` 的页面如何简化到`10+行java代码` !

## 2. 模块概览
> 接口全抓自掘金app, 支持登录、注册(走的官方接口,并非假数据哦~
- 已完成:
- 登录、注册:可以用自己的掘金帐号登录,或者临时注册一个
- 首页:热门推荐及文章列表,以及各个分类页面(Android、前端、产品。。。)
- 发现:一级页面,包括 banner、活动、沸点、热门文章
- 消息:完成消息列表
- 我的:一级页面,包括登录与未登录两个状态
- 文章详情页面: 文章 html 以及 下方的评论列表
- TODO:
- splash 页
- 第三方登录
- 发现页 - 搜索模块、活动、沸点的二级页面
- 我的 - 个人信息页,包括从用户头像跳转
- 我的 - 喜欢、收藏、设置等二级页面
- 发布文章页
- 收藏、评论、分享等其他功能
- 夜间模式
- ...

## 3. 效果图
![首页](./screen-record/home.gif)
![文章详情](./screen-record/article.gif)

![登录](./screen-record/login.gif)
![其他](./screen-record/other.gif)

## 4. 技术栈
- databinding
- kotlin
- rxJava + rxAndroid
- retrofit + okhttp
- glide

## 5. 关于 "10+行" 实现的分页列表
我们来看第3个tab - 消息列表:

![消息](./screen-record/notify.png)

```kotlin
// NotifyListVM.kt
@ResHolder(R.layout.item_notify_list) // item 布局
@HeaderResHolder(R.layout.header_notify) // header 布局
class NotifyListVM : TwoWayListVM<NotifyBean>() {
override val loadTask = { lastItem: NotifyBean? -> // 网络请求(refresh、loadMore 二合一)
ApiFactory.getApi(JueJinApis.Notify:: class.java)
.getUserNotification(lastItem?.createdAtString?: "")
.compose(Composers.handleError())
}
override val onItemClick = ArticleActivity.START_FROM_NOTIFY // 点击事件
override val headerData = Any()
}
```

框架中封装了`TwoWayListVM`,我们的`NotifyListVM`继承与它,并在布局中与`RecyclerView`绑定在一起。
重点来了:

1. view(pullToRefresh) -> data(list): 当view有动作(下拉刷新 or 上拉加载),框架会自行调用`loadTask`,然后更新`TwoWayListVM.data`
2. data(list) -> view(RecyclerView): 而当`TwoWayListVM.data`发生变化,会自动触发`RecyclerView`刷新。

然后,配合 kotlin 简洁的语法,我们实现了`也许是史上最简洁???`的分页列表。

## 6. 项目持续打磨中,有兴趣给个star~

## 7. 参考
[Android DataBinding 数据绑定 —— QQ音乐技术团队](https://mp.weixin.qq.com/s?__biz=MzI1NjEwMTM4OA==&mid=2651232170&idx=1&sn=f4d7eb8f35ebf3b13696562ca3172bac&chksm=f1d9eac9c6ae63df357c3a96aa0218b5d66237c5411de5b34cd24ddb7a1d258b34444966d8c6&scene=0#rd)

[官方文档](https://developer.android.com/topic/libraries/data-binding/index.html)

[完全掌握Android Data Binding](http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0603/2992.html)

[MVVMLight](https://github.com/Kelin-Hong/MVVMLight)
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ import com.fashare.mvvm_juejin.viewmodel.NotifyListVM
</pre> *
*/
class NotifyFragment : BaseFragment(){

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return DataBindingUtil.inflate<FragmentNotifyBinding>(inflater, R.layout.fragment_notify, container, false).apply{
this.listVM = NotifyListVM()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,11 @@ import com.fashare.net.ApiFactory
@ResHolder(R.layout.item_notify_list)
@HeaderResHolder(R.layout.header_notify)
class NotifyListVM : TwoWayListVM<NotifyBean>() {

override val loadTask = { lastItem: NotifyBean? ->
ApiFactory.getApi(JueJinApis.Notify:: class.java)
.getUserNotification(lastItem?.createdAtString?: "")
.compose(Composers.handleError())
}

override val onItemClick = ArticleActivity.START_FROM_NOTIFY

override val headerData = HeaderVM()
class HeaderVM
override val headerData = Any()
}
2 changes: 2 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,5 @@ subprojects {
}
}
}

apply from: "https://raw.githubusercontent.com/fashare2015/gradle-screen-record/master/screen-record.gradle"
9 changes: 9 additions & 0 deletions mkdir-if-absent.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
TAG="mkdir-if-absent ---> "

targetDir=$1

echo -e "$TAG mkdir $targetDir\n"

if [ ! -d "$targetDir" ]; then
mkdir "$targetDir"
fi
85 changes: 85 additions & 0 deletions screen-record.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
TAG="screen-record ---> "

OUTPUT_DIR="./screen-record/"
MP4_POSTFIX=".mp4"
GIF_POSTFIX=".gif"

fileName=$1

# [Required] Get gif $fileName from 1st arg: . Or else do nothing.
if [ "$fileName" != "" ]
then
echo -e "$TAG specify fileName: $fileName\n"

mobilePath="/sdcard/$fileName"
pcPath="$OUTPUT_DIR$fileName"

# [Optional] Get gif $size from 2nd arg: . default size: "240x400".
size="240x400"
if [ "$2" != "" ]
then
size=$2
echo -e "$TAG specify size: $size\n"
fi

# [Optional] Get gif $time from 3rd arg. default time: 8s.
time=8
if [ "$3" != "" ]
then
time=$3
echo -e "$TAG specify limited time: $time\n"
fi

# [Optional] Get $ffmpegPath from 4th arg. default ffmpegPath: "ffmpeg".
ffmpegPath="ffmpeg"
if [ "$4" != "" ]
then
ffmpegPath=$4
echo -e "$TAG specify ffmpegPath: $ffmpegPath\n"
fi

# Make output dir if absent.
chmod 777 ./mkdir-if-absent.sh
./mkdir-if-absent.sh $OUTPUT_DIR

# Execute cmd screenrecord and then you get a video (XXX.mp4) in your sdcard.
echo -e "$TAG begin screenrecord\n"
adb shell screenrecord "$mobilePath$MP4_POSTFIX" --time-limit $time
echo -e "$TAG end screenrecord\n"

# Upload the video into OUTPUT_DIR of your PC .
adb pull "$mobilePath$MP4_POSTFIX" "$pcPath$MP4_POSTFIX"

# Remove target gif if exists
if [ -f "$pcPath$GIF_POSTFIX" ]; then
rm "$pcPath$GIF_POSTFIX"
fi

# Convert the video (XXX.mp4) to a gif (XXX.gif).
$ffmpegPath -r 20 -i "$pcPath$MP4_POSTFIX" -s $size -b:v 1500k "$pcPath$GIF_POSTFIX"

if [ $? -eq 0 ]; then
# Delete the redundant file (XXX.mp4).
rm "$pcPath$MP4_POSTFIX"

else
echo -e "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
echo -e "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
echo -e "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
echo -e "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"

echo -e "$TAG ffmpegPath not found!!!\n"
echo -e "Make sure you have installed ffmpeg!\n"
echo -e "Please specify the ffmpegPath by \"FFMPEG_PATH=XXX\" in gradle.properties.\n"

echo -e "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
echo -e "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
echo -e "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
echo -e "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"

exit 1
fi

else
echo -e "$TAG PARAM ERROR! Please specify a fileName (without postfix)!\n"
fi
Binary file added screen-record/article.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screen-record/home.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screen-record/login.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screen-record/notify.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added screen-record/other.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit a633bbc

Please sign in to comment.