Collection聚合了項目搭建的一些基本模塊,節約開發者時間,協助項目的快速搭建,RecyclerView+Adapter+Retrofit+RxJava+MVP+DataManager+基本Base,可以知足一個項目的基本實現。java
github地址:github.com/usernameyan…
掘金地址:juejin.im/post/5ab998…
1.更新Realm數據庫依賴。
2.更新RxJava、rxandroid、retrofit、converter-gson、adapter-rxjava2依賴。
3.封裝好Fragment之間的交互,項目中能夠選擇使用一個Activity來做爲跟容器,其它實現頁面統一使用fragment來實現。
4.collectionLibary中的Config配置類增長json字段過濾、網絡請求超時設置、網絡請求頭設置(全局請求頭)。
5.增長自動換行佈局。
6.Realm增長按數據字段查詢和刪除接口。
7.網絡請求類型HttpType增長json類型請求參數。
8.網絡請求增長個別接口請求頭設置。
9.增長適配不一樣手機像素。android
1.增長自定義控件TabLayout。git
1.RxJava的依賴更新。 2.修正RecyclerView頭部佈局不能鋪滿問題。 3.PopupWindow的使用。 4.DisplayUtils工具類對狀態欄的修改。github
1.修正Retrofit DEFAULT_POST請求方式指向錯誤。 2.Retrofit 數據解析兼容沒有公用been類,能夠指定公用been類和不指定公用been類、或者混合使用。 3.Realm增長數據遷移(數據庫字段增長或移除)。 4.增長几種通用的Dialog彈窗,提供方法自定義。 5.提供幾種比較經常使用的Utils工具類數據庫
1.增長DataManager用來統一管理數據請求,包括Retrofit的請求、SharePreference以及Realm的數據請求。
2.Retrofit的請求的整合。 3.PullToRefreshRecyclerView的空佈局bug修改。json
1.框架的引入api
2.PullToRefreshRecyclerView的使用緩存
3.BaseRecyclerViewAdapter的使用微信
4.MVP+RxJava+Retrofit的封裝使用網絡
5.DataManager的使用
6.Base的使用
7.CustomView的使用
implementation 'com.youngman:collectionlibrary:1.2.8'
Error:Could not find com.android.support:appcompat-v7:27.x.x. 由於library的Support Repository是27.x.x,可能跟項目有所衝突,若是sdk已經裝了27仍是會出現一樣的錯誤。 解決辦法:在項目根build.gradle中加入 maven { url "maven.google.com" }
<com.youngmanster.collectionlibrary.refreshrecyclerview.pulltorefresh.PullToRefreshRecyclerView
android:id="@+id/recycler_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
複製代碼
mRecyclerView.setPullRefreshEnabled(true);
mRecyclerView.setLoadMoreEnabled(true);
複製代碼
mRecyclerView.setPullRefreshEnabled(true);
mRecyclerView.setLoadMoreEnabled(true);
mRecyclerView.setRefreshView(new DefinitionAnimationRefreshHeaderView(getActivity()));
mRecyclerView.setLoadMoreView(new DefinitionAnimationLoadMoreView(getActivity()));
複製代碼
/***
* 下拉刷新分爲4個狀態
*/
//下拉的狀態(還沒到下拉到固定的高度時)
public static final int STATE_PULL_DOWN=0;//
//下拉到固定高度提示釋放刷新的狀態
public static final int STATE_RELEASE_REFRESH=1;
//刷新狀態
public static final int STATE_REFRESHING=2;
//刷新完成
public static final int STATE_DONE=3;
複製代碼
/***
* 加載更多分爲3個狀態
*/
//正在加載
public final static int STATE_LOADING = 0;
//加載完成
public final static int STATE_COMPLETE = 1;
//沒有數據
public final static int STATE_NODATA= 2;
複製代碼
#####自定義刷新的步驟:
在initView()作自定義佈局、相關動畫的初始化,最後在initView()方法的最後面添加如下代碼便可。
//mContainer =LayoutInflater.from(context).inflate(R.layout.layout_default_arrow_refresh, null);
//把刷新頭部的高度初始化爲0
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
lp.setMargins(0, 0, 0, 0);
this.setLayoutParams(lp);
this.setPadding(0, 0, 0, 0);
addView(mContainer, new LayoutParams(LayoutParams.MATCH_PARENT, 0));//把刷新佈局添加進去
setGravity(Gravity.BOTTOM);
//測量高度
measure(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
mMeasuredHeight = getMeasuredHeight();
複製代碼
setRefreshTimeVisible(boolean show)是用來設置是否顯示刷新時間控件,在默認刷新樣式中經過mRecyclerView.setRefreshTimeVisible(false)便可隱藏刷新時間,若是在自定義的佈局中沒有這項這個方法就能夠忽略。
destroy()是用來關掉改頁面時把刷新View的一些動畫等釋放,防止內存泄漏。
在構造函數中
onStateChangeListener=this;
複製代碼
onStateChange的模板樣式
@Override
public void onStateChange(int state) {
//下拉時狀態相同不作繼續保持原有的狀態
if (state == mState) return ;
//根據狀態進行動畫顯示
switch (state){
case STATE_PULL_DOWN://跟隨手指下拉的狀態
//clearAnim();
//startAnim();
break;
case STATE_RELEASE_REFRESH://下拉釋放
break;
case STATE_REFRESHING://正在進行刷新
//clearAnim();
//startAnim();
scrollTo(mMeasuredHeight);//這段代碼須要添加
break;
case STATE_DONE://刷新完成
break;
}
mState = state;//狀態的更新
}
複製代碼
在initView()作自定義佈局、相關動畫的初始化,最後在initView()方法的最後面添加如下代碼便可。
//mContainer = LayoutInflater.from(context).inflate(R.layout.layout_definition_animation_loading_more, null);
addView(mContainer);
setGravity(Gravity.CENTER);
複製代碼
destroy()是用來關掉改頁面時把刷新View的一些動畫等釋放,防止內存泄漏。
在setState()進行狀態切換後的相關操做邏輯,模板樣式:
@Override
public void setState(int state) {
switch (state){
case STATE_LOADING://正在加載
//loadMore_Ll.setVisibility(VISIBLE);
//noDataTv.setVisibility(INVISIBLE);
//animationDrawable= (AnimationDrawable) loadingIv.getDrawable();
//animationDrawable.start();
this.setVisibility(VISIBLE);//這段代碼須要添加
break;
case STATE_COMPLETE:
//if(animationDrawable!=null){
// animationDrawable.stop();
//}
this.setVisibility(GONE);//這段代碼須要添加
break;
case STATE_NODATA:
//loadMore_Ll.setVisibility(INVISIBLE);
//noDataTv.setVisibility(VISIBLE);
//animationDrawable= (AnimationDrawable) loadingIv.getDrawable();
//animationDrawable.start();
this.setVisibility(VISIBLE);//這段代碼須要添加
break;
}
mState = state;//狀態的更新
}
複製代碼
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.youngmanster.collectionlibrary.refreshrecyclerview.pulltorefresh.PullToRefreshRecyclerView
android:id="@+id/recycler_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
複製代碼
mRecyclerView.setLoadMoreEnabled(true);
mRecyclerView.setLoadMoreView(new DefinitionAnimationLoadMoreView(getActivity()));
swl_Refresh.setColorSchemeResources(R.color.colorAccent);
swl_Refresh.setOnRefreshListener(this);
複製代碼
因爲PullToRefreshRecyclerView的下拉刷新和下拉加載更多完成時會自動刷新Adapter,而SwipeRefreshLayout刷新完成時須要手動進行notifyDataSetChanged刷新適配器。
View emptyView = LayoutInflater.from(getActivity()).inflate(R.layout.layout_empty,null);
mRecyclerView.setEmptyView(emptyView);
複製代碼
mRecyclerView.setNoMoreDate(true);
複製代碼
mRecyclerView.setAutoRefresh();
複製代碼
mRecyclerView.isLoading() //是否正在加載更多
mRecyclerView.loadMoreComplete() //加載更多完成
mRecyclerView.isRefreshing() //是否正在刷新
mRecyclerView.refreshComplete(); //刷新數據完成
複製代碼
onRecyclerViewRefresh()
onRecyclerViewLoadMore()
複製代碼
//下拉刷新、上拉加載更多設置
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
refreshRv.setLayoutManager(linearLayoutManager);
refreshRv.setRefreshView(new DefinitionAnimationRefreshHeaderView(getActivity()));
refreshRv.setLoadMoreView(new DefinitionAnimationLoadMoreView(getActivity()));
refreshRv.setPullRefreshEnabled(true);
refreshRv.setLoadMoreEnabled(true);
refreshRv.setRefreshAndLoadMoreListener(this);
============================下面是下拉刷新上拉加載更多的一些操做=========================================
//刷新頁面
@Override
public void refreshUI(List<WeChatNews> newsList) {
//先作數據拼接
if(newsList!=null){
if (pageSize == 1) {
mDatas.clear();
mDatas.addAll(newsList);
} else {
mDatas.addAll(newsList);
}
}
if (weChatFeaturedAdapter == null) {
//配合StateView使用,StateView具體使用下面有介紹
if(mDatas.size()==0){
stateView.showViewByState(StateView.STATE_EMPTY);
}else{
stateView.showViewByState(StateView.STATE_NO_DATA);
}
weChatFeaturedAdapter = new WeChatFeaturedAdapter(getActivity(), mDatas, refreshRv);
refreshRv.setAdapter(weChatFeaturedAdapter);
} else {
//判斷該操做是下拉刷新仍是上拉加載更多
if (refreshRv.isLoading()) {
refreshRv.loadMoreComplete();
//若是沒有更多數據就顯示沒有更多數據提示
if (newsList==null||newsList.size() == 0) {
refreshRv.setNoMoreDate(true);
}
} else if (refreshRv.isRefreshing()) {
refreshRv.refreshComplete();
}
}
}
複製代碼
@Override
public void onDestroy() {
super.onDestroy();
if(mRecyclerView!=null){
mRecyclerView.destroy();
}
}
複製代碼
在BaseRecyclerViewAdapter中的BaseViewHolder進行佈局轉化,同時定義了一些比較基本的View操做,使用簡單。 #####(1)使用代碼:
public class PullToRecyclerViewAdapter extends BaseRecyclerViewAdapter<String> {
public PullToRecyclerViewAdapter(Context mContext, List<String> mDatas, PullToRefreshRecyclerView pullToRefreshRecyclerView) {
super(mContext, R.layout.item_pull_refresh, mDatas, pullToRefreshRecyclerView);
}
@Override
protected void convert(BaseViewHolder baseViewHolder, String s) {
baseViewHolder.setText(R.id.title,s);
}
}
複製代碼
public BaseRecyclerViewAdapter(Context mContext, int mLayoutResId, List<T> mDatas, PullToRefreshRecyclerView pullToRefreshRecyclerView) {
this.mContext = mContext;
this.mLayoutResId = mLayoutResId;
this.mDatas = mDatas;
this.mRecyclerView=pullToRefreshRecyclerView;
}
public BaseRecyclerViewAdapter(Context mContext, int mLayoutResId, List<T> mDatas) {
this.mContext = mContext;
this.mLayoutResId = mLayoutResId;
this.mDatas = mDatas;
}
複製代碼
itemClickAdapter.setOnItemClickListener(new BaseRecyclerViewAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
showToast(mDatas.get(position).getTitle());
}
});
複製代碼
itemClickAdapter.setOnItemLongClickListener(new BaseRecyclerViewAdapter.onItemLongClickListener() {
@Override
public boolean onItemLongClick(View view, int position) {
showToast("進行長按操做");
return true;
}
});
複製代碼
//事件監聽
itemClickAdapter.setOnItemClickListener(this);
itemClickAdapter.setOnItemLongClickListener(this);
//點擊實現
@Override
public void onItemClick(View view, int position) {
showToast(mDatas.get(position).getTitle());
}
@Override
public boolean onItemLongClick(View view, int position) {
showToast("進行長按操做");
return true;
}
複製代碼
public class MultipleAdapter extends BaseRecyclerViewMultiItemAdapter<MultiItem> {
private int mHeight;
public MultipleAdapter(Context mContext, List<MultiItem> mDatas) {
super(mContext, mDatas);
mHeight = DisplayUtil.dip2px(mContext, 100);
addItemType(MultiItem.TYPE_TEXT, R.layout.item_main);
addItemType(MultiItem.TYPE_IMG, R.layout.item_img);
addItemType(MultiItem.TYPE_TEXT_IMG, R.layout.item_click);
}
@Override
protected void convert(BaseViewHolder baseViewHolder, MultiItem multiItem) {
switch (baseViewHolder.getItemViewType()) {
case MultiItem.TYPE_TEXT:
baseViewHolder.getView(R.id.card_view).getLayoutParams().height = mHeight;
baseViewHolder.setText(R.id.title, multiItem.getTitle());
break;
case MultiItem.TYPE_IMG:
baseViewHolder.setImageResource(R.id.ivImg, multiItem.getRes());
break;
case MultiItem.TYPE_TEXT_IMG:
baseViewHolder.setImageResource(R.id.ivImg, multiItem.getRes());
baseViewHolder.setText(R.id.titleTv, multiItem.getTitle());
break;
}
}
複製代碼
ItemTouchHelper.Callback callback=new BaseRecycleItemTouchHelper(dragAndDeleteAdapter);
ItemTouchHelper itemTouchHelper=new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(mRecyclerView);
複製代碼
@Override
public void onDragAndDeleteFinished() {
mRecyclerView.postDelayed(new Runnable() {
@Override
public void run() {
dragAndDeleteAdapter.notifyDataSetChanged();
showToast("操做完成");
}
},300);
}
複製代碼
public class Config {
/**必傳參數**/
//是否爲BuildConfig.DEBUG,日誌輸出須要
public static boolean DEBUG;
//設置Context
public static Context CONTEXT;
/**Retrofit**/
//網絡請求的域名
public static String URL_DOMAIN;
//網絡緩存地址
public static String URL_CACHE;
//設置OkHttp的緩存機制的最大緩存時間,默認爲一天
public static long MAX_CACHE_SECONDS= 60 * 60 * 24;
//緩存最大的內存,默認爲10M
public static long MAX_MEMORY_SIZE=10 * 1024 * 1024;
//設置該參數能夠去掉json某個字段不解析,好比EXPOSEPARAM=「data」,json的data字段內容不被解析
public static String EXPOSEPARAM;
//設置網絡請求json通用解析類
public static Class MClASS;
/**SharePreference**/
public static String USER_CONFIG;
/**Realm**/
public static RealmMigration realmMigration;
public static int realmVersion=0;
public static String realmName="myRealm.realm";
/***請求接口超時設定**/
public static int CONNECT_TIMEOUT_SECONDS=60;
public static int READ_TIMEOUT_SECONDS=60;
public static int WRITE_TIMEOUT_SECONDS=60;
/***設置全局請求頭***/
public static Map<String,String> HEADERS;
}
複製代碼
private void config(){
Config.DEBUG= BuildConfig.DEBUG;//這個若是是測試時,日誌輸出,網絡請求相關信息輸出
Config.URL_CACHE=AppConfig.URL_CACHE;//OkHttp緩存地址
Config.CONTEXT=this;//這個是必傳
Config.MClASS= Result.class;//若是項目的json數據格式統一能夠設置一個統一的been類
Config.URL_DOMAIN="http://api.tianapi.com/";//網絡請求域名
}
複製代碼
public class Result<T> implements Serializable {
private int code;
private String msg;
private T newslist;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getNewslist() {
return newslist;
}
public void setNewslist(T newslist) {
this.newslist = newslist;
}
}
複製代碼
public class WeChatWorldNewsPresenter extends WeChatWorldNewsContract.Presenter {
@Override
public void requestWorldNews(int page, int num) {
RequestBuilder<Result<List<WeChatNews>>> resultRequestBuilder = new RequestBuilder<>(new RxObservableListener<Result<List<WeChatNews>>>(mView) {
@Override
public void onNext(Result<List<WeChatNews>> result) {
mView.refreshUI(result.getNewslist());
}
});
resultRequestBuilder
.setUrl(ApiUrl.URL_WETCHAT_WORLD_NEWS)
.setTransformClass(WeChatNews.class)
.setRequestParam(ApiClient.getRequiredBaseParam())
.setHttpTypeAndReqType(RequestBuilder.HttpType.DEFAULT_GET, RequestBuilder.ReqType.DEFAULT_CACHE_LIST)
.setParam("page",page)
.setParam("num",num);
rxManager.addObserver(DataManager.getInstance(DataManager.DataType.RETROFIT).httpRequest(resultRequestBuilder));
}
}
複製代碼
public class WeChatChinaNewsDefinitionPresenter extends WeChatChinaNewsContract.Presenter {
@Override
public void requestChinaNews(int page, int num) {
String filePath = AppConfig.STORAGE_DIR + "wechat/china";
String fileName = "limttime.t";
RequestBuilder resultRequestBuilder = new RequestBuilder<>(new RxObservableListener<Result<List<WeChatNews>>>(mView) {
@Override
public void onNext(Result<List<WeChatNews>> result) {
mView.refreshUI(result.getNewslist());
}
}).setFilePathAndFileName(filePath, fileName)
.setTransformClass(WeChatNews.class)
.setUrl(ApiUrl.URL_WETCHAT_CHINA_NEWS)
.setRequestParam(ApiClient.getRequiredBaseParam())
.setHttpTypeAndReqType(RequestBuilder.HttpType.DEFAULT_GET,RequestBuilder.ReqType.DISK_CACHE_LIST_LIMIT_TIME)
.setParam("page", page)
.setParam("num", num);
rxManager.addObserver(DataManager.getInstance(DataManager.DataType.RETROFIT).httpRequest(resultRequestBuilder));
}
}
複製代碼
resultRequestBuilder.setUrl(ApiUrl.URL_ABOUT_US_RULE)
.setTransformClass(ContractUsInfo.class)
.setParam("code","contact_us")
.setHeader("Accept-Language",MultiLanguageUtils.getInstance().getRequestLanguage())
.setHttpTypeAndReqType(RequestBuilder.HttpType.JSON_PARAM_POST, RequestBuilder.ReqType.NO_CACHE_MODEL);
rxManager.addObserver(DataManager.getInstance(DataManager.DataType.RETROFIT).httpRequest(resultRequestBuilder));
複製代碼
void onNext(T result);
void onComplete();
void onError(NetWorkCodeException.ResponseThrowable e);
複製代碼
protected RxObservableListener(BaseView view){
this.mView = view;
}
protected RxObservableListener(BaseView view, String errorMsg){
this.mView = view;
this.mErrorMsg = errorMsg;
}
複製代碼
提供了三種方式
public enum DataType {
RETROFIT, REALM, SHAREPREFERENCE
}
複製代碼
經過DataManager.getInstance(DataManager.DataType.XXX)可得到對應的請求方式。
須要在項目的Application初始化Retrofit的一些參數
//基本配置
Config.DEBUG= BuildConfig.DEBUG;
Config.CONTEXT=this;
//Retrofit配置
Config.URL_CACHE=AppConfig.URL_CACHE;
Config.MClASS= Result.class;//若是項目的json數據格式統一能夠設置一個統一的been類
Config.URL_DOMAIN="http://api.tianapi.com/";
複製代碼
RequestBuilder<Result<List<WeChatNews>>> resultRequestBuilder = new RequestBuilder<>(new RxObservableListener<Result<List<WeChatNews>>>(mView) {
@Override
public void onNext(Result<List<WeChatNews>> result) {
mView.refreshUI(result.getNewslist());
}
});
resultRequestBuilder
.setUrl(ApiUrl.URL_WETCHAT_FEATURED)
.setTransformClass(WeChatNews.class)
.setRequestParam(ApiClient.getRequiredBaseParam())
.setParam("page",page)
.setParam("num",num);
rxManager.addObserver(DataManager.getInstance(DataManager.DataType.RETROFIT).httpRequest(resultRequestBuilder));
複製代碼
RequestBuilder<WeChatNewsResult> resultRequestBuilder = new RequestBuilder<>(new RxObservableListener<WeChatNewsResult>(mView) {
@Override
public void onNext(WeChatNewsResult result) {
mView.refreshUI(result.getNewslist());
}
});
resultRequestBuilder
.setUrl(ApiUrl.URL_WETCHAT_FEATURED)
.setTransformClass(WeChatNewsResult.class)
.setRequestParam(ApiClient.getRequiredBaseParam())
.setParam("page",page)
.setParam("num",num);
rxManager.addObserver(DataManager.getInstance(DataManager.DataType.RETROFIT).httpRequest(resultRequestBuilder));
複製代碼
RequestBuilder<WeChatNewsResult> resultRequestBuilder = new RequestBuilder<>(new RxObservableListener<WeChatNewsResult>(mView) {
@Override
public void onNext(WeChatNewsResult result) {
mView.refreshUI(result.getNewslist());
}
});
resultRequestBuilder
.setUrl(ApiUrl.URL_WETCHAT_FEATURED)
.setTransformClass(WeChatNewsResult.class)
.setUserCommonClass(false)
.setRequestParam(ApiClient.getRequiredBaseParam())
.setParam("page",page)
.setParam("num",num);
rxManager.addObserver(DataManager.getInstance(DataManager.DataType.RETROFIT).httpRequest(resultRequestBuilder));
複製代碼
public enum ReqType {
//沒有緩存
NO_CACHE_MODEL,
No_CACHE_LIST,
//默認Retrofit緩存
DEFAULT_CACHE_MODEL,
DEFAULT_CACHE_LIST,
//自定義磁盤緩存,返回List
DISK_CACHE_LIST_LIMIT_TIME,
//自定義磁盤緩存,返回Model
DISK_CACHE_MODEL_LIMIT_TIME,
//自定義磁盤緩存,沒有網絡返回磁盤緩存,返回List
DISK_CACHE_NO_NETWORK_LIST,
//自定義磁盤緩存,沒有網絡返回磁盤緩存,返回Model
DISK_CACHE_NO_NETWORK_MODEL,
//保存網絡數據到本地磁盤,能夠設定網絡請求是否返回數據
DISK_CACHE_NETWORK_SAVE_RETURN_MODEL,
DISK_CACHE_NETWORK_SAVE_RETURN_LIST,
}
複製代碼
public enum HttpType {
//GET請求
DEFAULT_GET,
//POST請求
DEFAULT_POST,
//若是請求URL出現中文亂碼,可選擇這個
FIELDMAP_POST,
//json格式請求參數
JSON_PARAM_POST,
//上傳一張圖片
ONE_MULTIPART_POST
}
複製代碼
RequestBuilder<Result<List<WeChatNews>>> resultRequestBuilder = new RequestBuilder<>(new RxObservableListener<Result<List<WeChatNews>>>(mView) {
@Override
public void onNext(Result<List<WeChatNews>> result) {
mView.refreshUI(result.getNewslist());
}
});
resultRequestBuilder
.setUrl(ApiUrl.URL_WETCHAT_WORLD_NEWS)
.setTransformClass(WeChatNews.class)
.setHttpTypeAndReqType(RequestBuilder.HttpType.DEFAULT_GET, RequestBuilder.ReqType.DEFAULT_CACHE_LIST)
.setRequestParam(ApiClient.getRequiredBaseParam())
.setParam("page",page)
.setParam("num",num);
複製代碼
<T> DisposableObserver<ResponseBody> httpRequest(RequestBuilder<T> requestBuilder);
複製代碼
Observable<WeChatAccessToken> observable = RetrofitManager.getNoCacheApiService(ApiService.class)
.getWeChatStr(ApiUrl.URL_WECHAT_HOST + ApiUrl.ACCESS_TOKEN, reqParams);
DisposableObserver<WeChatAccessToken> observer = observable
.compose(RxSchedulers.<WeChatAccessToken>io_main())
.subscribeWith(new RxSubscriber<WeChatAccessToken>() {
@Override
public void _onNext(WeChatAccessToken weChatAccessToken) {
getUserInfo(weChatAccessToken);
}
@Override
public void _onError(NetWorkCodeException.ResponseThrowable responseThrowable) {
showToast(R.string.wx_LoginResultEmpty);
hideLoadingDialog();
finish();
}
@Override
public void _onComplete() {
}
});
rxManager.addObserver(observer);
複製代碼
public interface ApiService {
/**
* 微信精選
* @param url
* @param map
* @return
*/
@GET
Observable<Result<List<WeChatNews>>> getWeChatFeaturedNews(@Url String url, @QueryMap Map<String,Object> map);
}
複製代碼
須要在項目的Application初始化SharePreference的一些參數
//SharePreference配置
Config.USER_CONFIG="Collection_User";
複製代碼
DataManager.getInstance(DataManager.DataType.SHAREPREFERENCE).saveByKeyWithSP("user","這
String user=DataManager.getInstance(DataManager.DataType.SHAREPREFERENCE).queryByKeyWithSP("user",String.class);是一條測試的內容");
複製代碼
//自定義保存的配置文件名、key
void saveByNameAndKeyWithSP(String name, String key, Object object);
//使用在Application配置的保存文件名
void saveByKeyWithSP(String key,Object object);
//查詢保存在自定義的配置文件的內容
<T> T queryByNameAndKeyWithSP(String name, String key, Class<T> clazz);
//查詢保存在Application設置的文件的內容
<T> T queryByKeyWithSP(String key, Class<T> clazz);
複製代碼
//Realm的配置
Config.realmVersion=0;
Config.realmName="realm.realm";
Config.realmMigration=customMigration;//數據庫數據遷移(been類字段增長移除)
複製代碼
classpath "io.realm:realm-gradle-plugin:5.0.0"
複製代碼
apply plugin: 'realm-android'
複製代碼
DataManager.getInstance(DataManager.DataType.REALM).saveOrUpdateWithPKByRealm(user);
user= (User) DataManager.getInstance(DataManager.DataType.REALM).queryFirstByRealm(User.class);
複製代碼
/**
* 保存操做
*/
void saveOrUpdateWithPKByRealm(final RealmObject bean);
void saveOrUpdateWithPKByRealm(final List<? extends RealmObject> beans);
void saveWithoutPKByRealm(final RealmObject bean);
void saveWithoutPKByRealm(final List<? extends RealmObject> beans);
/**
* 查詢操做
*/
RealmObject queryFirstByRealm(Class<? extends RealmObject> clazz);
RealmObject queryAllWithFieldByRealm(Class<? extends RealmObject> clazz, String fieldName, String value);
RealmObject queryWithFieldByRealm(Class<? extends RealmObject> clazz, String fieldName, String value)
List<? extends RealmObject> queryAllByRealm(Class<? extends RealmObject> clazz);
List<? extends RealmObject> queryAllWithSortByRealm(Class<? extends RealmObject> clazz, String fieldName,Boolean isAscendOrDescend);
/**
* 修改操做
*/
void updateParamWithPKByRealm(Class<? extends RealmObject> clazz, String primaryKeyName, Object primaryKeyValue, String fieldName,Object newValue);
/**
* 刪除操做
*/
void deleteFirstByRealm(Class<? extends RealmObject> clazz);
void deleteAllByRealm(Class<? extends RealmObject> clazz);
void deleteAllWithFieldByRealm(Class<? extends RealmObject> clazz, String fieldName, String value)
複製代碼
隨着app版本的迭代,數據庫的字段可能會增長或者移除這時候就須要用到Realm提供的RealmMigration進行設置。
public class CustomMigration implements RealmMigration {
@Override
public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {
RealmSchema schema = realm.getSchema();
for(int i = (int) (oldVersion+1);i<=newVersion;i++){
if (i == 1) {
RealmObjectSchema personSchema = schema.get("BleLabelInfo");
RealmObjectSchema multilanguageSchema = schema.create("MultiLanguage");
multilanguageSchema.addField("zhHk", String.class);
multilanguageSchema.addField("zhCn", String.class);
multilanguageSchema.addField("en", String.class);
personSchema
.addRealmObjectField("multilingualism", multilanguageSchema);
} else if (i == 2) {
RealmObjectSchema personSchema = schema.get("User");
personSchema
.addField("id", String.class);
RealmObjectSchema multilanguageSchema = schema.create("LabelRecord");
multilanguageSchema.addField("major_minor", String.class);
multilanguageSchema.addPrimaryKey("major_minor");
multilanguageSchema.addField("major", String.class);
multilanguageSchema.addField("minor", String.class);
multilanguageSchema.addField("timeMillis", long.class);
multilanguageSchema.addField("threshold",int.class);
}else if(i==3){
RealmObjectSchema personSchema = schema.get("User");
personSchema
.addField("authType", boolean.class);
}
}
}
}
複製代碼
public interface WeChatChinaNewsContract {
interface View extends BaseView {
void refreshUI(List<WeChatNews> weChatNews);
}
abstract class Presenter extends BasePresenter<View> {
public abstract void requestChinaNews(int page,int num);
}
}
複製代碼
public class WeChatChinaNewsPresenter extends WeChatChinaNewsContract.Presenter {
@Override
public void requestChinaNews(int page, int num) {
RequestBuilder resultRequestBuilder = new RequestBuilder<>(new RxObservableListener<Result<List<WeChatNews>>>(mView) {
@Override
public void onNext(Result<List<WeChatNews>> result) {
mView.refreshUI(result.getNewslist());
}
}).setUrl(ApiUrl.URL_WETCHAT_CHINA_NEWS)
.setTransformClass(WeChatNews.class)
.setRequestParam(ApiClient.getRequiredBaseParam())
.setParam("page", page)
.setParam("num", num);
rxManager.addObserver(DataManager.getInstance(DataManager.DataType.RETROFIT).httpRequest(resultRequestBuilder));
}
}
複製代碼
public class FragmentChinaNews extends BaseFragment<WeChatChinaNewsModel,WeChatChinaNewsPresenter> implements WeChatChinaNewsContract.View{
@Override
public void init() {
}
@Override
public void requestData() {
((WeChatChinaNewsPresenter)mPresenter).requestChinaNews(pageSize,PAGE_SIZE);
}
@Override
public void refreshUI(List<WeChatNews> newsList) {
}
@Override
public void onError(String errorMsg) {
}
}
複製代碼
IBaseActivity:主要提供了一個頁面的基本方法、處理了MVP之間的關聯、使用者能夠直接繼承該類使用、也能夠繼承該類實現擴展。
IBaseActivity<T extends BaseModel, E extends BasePresenter>已經進行MVP之間的傳遞和關聯。
處理好頁面銷燬以後Observables 和 Subscribers的解綁。
getLayoutId()設置佈局、init()數據初始化、requestData()請求數據,執行順序已經在IBaseActivity作好處理。
能夠繼承IBaseActivity進行擴展。
public abstract class BaseActivity<T extends BaseModel,E extends BasePresenter> extends IBaseActivity{ private Unbinder unbinder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
unbinder= ButterKnife.bind(this);
}
複製代碼
}
@Override
public void requestData() {
((WeChatFeaturedPresenter) mPresenter).requestFeaturedNews(pageSize, PAGE_SIZE);
}
複製代碼
IBaseFragment:主要提供一個頁面的基本方法,處理了MVP之間的關聯,該類已經加入了懶人加載的控制方式、使用者能夠直接繼承該類使用、也能夠繼承該類實現擴展。
IBaseFragment<T extends BaseModel,E extends BasePresenter>已經進行MVP之間的傳遞和關聯。
處理好頁面銷燬以後Observables 和 Subscribers的解綁。
加入了懶人加載方式,只有頁面顯示纔會調用requestData()請求數據,並只會調用一次。
getLayoutId()設置佈局、init()數據初始化、requestData()請求數據,執行順序已經在IBaseFragment作好處理。
能夠繼承IBaseFragment進行擴展。
public abstract class BaseFragment<T extends BaseModel,E extends BasePresenter> extends IBaseFragment {
private Unbinder unbinder;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
unbinder= ButterKnife.bind(this,mainView);
return mainView;
}
}
複製代碼
@Override
public void requestData() {
((WeChatChinaNewsPresenter)mPresenter).requestChinaNews(pageSize,PAGE_SIZE);
}
複製代碼
[站外圖片上傳中...(image-4c118c-1566984217649)]
//不顯示
public static final int STATE_NO_DATA = 0;
//正在加載
public static final int STATE_LOADING = 1;
//空數據
public static final int STATE_EMPTY = 2;
//沒有網絡
public static final int STATE_DISCONNECT=3;
複製代碼
<?xml version="1.0" encoding="utf-8"?>
<com.youngmanster.collectionlibrary.base.StateView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerInParent="true"
android:id="@+id/state_view">
</com.youngmanster.collectionlibrary.base.StateView >
複製代碼
<include layout="@layout/layout_state"/>
複製代碼
stateView.showViewByState(StateView.STATE_LOADING);
stateView.showViewByState(StateView.STATE_EMPTY);
stateView.showViewByState(StateView.STATE_NO_DATA);
stateView.showViewByState(StateView.STATE_DISCONNECT);
複製代碼
app:emptyText=""//設置空數據的文字提示
app:emptyImage=""//設置空數據顯示的圖片
app:disConnectImage=""//設置無網絡的顯示圖片
app:disConnectText=""//設置無網絡的文字提示
app:loadingText=""//設置loading的文字提示
app:loadingViewAnimation=""//設置loading的動畫文件,只是ImageView
app:tipTextColor=""//設置文字顯示顏色
app:tipTextSize=""//設置文字的大小
複製代碼
// 項目的必須權限,沒有這些權限會影響項目的正常運行
private static final String[] PERMISSIONS = new String[]{
Manifest.permission.READ_SMS,
Manifest.permission.RECEIVE_WAP_PUSH,
};
複製代碼
PermissionManager permissionManager=PermissionManager.with(this).
//必須權限
setNecessaryPermissions(PERMISSIONS);
//經過如下語句進行請求
permissionManager.requestPermissions();
複製代碼
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == PermissionManager.PERMISSION_REQUEST_CODE) {//PERMISSION_REQUEST_CODE爲請求權限的請求值
//有必須權限選擇了禁止
if (permissionManager.getShouldShowRequestPermissionsCode() == PermissionManager.EXIST_NECESSARY_PERMISSIONS_PROHIBTED) {
showToast("能夠在這裏設置從新跳出權限請求提示框");
} //有必須權限選擇了禁止不提醒
else if (permissionManager.getShouldShowRequestPermissionsCode() == PermissionManager.EXIST_NECESSARY_PERMISSIONS_PROHIBTED_NOT_REMIND) {
showToast("能夠在這裏彈出提示框提示去應用設置頁開啓權限");
permissionManager.startAppSettings();
}
}
}
複製代碼
繼承BasePopupWindow。
經過getPopupLayoutRes(R.layout.xxx)設置彈窗佈局。
經過getPopupAnimationStyleRes(R.style.xxx)設置彈窗動畫,不須要動畫能夠忽略不設置。
<style name="animation_scale" parent="android:Animation.Dialog">
<item name="android:windowEnterAnimation">@anim/scale_tip_in</item>
<item name="android:windowExitAnimation">@anim/scale_tip_out</item>
</style>
複製代碼
若是須要顯示遮層,在構造函數經過setShowMaskView(true)設置。
<com.youngmanster.collectionlibrary.customview.wraplayout.AutoLinefeedLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
//填充的內容
</com.youngmanster.collectionlibrary.customview.wraplayout.AutoLinefeedLayout>
複製代碼
[站外圖片上傳中...(image-6d936c-1566984217649)]