import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.app.Fragment;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.Toast;
import net.bwie.month0301.adapter.ZhiHuPagerAdapter;
import net.bwie.month0301.application.MyApplication;
import net.bwie.month0301.bean.CheckVersionBean;
import net.bwie.month0301.fragment.ZhiHuFragment;
import net.bwie.month0301.httpservice.CheckVersionHttpService;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
*
* 1、Retrofit + RxJava访问版本更新地址,展示最新版本
* 2、Retrofit + RxJava访问最新消息和过往消息
* 使用ViewPager加载两页,分别展示最新消息和过往消息
* <p>
* 3、使用Glide加载图片
* 4、点击item进入详情页
* 5、向上滑动详情页抬头渐变:CoordinatorLayout + AppBarLayout + CollapingToolbarLayout
* <p>
* 6、使用greenDAO数据库缓存网络数据,在没有网络时也可以访问本地离线数据
* 7、使用EventBus实现必要的数据传递
* 8、UI控件要使用ButterKnife初始化
* 8、详情页查看新闻点赞数
* <p>
* 流程
* 1、导包
* 2、搭建UI布局
* 3、获取网络数据,并立即缓存到数据库中
* 4、无论是否有网,都从数据库中读取数据并展示
* 5、实现RecyclerView中item的点击事件,跳转下一个界面:详情页
* 6、详情页中实现标题栏滑动悬停
* 7、详情页中使用WebView加载html代码
* <p>
* UI控件要使用ButterKnife初始化
*/
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tab_layout)
protected TabLayout mTabLayout;
@BindView(R.id.view_pager)
protected ViewPager mViewPager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_main);
checkAppVersion();
initView();
}
// 检测版本更新
private void checkAppVersion() {
CheckVersionHttpService httpService = MyApplication.getRetrofit()
.create(CheckVersionHttpService.class);
Observable<CheckVersionBean> observable = httpService.getCheckVersionObservable();
observable.subscribeOn(Schedulers.io())
.map(new Function<CheckVersionBean, String>() {
@Override
public String apply(CheckVersionBean checkVersionBean) throws Exception {
return checkVersionBean.getLatest();// 将完整数据bean变换为最终需要的字符串信息
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<String>() {
@Override
public void accept(String latest) throws Exception {
Toast.makeText(MainActivity.this, latest, Toast.LENGTH_LONG).show();
Log.d("1511", "最新版本:" + latest);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
}
});
}
private void initView() {
ButterKnife.bind(this);
// 绑定适配器
List<Fragment> fragmentList = new ArrayList<>();
for (int i = 0; i < 2; i++) {
ZhiHuFragment fragment = ZhiHuFragment.newInstance(i);// 0代表最新,1代表过往
fragmentList.add(fragment);
}
mViewPager.setAdapter(new ZhiHuPagerAdapter(getSupportFragmentManager(),
fragmentList));
// 绑定标题栏
mTabLayout.setupWithViewPager(mViewPager);
}
DetailActivity
package net.bwie.month0301.activity;
import android.os.Bundle;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.webkit.WebView;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import net.bwie.month0301.R;
import net.bwie.month0301.bean.DetailMsgBean;
// https://news-at.zhihu.com/api/4/news/3892357
public class DetailActivity extends AppCompatActivity {
protected ImageView mTitleBgIv;
protected CollapsingToolbarLayout mCollapsingToolbarLayout;
protected WebView mWebView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
super.setContentView(R.layout.activity_detail);
initView();
loadDataFromNetwork();
}
// 获取网络数据
private void loadDataFromNetwork() {
int articleId = getIntent().getIntExtra("id", 0);
Log.d("1511", "articleid: " + articleId);
// DetailHttpService httpService = MyApplication.getRetrofit()
// .create(DetailHttpService.class);
// Observable<DetailMsgBean> observable = httpService.getDetailObservable(articleId);
// observable.subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new Consumer<DetailMsgBean>() {
// @Override
// public void accept(DetailMsgBean detailMsgBean) throws Exception {
// Log.d("1511", "error in onnext: " + detailMsgBean);
// showData(detailMsgBean);
// }
// });
restadapter
}
// 展示数据
private void showData(DetailMsgBean detail) {
mCollapsingToolbarLayout.setTitle(detail.getTitle());
Glide.with(this)
.load(detail.getImage())
.into(mTitleBgIv);
mWebView.loadDataWithBaseURL(null,
detail.getBody(),
"text/html",
"UTF-8",
null);
}
private void initView() {
mTitleBgIv = (ImageView) findViewById(R.id.title_bg_iv);
mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.collapsing_toolbar_layout);
mWebView = (WebView) findViewById(R.id.web_view);
}
}
MsgAdapter
package net.bwie.month0301.adapter;
import android.content.Context;
import android.content.Intent;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.RequestOptions;
import net.bwie.month0301.R;
import net.bwie.month0301.activity.DetailActivity;
import net.bwie.month0301.application.MyApplication;
import net.bwie.month0301.bean.StoriesBean;
import net.bwie.month0301.bean.StoriesBeanDao;
import java.util.ArrayList;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {
private Context mContext;
private List<StoriesBean> mDatas;
private RecyclerView mRecyclerView;
public MsgAdapter(Context context) {
mContext = context;
mDatas = new ArrayList<>();
}
// 添加数据的方法
public void addDatas(List<StoriesBean> datas) {
mDatas.addAll(datas);
notifyDataSetChanged();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(mContext)
.inflate(R.layout.item_msg, parent, false);
// 获取item的父容器,即RecyclerView
mRecyclerView = ((RecyclerView) parent);
// item绑定点击事件
initItemClickListener(itemView);
return new ViewHolder(itemView);
}
private void initItemClickListener(View itemView) {
// 点击item,跳转对应详情页
itemView.setOnClickListener(this);
// 长点击item,删除该item
itemView.setOnLongClickListener(this);
}
// 点击item,跳转对应详情页
@Override
public void onClick(View v) {
int position = mRecyclerView.getChildLayoutPosition(v);
int id = mDatas.get(position).getId();
Intent intent = new Intent(mContext, DetailActivity.class);
intent.putExtra("id", id);
mContext.startActivity(intent);
}
// 长点击item,删除该item
@Override
public boolean onLongClick(View v) {
int position = mRecyclerView.getChildLayoutPosition(v);
// 先删除数据库
StoriesBean deleteStory = mDatas.get(position);
StoriesBeanDao dao = MyApplication.getDaoSession().getStoriesBeanDao();
dao.delete(deleteStory);
// 再删除展示数据
mDatas.remove(position);
notifyDataSetChanged();
Toast.makeText(mContext, "删除成功", Toast.LENGTH_SHORT).show();
return true;
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
StoriesBean story = mDatas.get(position);
holder.mTitleTextView.setText(story.getTitle());
RequestOptions options = new RequestOptions()
.circleCrop()
.error(R.drawable.fail)
.placeholder(R.drawable.load);
Glide.with(mContext)
.load(story.getImages())
.apply(options)
.into(holder.mImageView);
}
@Override
public int getItemCount() {
return mDatas.size();
}
static class ViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.title_tv)
TextView mTitleTextView;
@BindView(R.id.image_view)
ImageView mImageView;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
ZhiHuPagerAdapter
package net.bwie.month0301.adapter;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import java.util.List;
public class ZhiHuPagerAdapter extends FragmentPagerAdapter {
// 标题数据
private String[] mTitles = new String[]{"最新消息", "过往消息"};
// 碎片集合
private List<Fragment> mFragmentList;
public ZhiHuPagerAdapter(FragmentManager fm, List<Fragment> fragmentList) {
super(fm);
mFragmentList = fragmentList;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
@Override
public int getCount() {
return mFragmentList.size();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return mTitles[position];
}
}
MyApplication
package net.bwie.month0301.application;
import android.app.Application;
import net.bwie.month0301.retrofit.MyJsonConverter;
import net.bwie.month0301.bean.DaoMaster;
import net.bwie.month0301.bean.DaoSession;
import org.greenrobot.greendao.database.Database;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
public class MyApplication extends Application {
private static Retrofit sRetrofit;
private static DaoSession sDaoSession;
@Override
public void onCreate() {
super.onCreate();
initRetrofit();
initGreenDAO();
}
// 想要使用数据库助手等类,需要先将bean对应指定的表和列名再make一下,即可使用
private void initGreenDAO() {
// 创建数据库助手
DaoMaster.DevOpenHelper helper = new DaoMaster.DevOpenHelper(this, "msg.db");
// 通过助手创建数据库
Database db = helper.getWritableDb();
// 数据库对应了一个会话
sDaoSession = new DaoMaster(db).newSession();
}
private void initRetrofit() {
sRetrofit = new Retrofit.Builder()
.baseUrl("https://news-at.zhihu.com/")// https://news-at.zhihu.com/api/4/version/android/2.3.0
.addConverterFactory(new MyJsonConverter())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
public static Retrofit getRetrofit() {
return sRetrofit;
}
public static DaoSession getDaoSession() {
return sDaoSession;
}
}
CheckVersionBean
DetailMsgBean
MsgBean
StoriesBean
ZhiHuFragment
package net.bwie.month0301.fragment;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import net.bwie.month0301.R;
import net.bwie.month0301.adapter.MsgAdapter;
import net.bwie.month0301.application.MyApplication;
import net.bwie.month0301.bean.MsgBean;
import net.bwie.month0301.bean.StoriesBean;
import net.bwie.month0301.bean.StoriesBeanDao;
import net.bwie.month0301.httpservice.MsgHttpService;
import org.greenrobot.greendao.query.Query;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Action;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
* 最新消息和过往消息的容器
*/
public class ZhiHuFragment extends Fragment {
// 最新消息标记、过往消息标记
public static final int TYPE_NEW = 0;
public static final int TYPE_OLD = 1;
private int mType;
@BindView(R.id.recycler_view)
RecyclerView mRecyclerView;
private MsgAdapter mAdapter;
// 创建带有参数的fragment的模板(该方法在需要创建碎片实例的地方调用)
public static ZhiHuFragment newInstance(int type) {
Bundle args = new Bundle();
args.putInt("type", type);
ZhiHuFragment fragment = new ZhiHuFragment();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mType = getArguments().getInt("type");
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_zhihu, container, false);
ButterKnife.bind(this, rootView);
mAdapter = new MsgAdapter(getContext());
mRecyclerView.setAdapter(mAdapter);
// 获取网络数据
loadDataFromNetwork();
return rootView;
}
private void loadDataFromNetwork() {
MsgHttpService httpService = MyApplication.getRetrofit()
.create(MsgHttpService.class);
Observable<MsgBean> observable = null;
if (mType == TYPE_NEW) {
observable = httpService.getNewMsgObservable();
} else {
observable = httpService.getBeforeMsgObservable();
}
observable.subscribeOn(Schedulers.io())
.map(new Function<MsgBean, List<StoriesBean>>() {
@Override
public List<StoriesBean> apply(MsgBean msgBean) throws Exception {
return msgBean.getStories();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<StoriesBean>>() {
@Override
public void accept(List<StoriesBean> storiesBeans) throws Exception {
Log.d("451", "网络数据请求成功时");
// 网络数据下载成功后,加入数据库中
cacheDatasToDB(storiesBeans);
}
}, new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
Log.d("451", "网络数据请求失败时, " + throwable.getMessage());
showDatasFromDB();
}
}, new Action() {
@Override
public void run() throws Exception {
Log.d("451", "在成功执行完全部操作后,最后执行该方法");
// 读取数据库中的数据并展示
showDatasFromDB();
}
});
}
// 将网络数据缓存至数据库
private void cacheDatasToDB(List<StoriesBean> storiesBeans) {
StoriesBeanDao dao = MyApplication.getDaoSession()
.getStoriesBeanDao();
for (StoriesBean story : storiesBeans) {
dao.insert(story);// 主键_id已经在自定义json转换器中初始化完毕
}
}
// 从数据库读取数据并展示
private void showDatasFromDB() {
StoriesBeanDao dao = MyApplication.getDaoSession()
.getStoriesBeanDao();
Query<StoriesBean> query = dao.queryBuilder()
.build();
List<StoriesBean> datas = query.list();
mAdapter.addDatas(datas);
}
}
CheckVersionHttpService
package net.bwie.month0301.httpservice;
import net.bwie.month0301.bean.CheckVersionBean;
import io.reactivex.Observable;
import retrofit2.http.GET;
/**
* 检查版本更新的接口
*/
public interface CheckVersionHttpService {
@GET("api/4/version/android/2.3.0")
Observable<CheckVersionBean> getCheckVersionObservable();
}
DetailHttpService
package net.bwie.month0301.httpservice;
import net.bwie.month0301.bean.DetailMsgBean;
import io.reactivex.Observable;
import retrofit2.http.GET;
import retrofit2.http.Path;
public interface DetailHttpService {
// Restful风格的url地址,在变化参数的位置使用{自定义参数名},并使用@Path
@GET("api/4/news/{id}")
Observable<DetailMsgBean> getDetailObservable(@Path("id") int id);
}
MsgHttpService
package net.bwie.month0301.httpservice;
import net.bwie.month0301.bean.MsgBean;
import io.reactivex.Observable;
import retrofit2.http.GET;
/**
* 最新/过往消息请求接口
*/
public interface MsgHttpService {
@GET("api/4/news/latest")
Observable<MsgBean> getNewMsgObservable();
@GET("api/4/news/before/20131119")
Observable<MsgBean> getBeforeMsgObservable();
}
MyJsonConverter
package net.bwie.month0301.retrofit;
import net.bwie.month0301.bean.MsgBean;
import net.bwie.month0301.bean.StoriesBean;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import okhttp3.ResponseBody;
import retrofit2.Converter;
import retrofit2.Retrofit;
/**
* 手动解析JSON数据,这样将网络数据中的List<String>可以转换为简单的一个String
*/
public class MyJsonConverter extends Converter.Factory {
// 将响应体转为json字符串,我们再去手动解析
@Override
public Converter<ResponseBody, MsgBean> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
return new Converter<ResponseBody, MsgBean>() {
@Override
public MsgBean convert(ResponseBody responseBody) throws IOException {
// 获取JSON字符串
String json = responseBody.string();
try {
// 手动解析处理List<String> -> String
JSONObject msgObject = new JSONObject(json);
MsgBean msg = new MsgBean();
JSONArray storiesArray = msgObject.getJSONArray("stories");
List<StoriesBean> storiesList = new ArrayList<>();
for (int i = 0; i < storiesArray.length(); i++) {
JSONObject storyObject = storiesArray.getJSONObject(i);
StoriesBean story = new StoriesBean();
long _id = System.currentTimeMillis();
story.set_id(_id);// 主键_id
int id = storyObject.getInt("id");
story.setId(id);// 文章id,是一个内容
String title = storyObject.getString("title");
story.setTitle(title);
// 将List ——》 String
JSONArray imagesArray = storyObject.getJSONArray("images");
String image = imagesArray.getString(0);
story.setImages(image);
storiesList.add(story);
}
msg.setStories(storiesList);
return msg;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
};
}
}
activity_detail
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="net.bwie.month0301.activity.DetailActivity">
<!--能够实现标题栏滑动的容器:滑动功能-->
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!--更强调滑动效果:折叠-->
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/collapsing_toolbar_layout"
android:layout_width="match_parent"
app:contentScrim="@color/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
android:layout_height="wrap_content">
<!--展开后的大标题栏-->
<ImageView
android:id="@+id/title_bg_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<!--折叠后的标题栏-->
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="56dp"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_height="match_parent">
<WebView
android:id="@+id/web_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="net.bwie.month0301.MainActivity">
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
app:tabIndicatorColor="@color/colorAccent"
app:tabTextColor="#ffffff"
android:background="@color/colorPrimary"
app:tabSelectedTextColor="@color/colorAccent"
android:layout_width="match_parent"
android:layout_height="56dp"/>
<android.support.v4.view.ViewPager
android:id="@+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
fragment_zhihu
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
item_msg
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image_view"
android:layout_alignParentRight="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/title_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
整个工程的build.gradle
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'
// greenDAO
// 整个工程中的build.gradle中添加:
classpath 'org.greenrobot:greendao-gradle-plugin:3.2.2'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
APP-------------------------------------------------------
apply plugin: 'com.android.application'
//app模块下的build.gradle中添加:
apply plugin: 'org.greenrobot.greendao'
android {
compileSdkVersion 26
defaultConfig {
applicationId "net.bwie.month0301"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
// Glide
compile 'com.github.bumptech.glide:glide:4.5.0'
// Retrofit
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
// RxJava+Retrofit
compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
compile 'io.reactivex.rxjava2:rxjava:2.1.5'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
// app模块下的build.gradle中添加:apply plugin: 'org.greenrobot.greendao'
compile 'org.greenrobot:greendao:3.2.2'
// ButterKnife
// 在app文件夹的build.gradle中一起导入:
compile 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
// EventBus
compile 'org.greenrobot:eventbus:3.1.1'
// SectionedRecyclerViewAdapter
compile 'com.truizlop.sectionedrecyclerview:library:1.2.0'
implementation 'com.android.support:design:26.1.0'
implementation 'com.android.support:recyclerview-v7:26.1.0'
}