個人Android開發框架Collection

Collection聚合了項目搭建的一些基本模塊,節約開發者時間,協助項目的快速搭建,RecyclerView+Adapter+Retrofit+RxJava+MVP+DataManager+基本Base,可以知足一個項目的基本實現。java

github地址:github.com/usernameyan…
掘金地址:juejin.im/post/5ab998…

更新說明

v1.2.8

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

v1.2.7

1.增長自定義控件TabLayout。git

v1.2.6

1.RxJava的依賴更新。 2.修正RecyclerView頭部佈局不能鋪滿問題。 3.PopupWindow的使用。 4.DisplayUtils工具類對狀態欄的修改。github

v1.2.5

1.修正Retrofit DEFAULT_POST請求方式指向錯誤。   2.Retrofit 數據解析兼容沒有公用been類,能夠指定公用been類和不指定公用been類、或者混合使用。 3.Realm增長數據遷移(數據庫字段增長或移除)。 4.增長几種通用的Dialog彈窗,提供方法自定義。 5.提供幾種比較經常使用的Utils工具類數據庫

v1.2.4

1.增長DataManager用來統一管理數據請求,包括Retrofit的請求、SharePreference以及Realm的數據請求。
2.Retrofit的請求的整合。 3.PullToRefreshRecyclerView的空佈局bug修改。json

文章目錄

1.框架的引入api

2.PullToRefreshRecyclerView的使用緩存

  • 框架默認下拉刷新、上拉加載更多樣式
  • 自定義下拉刷新、上拉加載更多樣式
  • 上拉加載更多結合SwipeRefreshLayout使用
  • RecyclerView添加頭部、空佈局
  • 上拉加載更多實現NoMoreData、自動刷新

3.BaseRecyclerViewAdapter的使用微信

  • BaseRecyclerViewAdapter比原始Adapter代碼量減小
  • 添加Item的點擊事件
  • 添加Item的長按事件
  • 多佈局的使用
  • 添加拖拽、滑動刪除

4.MVP+RxJava+Retrofit的封裝使用網絡

  • 框架中的Retrofit+RxJava封裝的瞭解
  • 使用框架在項目須要作的操做
  • MVP+RxJava+Retrofit+OkHttp的緩存機制
  • MVP+RxJava+Retrofit+自定義磁盤緩存機制

5.DataManager的使用

  • DataManager的Retrofit請求
  • DataManager的SharePreference的使用
  • DataManager的Realm的使用

6.Base的使用

  • Base封裝了MVP和項目的基類
  • UI狀態控制StateView的使用
  • 三步實現Permission(權限)設置
  • 提供幾種比較經常使用的Dialog彈框
  • 提供幾種比較經常使用的PopupWindow彈框
  • 使用DisplayUtils修改狀態欄
  • 提供幾種比較經常使用的Utils工具類

7.CustomView的使用

  • CommonTabLayout的使用
  • OutSideFrameTabLayout的使用

1、框架總體模塊

2、框架的引入

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

三 、PullToRefreshRecyclerView的使用

1.框架默認下拉刷新、上拉加載更多樣式

(1)佈局文件
<com.youngmanster.collectionlibrary.refreshrecyclerview.pulltorefresh.PullToRefreshRecyclerView
    android:id="@+id/recycler_rv"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
複製代碼
(2)代碼設置
mRecyclerView.setPullRefreshEnabled(true);  
mRecyclerView.setLoadMoreEnabled(true);
複製代碼

二、自定義下拉刷新、上拉加載更多樣式

(1)代碼設置
mRecyclerView.setPullRefreshEnabled(true);
mRecyclerView.setLoadMoreEnabled(true);
mRecyclerView.setRefreshView(new DefinitionAnimationRefreshHeaderView(getActivity()));
mRecyclerView.setLoadMoreView(new DefinitionAnimationLoadMoreView(getActivity()));
複製代碼
(2)自定義刷新和加載更多樣式
在進行自定義View以前先來了解刷新和加載更多的幾種狀態:
① BasePullToRefreshView刷新:
/***
 * 下拉刷新分爲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;
複製代碼
②BaseLoadMoreView加載更多:
/***
 * 加載更多分爲3個狀態
 */
