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'
}