//正在加載
public final static int STATE_LOADING = 0;
//加載完成
public final static int STATE_COMPLETE = 1;
//沒有數據
public final static int STATE_NODATA= 2;
複製代碼

#####自定義刷新的步驟:

①自定義View繼承BasePullToRefreshView,重寫initView()、setRefreshTimeVisible(boolean show)、destroy()方法:

在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的一些動畫等釋放,防止內存泄漏。

②實現BasePullToRefreshView.OnStateChangeListener監聽(重點,主要是進行狀態切換後的相關操做邏輯)

在構造函數中

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;//狀態的更新
 }
複製代碼
自定義加載更多的步驟(包括沒有更多數據顯示的操做):
①自定義View繼承BaseLoadMoreView,重寫initView()、setState()、destroy()方法:

在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;//狀態的更新
}
複製代碼
②注意:在自定義加載更多樣式時,若是須要有沒有更多加載更多數據提示一樣須要在佈局中寫好,而後在onSatae中根據狀態對加載和沒有跟多顯示提示進行顯示隱藏操做。

三、上拉加載更多配合SwipeRefreshLayout使用

(1)佈局文件
<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>
複製代碼
(2)代碼設置
mRecyclerView.setLoadMoreEnabled(true);
mRecyclerView.setLoadMoreView(new DefinitionAnimationLoadMoreView(getActivity()));
swl_Refresh.setColorSchemeResources(R.color.colorAccent);
swl_Refresh.setOnRefreshListener(this);
複製代碼
(3)注意的問題

因爲PullToRefreshRecyclerView的下拉刷新和下拉加載更多完成時會自動刷新Adapter,而SwipeRefreshLayout刷新完成時須要手動進行notifyDataSetChanged刷新適配器。

四、RecyclerView添加頭部、空佈局

(1)代碼設置
View emptyView = LayoutInflater.from(getActivity()).inflate(R.layout.layout_empty,null);
mRecyclerView.setEmptyView(emptyView);
複製代碼

五、上拉加載更多實現NoMoreData、自動刷新

(1)上拉加載更多數據的佈局設置在上面的自定義LoadingMoreView中有介紹,若是要顯示沒有更多數據提示只須要在LoadMore返回數據以後設置:
mRecyclerView.setNoMoreDate(true);
複製代碼
(2)自動刷新須要列表已經填充了數據以後再作自動刷新操做纔會生效:
mRecyclerView.setAutoRefresh();
複製代碼

六、PullToRefreshRecyclerView的其餘使用以及注意問題

(1)提供的使用方法
mRecyclerView.isLoading()  //是否正在加載更多
mRecyclerView.loadMoreComplete()  //加載更多完成
mRecyclerView.isRefreshing()  //是否正在刷新
mRecyclerView.refreshComplete();  //刷新數據完成
複製代碼
(2)刷新、加載更多接口回調,PullToRefreshRecyclerView.OnRefreshAndLoadMoreListener提供一下兩個方法:
onRecyclerViewRefresh()
onRecyclerViewLoadMore()
複製代碼
(3)使用實例部分代碼:
//下拉刷新、上拉加載更多設置
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();
		}
	}
}
複製代碼
(4)其餘注意問題
①在設置RecyclerView是要設LayoutManager
②若是使用PullToRefreshRecyclerView在Activty/Fragment中的onDestroy()調用mRecyclerView.destroy()防止內存泄漏。
@Override
public void onDestroy() {
	super.onDestroy();
	if(mRecyclerView!=null){
		mRecyclerView.destroy();
	}
}
複製代碼
③設置refreshRv.setLoadMoreEnabled(true),當填充的數據的列表size爲0的同時還經過RecyclerView設置分割線底部就會出現一個空白的item,這個item就是加載更多顯示的Item。
解決辦法:不經過RecyclerView設置分割線,直接在佈局自定義分割線。

4、BaseRecyclerViewAdapter的使用

1.BaseRecyclerViewAdapter的比原始Adapter的代碼量減少

在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);
	}
}
複製代碼
①使用者須要在繼承BaseRecyclerViewAdapter時傳入一個數據實體類型,具體的操做在convert()方法中操做。
②BaseViewHolder提供了一些經常使用View的基本操做,經過baseViewHolder.getView()可獲得佈局中的控件。
(2)BaseRecyclerViewAdapter提供了兩個構造函數
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;
}
複製代碼
主要是對PullToRefreshRecyclerView和RecyclerView的適配,使用時適配器根據須要使用對應的構造函數。

2.添加Item的點擊和長按事件

(1) Item點擊事件實現
itemClickAdapter.setOnItemClickListener(new BaseRecyclerViewAdapter.OnItemClickListener() {
	@Override
	public void onItemClick(View view, int position) {
		showToast(mDatas.get(position).getTitle());
	}
});
複製代碼
(2)Item長按事件實現
itemClickAdapter.setOnItemLongClickListener(new BaseRecyclerViewAdapter.onItemLongClickListener() {
	@Override
	public boolean onItemLongClick(View view, int position) {
		showToast("進行長按操做");
		return true;
	}
});
複製代碼
(3)也能夠實現BaseRecyclerViewAdapter.OnItemClickListener和BaseRecyclerViewAdapter.onItemLongClickListener
//事件監聽
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;
}
複製代碼

3.多佈局的使用

BaseRecyclerViewAdapter的多佈局實現須要注意的四步:
①自定義Adapter須要繼承BaseRecyclerViewMultiItemAdapter。
② 數據實體類須要繼承BaseMultiItemEntity,在getItemViewType()返回佈局類型。
③ 在自定義Adapter中的構造函數中經過addItemType()傳入不一樣類型對應的佈局。
④在自定義Adapter中的convert進行類型判斷,作相對應的操做。
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;

		}

	}
複製代碼

4.添加拖拽、滑動刪除

侷限:只針對RecyclerView,對本框架封裝的PullToRefreshRecyclerView會出現混亂。
①BaseRecyclerViewAdapter和BaseRecyclerViewMultiItemAdapter都已經封裝支持拖拽、滑動,適配器只須要根據需求繼承其中一個便可。
②框架提供了一個BaseRecycleItemTouchHelper,對於普通的左右滑動刪除、拖拽已經實現,若是想自定義能夠繼承BaseRecycleItemTouchHelper類,再重寫相對應的方法進行實現。
④在Activity/Fragment中須要實現如下代碼:
ItemTouchHelper.Callback callback=new BaseRecycleItemTouchHelper(dragAndDeleteAdapter);
ItemTouchHelper itemTouchHelper=new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(mRecyclerView);
複製代碼
⑤BaseRecyclerViewAdapter.OnDragAndDeleteListener進行操做動做完成以後的回調。
@Override
public void onDragAndDeleteFinished() {

	mRecyclerView.postDelayed(new Runnable() {
		@Override
		public void run() {
			dragAndDeleteAdapter.notifyDataSetChanged();
			showToast("操做完成");
		}
	},300);
}
複製代碼
注意:須要延時再進行邏輯操做,否則會出現數據混亂。

4、MVP+RxJava+Retrofit的封裝使用

因爲Retrofit已經封裝在DataManager中,在DataManager中有詳細的介紹,這裏只是提供一個例子讓你們瞭解如何使用MVP+RxJava+Retrofit。

1.在使用Retrofit請求網絡以前須要進行配置,在框架中提供了了Config配置類

框架中的Config總覽以下:
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;
}
複製代碼
在項目中須要根據項目須要進行配置,在Application中設置
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/";//網絡請求域名
}
複製代碼
根據項目須要定義一個通用的數據實體類,這是本例通用實體類,這個類須要設置到Applicatin中
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;
	}
}
複製代碼
舒適提醒:因爲每一個項目返回來的json數據格式有所不一樣,若是Result中表明的字段例如newslist沒有內容返回來的時候這個字段須要後臺控制不返回,若是不作處理會報解析錯誤。

3.MVP+RxJava+Retrofit+OkHttp的緩存機制

上面的緩存配置完成以後經過如下代碼便可:
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));
  }
}
複製代碼

4.MVP+RxJava+Retrofit+自定義磁盤緩存機制

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));
    }
}
複製代碼
新增json請求參數以及針對接口設置的請求頭設置
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));
複製代碼
注意:
①RxObservableListener有三個回調方法
void onNext(T result);
void onComplete();
void onError(NetWorkCodeException.ResponseThrowable e);
複製代碼
只會重寫onNext方法,其它兩個方法能夠自行選擇重寫。
②RxObservableListener提供兩個構造函數
protected RxObservableListener(BaseView view){
    this.mView = view;
}

protected RxObservableListener(BaseView view, String errorMsg){
     this.mView = view;
     this.mErrorMsg = errorMsg;
}
複製代碼
這兩個構造函數主要主要是爲了統一處理onError的,若是要自定義錯誤提醒,則能夠選擇第二個構造函數。
③經過DataManager的網絡請求方式會返回來一個DisposableObserver,須要把它經過rxManager.addObserver()添加進CompositeDisposable才能正常執行。

5、DataManager的使用(DataManager封裝了三種數據請求方式,包括Retroift、SharePreference和Realm)

1.DataManager的瞭解

提供了三種方式

public enum DataType {
	RETROFIT, REALM, SHAREPREFERENCE
}
複製代碼

經過DataManager.getInstance(DataManager.DataType.XXX)可得到對應的請求方式。

1.DataManager的Retrofit請求

(1)配置

須要在項目的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/";
複製代碼
(2)使用統一解析類、不使用統一解析類、混合使用

① 若是項目若是項目的json數據格式統一能夠設置一個統一的been類,例如上面的例子的Result類,同時要在Config類設置(下面例子都是有統一解析類):
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));
複製代碼
②若是項目沒有統一的解析been類,那麼Config類就不用設置了,在Retrofit請求的時候直接指定一個解析類就能夠了:
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));
複製代碼
③若是項目想兩種方式共存,那麼在請求的時候須要經過setUserCommonClass(false)設置才能不使用統一解析類進行解析:
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));
複製代碼
注意:DISK_CACHE_LIST_LIMIT_TIME和DISK_CACHE_MODEL_LIMIT_TIME這兩種限時使用緩存的請求方式不統一一種解析方式會出現頁面沒有數據顯示,由於在限定的時間內若是忽然轉用另一個解析實體類去解析會解析失敗,只能等過限定時間或者清除本地緩存去解決這一問題。
(2)RequestBuilder的設置(網絡請求的配置)
①數據處理的方式
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的填充
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);
複製代碼
④DataManager提供Retrofit請求的方法
<T> DisposableObserver<ResponseBody> httpRequest(RequestBuilder<T> requestBuilder);
複製代碼
(3)Retrofit的擴展
若是存在DataManager提供的方法知足不了的請求能夠經過RetrofitManager提供的getNoCacheApiService()和getApiService()得到不緩存和緩存的Retrofit,而後經過RxSubscriber進行回調。
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);
複製代碼
定義一個ApiService類
public interface ApiService {
	/**
 	* 微信精選
 	* @param url
 	* @param map
 	* @return
 	*/
	@GET
	Observable<Result<List<WeChatNews>>> getWeChatFeaturedNews(@Url String url, @QueryMap Map<String,Object> map);
}
複製代碼
(4)注意的問題
①請求的域名已經在Application設置好了,setUrl不須要填完整的url
②要區分清楚接口返回的數據時List仍是Model,從而選擇對應的ReqType
③setRequestParam能夠設置參數集合,setParam能夠單個設置
④使用DISK_CACHE_LIST_LIMIT_TIME/DISK_CACHE_MODEL_LIMIT_TIME這兩個顯示限時緩存時須要經過setFilePathAndFileName()設置保存路徑setLimtHours()設置緩存時間(單位爲:小時)
⑤若是要上傳單張圖片須要用到HttpType.ONE_MULTIPART_POST的請求方式,同時經過RequestBuilder設置MultipartBody.Part

2.DataManager的SharePreference的使用

(1)配置

須要在項目的Application初始化SharePreference的一些參數

//SharePreference配置
Config.USER_CONFIG="Collection_User";
複製代碼
(2)使用方法
DataManager.getInstance(DataManager.DataType.SHAREPREFERENCE).saveByKeyWithSP("user","這

String user=DataManager.getInstance(DataManager.DataType.SHAREPREFERENCE).queryByKeyWithSP("user",String.class);是一條測試的內容");
複製代碼
(3)DataManager提供SharePreference請求的方法
//自定義保存的配置文件名、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);
複製代碼

3.DataManager的Realm的使用

(1)配置
①須要在項目的Application初始化Realm的一些參數
//Realm的配置
Config.realmVersion=0;
Config.realmName="realm.realm";
Config.realmMigration=customMigration;//數據庫數據遷移(been類字段增長移除)
複製代碼
②在Project 的build.gradle中的dependencies加入
classpath "io.realm:realm-gradle-plugin:5.0.0"
複製代碼
③在項目 的build.gradle中的頂部加入
apply plugin: 'realm-android'
複製代碼
(2)使用方法
DataManager.getInstance(DataManager.DataType.REALM).saveOrUpdateWithPKByRealm(user);
user= (User) DataManager.getInstance(DataManager.DataType.REALM).queryFirstByRealm(User.class);
複製代碼
(3)DataManager提供Realm請求的方法
/**
 * 保存操做
 */
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)
複製代碼
(4)Realm數據遷移(been類字段增長移除)

隨着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);
        		}
    		}


	}
}
複製代碼
步驟:
  • 自定義RealmMigration,在migrate方法中進行字段的增長或者移除。
  • 在Application中升Realm的版本號Config.realmVersion往上增長。
  • 在Application設置RealmMigration,Config.realmMigration=customMigration。
(5)注意的問題
  • 自定義Realm的保存文件文成的時候須要以.realm爲後綴。

6、 Base的使用

1.Base封裝了MVP和項目的基類

(1)MVP
  • BaseModel
  • BaseView
  • BasePresenter
(2)在項目中的網絡請求+MVP的完整實現
①定義一個contract類,內部分別繼承上面的MVP base類,在這裏定義操做。
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);
    }
}
複製代碼
②Presenter的具體執行類。
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));
    }
}
複製代碼
③UI
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) {
	}
}
複製代碼
2.UI Base
(1)IBaseActivity
  • 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);
     }
    複製代碼

    }

缺陷:若是對IBaseActivity進行擴展,在具體調用時須要類型才能調用相關方法。
@Override
public void requestData() {
	((WeChatFeaturedPresenter) mPresenter).requestFeaturedNews(pageSize, PAGE_SIZE);
}
複製代碼
(2)IBaseFragment
  • 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;
     	}
     }
    複製代碼
缺陷:若是對IBaseActivity進行擴展,在具體調用時須要類型才能調用相關方法。
@Override
public void requestData() {
	((WeChatChinaNewsPresenter)mPresenter).requestChinaNews(pageSize,PAGE_SIZE);
}
複製代碼
(3)項目中的頁面大多數都是使用Fragment實現交互,只有幾個Activity做爲跟容器的實現方式:
  • Activity繼承IBaseActivity或者IBaseActivity的子類
  • 經過loadRootFragment加載根Fragment
  • 重寫onCreateFragmentAnimator進行設定Fragment之間的跳轉動畫,分別能夠設置爲DefaultHorizontalAnimator、DefaultVerticalAnimator,能夠自定義
  • 部分經常使用方法
    • onSupportVisible()/onSupportInvisible()頁面的顯示/隱藏
    • onBackPressedSupport()點擊返回按鈕回調,替換掉onBackPress方法
    • onFragmentResult相似Activity的onActivityResult
    • onNewBundle相似Activity的onNewBundle
    • isRootFragment判斷是不是跟Fragment
    • loadRootFragment加載根Fragment, 即Activity內的第一個Fragment 或 Fragment內的第一個子Fragment
    • loadMultipleRootFragment:加載多個同級根Fragment
    • showHideFragment:show一個Fragment,hide其餘同棧全部Fragment
    • showHideFragment: show一個Fragment,hide一個Fragment
    • start/startForResult/startWithPop/startWithPopTo
    • 其它方法能夠具體看IBaseFragment類

[站外圖片上傳中...(image-4c118c-1566984217649)]

(4)適配不一樣手機像素
  • 在Applicaton的onCreate中設置 Density.setDensity(this, 375f);
  • 375f表明設計稿的寬度,以dp爲單位,後面須要以f(浮點型)

3.UI狀態控制StateView的使用

(1)StateView的四種狀態:
//不顯示
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;
複製代碼
(2)StateView的使用:
①定義一個通用佈局
<?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 >
複製代碼
②添加到Ui頁面的layout中
<include layout="@layout/layout_state"/>
複製代碼
注意:上面的語句添加的layout最外層最好是LinearLayout以及設置爲android:orientation="vertical"
③經過如下語句進行狀態切換
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=""//設置文字的大小
複製代碼

3.三步實現Permission(權限)設置

(1)設置好要請求的權限
// 項目的必須權限,沒有這些權限會影響項目的正常運行
private static final String[] PERMISSIONS = new String[]{
		Manifest.permission.READ_SMS,
		Manifest.permission.RECEIVE_WAP_PUSH,
};
複製代碼
(2)權限經過PermissionManager管理
PermissionManager permissionManager=PermissionManager.with(this).
			//必須權限
			setNecessaryPermissions(PERMISSIONS);
//經過如下語句進行請求
permissionManager.requestPermissions();
複製代碼
(3)頁面重寫onRequestPermissionsResult
@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();
		}
	}
}
複製代碼
注意:若是有需求先判斷是否全部權限都已經容許以後再進入主頁面能夠經過permissionManager.isLackPermission()進行判斷,若是返回true則進行權限請求,若是返回false則進入主頁面。
  • 多個權限請求若是其中某一個被禁止提醒,會先把沒有禁止提醒的權限處理完以後再進行處理。
  • 若是是必要權限被禁止而沒有選擇禁止提醒退出以後下次會從新請求權限。
  • 若是必要權限被禁止和選擇了禁止提醒從新進入頁面在onRequestPermissionsResult會從新回調方法。
  • 使用者能夠根據onRequestPermissionsResult()方法中返回來的標誌PermissionManager.EXIST_NECESSARY_PERMISSIONS_PROHIBTED和PermissionManager.EXIST_NECESSARY_PERMISSIONS_PROHIBTED_NOT_REMIND作出對應的顯示和操做(例如彈框提示跳轉到設置頁面或者toat提示)。

4.提供幾種比較經常使用的Dialog彈框

①提供的Dialog
  • DIALOG_TEXT_TWO_BUTTON_DEFAULT:默認彈窗樣式。
  • DIALOG_TEXT_TWO_BUTTON_CUSTOMIZE:自定義彈出按鈕提示。
  • DIALOG_LOADING_PROGRASSBAR:默認加載彈框。
  • DIALOG_DISPLAY_ADVERTISING:顯示廣告圖的彈框樣式。
  • DIALOG_CHOICE_ITEM:單項選擇彈框樣式。
②自定義Dialog樣式
  • 繼承BaseDialog,經過setContentView(R.layout.dialog_list);設置彈窗佈局。
  • 在提供的initUI()方法中進行相應的邏輯設置。
③BaseDialog提供的方法
  • setContentView():設置彈框佈局樣式。
  • show():顯示彈框。
  • isShowing():判斷彈框是否顯示。
  • dismiss():彈框銷燬。
  • setCancelable():點擊返回鍵和外部不可取消。
  • setDialogCancel():點擊返回鍵能夠取消。

5.提供幾種比較經常使用的PopupWindow彈框

①BasePopupWindow提供的方法
  • BasePopupWindow(Context context) :調用該構造函數默認彈出框鋪滿全屏。
  • BasePopupWindow(Context context, int w, int h):調用該構造函數可指定彈出框大小。
  • showPopup():在屏幕中央顯示彈框。
  • showPopupAsDropDown(View anchor):在指定控件底部顯示彈框。
  • setShowMaskView(boolean isShowMaskView):設置是否顯示遮層。
  • dismiss():銷燬彈出框。
  • getPopupLayoutRes():自定義彈出框的佈局文件。
  • getPopupAnimationStyleRes():自定義彈出框的動畫文件。
②自定義PopupWindow
  • 繼承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)設置。

6.使用DisplayUtils修改狀態欄

  • setStatusBarFullTranslucentWithBlackFont(Activity act):狀態欄透明黑字。
  • setStatusBarBlackFontBgColor(Activity activity,int bgColor):修改狀態欄顏色同時字體變爲黑色。
  • setStatusBarFullTranslucent(Activity act):狀態欄透明。
  • setStatusBarColor(Activity activity, int colorResId):改變狀態欄顏色。

7.提供幾種比較經常使用的Utils工具類

  • DisplayUtils:px和dp的轉換、獲取屏幕高寬、狀態欄白底黑字、設置狀態欄顏色、設置狀態欄全屏透明、獲取狀態欄的高度、獲取ActionBar的高度。
  • FileUtils:寫文件、讀取文本文件中的內容、判斷緩存是否失效、檢查文件是否存在、刪除目錄、檢查是否安裝SD卡、刪除文件。
  • GlideUtils:Glide顯示網絡圖片、Glide實現高斯模糊。
  • LogUtils:日誌工具類。
  • NetworkUtils:網絡工具類。
  • ToastUtils:Toast提示類。

7、 CustomView的使用

1.CommonTabLayout的使用

①屬性:
  • tab_tabIndicatorWidth:設置下滑線的長度。
  • tab_tabIndicatorHeight:設置下滑線的高度。
  • tab_tabIndicatorColor:下滑線顏色。
  • tab_indicator_marginLeft/tab_indicator_marginRight/tab_indicator_marginTop/tab_indicator_marginBottom:設置下滑線外邊距。
  • tab_tabTextColor:沒選中字體顏色。
  • tab_tabTextSize:字體大小。
  • tab_tabSelectedTextColor:選中字體顏色。
  • tab_padding:下滑線內邊距,block樣式時能夠經過該屬性設置距離。
  • tab_tabBackground:Tab的背景顏色。
  • tab_indicator_corner:下滑線的圓角大小。
  • tab_indicator_gravity(bottom、top):設置下滑線顯示的位置,只針對line和triangle。
  • tab_tabMode(scrollable、fixed):Tab的顯示模式。
  • tab_indicator_style(line、triangle、block):下滑線的樣式。
②具體用戶可參照例子使用。

2.OutSideFrameTabLayout的使用

①屬性:
  • tab_tabIndicatorColor:設置Tab顏色。
  • tab_indicator_corner:圓角大小
  • tab_indicator_marginLeft/tab_indicator_marginRight/tab_indicator_marginTop/tab_indicator_marginBottom:設置下滑線外邊距。
  • tab_tabTextColor:沒選中字體顏色。
  • tab_tabTextSize:字體大小。
  • tab_tabSelectedTextColor:選中字體顏色。
  • tab_padding:內邊距。
  • tab_bar_color:bar的背景顏色。
  • tab_bar_stroke_color:外框的顏色。
  • tab_bar_stroke_width:外框的大小。
  • tab_width:bar的長度。
②具體用戶可參照例子使用。

3.自動佈局AutoLinefeedLayout的使用

<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)]

本文章會根據須要持續更新,建議點贊收藏,便於查看。也歡迎你們提出更多建議。

相關文章
相關標籤/搜索