1,其實早就想把這些東西給封裝封裝的,一直沒有時間,今天剛好項目進入到測試階段了,Bug同事在哪兒測試的飛起,但發現提bug的盡然是我(得意臉),然后上午把ios的包測試了一下,順便把服務器給測掛了(別問我是怎么做到的),現在服務器的同事還在拿着刀滿街找我吶。好了不扯了,就想標題寫了,一直想把這一塊揉在一起寫寫,那就趁這個機會吧。
先看看今天我們要實現的效果:

2,有些童鞋就很氣憤了,麻蛋,褲子都脫了,你給我看這個!!!!
其實我也想多寫點的啊,還想把App下載寫上呢,沒事,我們慢慢一點點的來,,先來看一下我們接口的數據吧
{
"code": 200,
"message": "",
"data": {
"code": "1.1.0",
"size": "8.6M",
"des": "1.【新增】自動更新\\r\\n2.【修改】部分ProgressBar替換為SVG\\r\\n3.【修改】Gank板塊部分改動\\r\\n"
}
}
可以看到,這是一個標准的接口數據,外部包含 code、message、data三大門神,這樣我們封裝Response就很好解決了。
看一下我們的BaseResponse類,很簡單,沒有什么講的
BaseResponse.java
package com.qianmo.myview2.response;
/**
* Created by wangjitao on 2016/11/8 0008.
* 數據返回類類
*/
public class BaseResponse<T> {
private int code;
private String message;
private T data;
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
}
再看一下我們這次的功能,就是一個簡單的網絡請求,判斷當前app版本和服務器上的版本,從而判斷是否是最新的,看到這里的同學可能就很疑惑了,麻蛋,不是說好了是封裝封裝嘛,為什么到現在還在說功能,嗯,阿呆哥哥只是想從本質上對比一下如果我們使用傳統的MVC去寫的話該是怎么寫的啊,相信現在很多同學已經在腦海中已經有了代碼了,不過我猜你們的Activity中的代碼會很多(奸笑臉),好吧,我前面的博客也介紹了MVP模式,現在我們是要把MVP運用在項目里面,所以,我們就要開始封裝基類了!!
3 ,封裝
①BaseView
我們知道在MVP模式中我們的V是和我們用戶界面的UI有關的,不管是控件的隱藏和顯示,還是控件大小的改變都是我們的V來處理的,所以這里我們只是寫了個簡單的夜間模式的UI改變的方法、還有展示錯誤信息的方法,很簡單
package com.qianmo.myview2.base;
import android.view.View;
/**
* Created by wangjitao on 2016/11/8 0008.
* 一般的Activity中要用到View操作無非是顯示加載框、影藏加載框、顯示出錯信息、顯示當數據為空的時候的view之類的
*/
public interface BaseView {
void showError(String msg);
void useNightMode(boolean isNight);
}
②BasePresenter
P在MVP架構中是主要用於邏輯處理的,所以一是我們最經常要寫的,看一下代碼,就是AttachView和DetachView(這也沒什么好解釋的)
package com.qianmo.myview2.base;
/**
* Created by wangjitao on 2016/11/8 0008.
* MVP框架的簡單封裝 P處理層
*/
public interface BasePresenter<T extends BaseView> {
void attachView(T view);
void detachView();
}
③App
這個類一般用於初始化一些數據,如屏幕的信息之類的和Activity的簡單的管理啊,還有一些第三方SDK的初始化
package com.qianmo.myview2;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
import java.util.HashSet;
import java.util.Set;
/**
* Created by wangjitao on 2016/11/8 0008.
*/
public class App extends Application {
private static App instance;
private Set<Activity> allActivities;
public static int SCREEN_WIDTH = -1;
public static int SCREEN_HEIGHT = -1;
public static float DIMEN_RATE = -1.0F;
public static int DIMEN_DPI = -1;
public static synchronized App getInstance() {
return instance;
}
@Override
public void onCreate() {
super.onCreate();
instance = this;
getScreenSize();
}
/**
* 初始化屏幕寬高
*/
public void getScreenSize() {
WindowManager windowManager = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
Display display = windowManager.getDefaultDisplay();
display.getMetrics(dm);
DIMEN_RATE = dm.density / 1.0F;
DIMEN_DPI = dm.densityDpi;
SCREEN_WIDTH = dm.widthPixels;
SCREEN_HEIGHT = dm.heightPixels;
if (SCREEN_WIDTH > SCREEN_HEIGHT) {
int t = SCREEN_HEIGHT;
SCREEN_HEIGHT = SCREEN_WIDTH;
SCREEN_WIDTH = t;
}
}
/**
* 添加activity
*/
public void addActivity(Activity act) {
if (allActivities == null) {
allActivities = new HashSet<>();
}
allActivities.add(act);
}
/**
* 移除activity
*/
public void removeActivity(Activity act) {
if (allActivities != null) {
allActivities.remove(act);
}
}
/**
* 退出app
*/
public void exitApp() {
if (allActivities != null) {
synchronized (allActivities) {
for (Activity act : allActivities) {
act.finish();
}
}
}
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
/**
* 這還有一系列的第三方SDK的初始化
*/
}
④BaseActivity
這個貌似是最重要的,這里我們考慮到5.0一下的機器切換Activity比較丑,所以寫了下切換的動畫,,然后就是一系列的初始化,直接貼出來吧
package com.qianmo.myview2.base;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import com.qianmo.myview2.App;
import com.qianmo.myview2.R;
import butterknife.Unbinder;
import butterknife.ButterKnife;
/**
* Created by wangjitao on 2016/11/8 0008.
* 基類Activity的封裝
* 一般使用mvp模式的話會在BaseActivity中進行P和V的初始化綁定
*/
public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements BaseView {
protected T mPresenter;
protected Activity mContext;
private Unbinder mUnbinder;
public enum TransitionMode {
LEFT, RIGHT, TOP, BOTTOM, SCALE, FADE
}
protected void onCreate(@Nullable Bundle savedInstanceState) {
if (toggleOverridePendingTransition()) {
switch (getOverridePendingTransitionMode()) {
case LEFT:
overridePendingTransition(R.anim.left_in, R.anim.left_out);
break;
case RIGHT:
overridePendingTransition(R.anim.right_in, R.anim.right_out);
break;
case TOP:
overridePendingTransition(R.anim.top_in, R.anim.top_out);
break;
case BOTTOM:
overridePendingTransition(R.anim.bottom_in, R.anim.bottom_out);
break;
case SCALE:
overridePendingTransition(R.anim.scale_in, R.anim.scale_out);
break;
case FADE:
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
break;
}
}
super.onCreate(savedInstanceState);
setContentView(getLayout());
mUnbinder = ButterKnife.bind(this);
mContext = this;
createPresenter();
if (mPresenter != null)
mPresenter.attachView(this);
App.getInstance().addActivity(this);
initEventAndData();
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter != null)
mPresenter.detachView();
mUnbinder.unbind();
App.getInstance().removeActivity(this);
}
protected void setToolBar(Toolbar toolbar, String title) {
toolbar.setTitle(title);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true);
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onBackPressed();
}
});
}
protected abstract int getLayout();
protected abstract void initEventAndData();
protected abstract boolean toggleOverridePendingTransition();
protected abstract TransitionMode getOverridePendingTransitionMode();
protected abstract void createPresenter();
}
⑤ BaseRecyclerViewAdapter
package com.qianmo.myview2.base;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.balysv.materialripple.MaterialRippleLayout;
import com.qianmo.myview2.R;
import java.util.List;
/**
* Created by wangjitao on 2016/11/7 0007.
* 對簡單的recycleview進行簡單的封裝
*/
public abstract class BaseRecyclerViewAdapter<T> extends RecyclerView.Adapter<BaseViewHolder> {
private Context context;
private LayoutInflater inflater;
private List<T> datas;
private int layoutId;
protected OnItemClickListner onItemClickListner;//單擊事件
protected OnItemLongClickListner onItemLongClickListner;//長按單擊事件
private boolean clickFlag = true;//單擊事件和長單擊事件的屏蔽標識
public BaseRecyclerViewAdapter(Context context, List<T> datas, int layoutId) {
this.context = context;
this.datas = datas;
this.layoutId = layoutId;
this.inflater = LayoutInflater.from(context);
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
BaseViewHolder holder = new BaseViewHolder(inflater.inflate(layoutId, parent, false));
MaterialRippleLayout.on(holder.getView(R.id.ll_all))
.rippleOverlay(true)
.rippleAlpha(0.2f)
.rippleColor(context.getResources().getColor(R.color.colorAccent))
.rippleHover(true)
.create();
return holder;
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
bindData(holder, datas.get(position), position);
}
@Override
public int getItemCount() {
return datas == null ? 0 : datas.size();
}
protected abstract void bindData(BaseViewHolder holder, T data, int position);
public void setOnItemClickListner(OnItemClickListner onItemClickListner) {
this.onItemClickListner = onItemClickListner;
}
public void setOnItemLongClickListner(OnItemLongClickListner onItemLongClickListner) {
this.onItemLongClickListner = onItemLongClickListner;
}
public interface OnItemClickListner {
void onItemClickListner(View v, int position);
}
public interface OnItemLongClickListner {
void onItemLongClickListner(View v, int position);
}
}
⑥ BaseViewHolder
package com.qianmo.myview2.base;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.View;
/**
* Created by wangjitao on 2016/11/7 0007.
* 萬能的Viewholder
*/
public class BaseViewHolder extends RecyclerView.ViewHolder {
private SparseArray<View> views;
public BaseViewHolder(View view) {
super(view);
this.views = new SparseArray<>();
}
public <T extends View> T getView(int viewId) {
View view = views.get(viewId);
if (view == null) {
view = itemView.findViewById(viewId);
views.put(viewId, view);
}
return (T) view;
}
public View getRootView() {
return itemView;
}
}
由於上一篇介紹了怎么封裝RecycleView的Adapter,在這里就不在廢話了
4,好了東西都封裝好了,看一下我們在實際功能中怎么寫吧,由於我們上面的功能,我們要實現一個簡單的版本判斷,由於要使用網絡,這里我打算使用Retrofit+RxJava,但是還沒有封裝好,就直接拿沒封裝的直接用了,這里創建mvp模式的話要創建好多個接口啊 ,所以推薦是用MVPHelper這個插件,挺好用的 ,好了,先看一下我們的MainContract
package com.qianmo.myview2.contract;
import com.qianmo.myview2.base.BasePresenter;
import com.qianmo.myview2.base.BaseView;
import com.qianmo.myview2.bean.VersionBean;
/**
* Created by wangjitao on 2016/11/8 0008.
* 首頁邏輯處理
*/
public class MainContract {
public interface View extends BaseView {
//View效果就是展示下載進度框
void showUpdateDialog(VersionBean bean);
void showProgressDialog();
void DissProgressDialog();
void ShowToast(String message);
}
public interface Presenter extends BasePresenter<View> {
//一般在首頁我們會進行一個版本的更新(功能)
void checkVersion(String currentVersion);
}
}
就只是接口的常見,並把這次功能要用到的方法全部抽象出來,
看一下我們Presenter的實現類
MainPresenterImpl.java
package com.qianmo.myview2.presenter;
import android.widget.Toast;
import com.qianmo.myview2.CheckVersionActivity;
import com.qianmo.myview2.MainActivity;
import com.qianmo.myview2.api.AppVersionService;
import com.qianmo.myview2.bean.VersionBean;
import com.qianmo.myview2.contract.MainContract;
import com.qianmo.myview2.response.BaseResponse;
import com.qianmo.myview2.utils.Constant;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import rx.Subscriber;
import rx.android.schedulers.AndroidSchedulers;
import rx.schedulers.Schedulers;
/**
* Created by MVPHelper on 2016/11/08
*/
public class MainPresenterImpl implements MainContract.Presenter {
private MainContract.View mView;
@Override
public void checkVersion(final String currentVersion) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constant.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
AppVersionService movieService = retrofit.create(AppVersionService.class);
movieService.getVersion()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<BaseResponse<VersionBean>>() {
@Override
public void onStart() {
mView.showProgressDialog();
}
@Override
public void onCompleted() {
mView.DissProgressDialog();
}
@Override
public void onError(Throwable e) {
mView.DissProgressDialog();
mView.ShowToast("請求出錯");
}
@Override
public void onNext(BaseResponse<VersionBean> versionBeanBaseResponse) {
if (Integer.valueOf(currentVersion.replace(".", "")) < Integer.valueOf(versionBeanBaseResponse.getData().getCode().replace(".", ""))) {
// mView.showUpdateDialog(versionBean);
//這里表示發現新版本
mView.ShowToast("發現最新版本");
} else {
//表示這就是最新版本
mView.ShowToast("已經是最新版本");
}
}
});
}
@Override
public void attachView(MainContract.View view) {
mView = view;
}
@Override
public void detachView() {
mView = null;
}
}
沒有什么好講解的,其實還可以把presenter層封裝一下的,可以在封裝成這樣的
public class BasePresenter<T extends MvpView> implements Presenter<T> {
private T mMvpView;
@Override
public void attachView(T mvpView) {
mMvpView = mvpView;
}
@Override
public void detachView() {
mMvpView = null;
}
public boolean isViewAttached() {
return mMvpView != null;
}
public T getMvpView() {
return mMvpView;
}
public void checkViewAttached() {
if (!isViewAttached()) throw new MvpViewNotAttachedException();
}
public static class MvpViewNotAttachedException extends RuntimeException {
public MvpViewNotAttachedException() {
super("Please call Presenter.attachView(MvpView) before" +
" requesting data to the Presenter");
}
}
}
ok,最后看一下我們的Activity
package com.qianmo.myview2;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.Toast;
import com.qianmo.myview2.base.BaseActivity;
import com.qianmo.myview2.bean.VersionBean;
import com.qianmo.myview2.contract.MainContract;
import com.qianmo.myview2.presenter.MainPresenterImpl;
import butterknife.BindView;
public class CheckVersionActivity extends BaseActivity<MainPresenterImpl> implements MainContract.View, View.OnClickListener {
@BindView(R.id.btn_getVersion)
Button btnGetVersion;
@BindView(R.id.progressBar)
ProgressBar progressBar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected int getLayout() {
return R.layout.activity_main;
}
@Override
protected void initEventAndData() {
btnGetVersion.setOnClickListener(this);
}
@Override
protected boolean toggleOverridePendingTransition() {
return false;
}
@Override
protected TransitionMode getOverridePendingTransitionMode() {
return null;
}
@Override
protected void createPresenter() {
mPresenter = new MainPresenterImpl();
}
@Override
public void showError(String msg) {
}
@Override
public void useNightMode(boolean isNight) {
}
@Override
public void showUpdateDialog(VersionBean bean) {
}
@Override
public void showProgressDialog() {
progressBar.setVisibility(View.VISIBLE);
}
@Override
public void DissProgressDialog() {
progressBar.setVisibility(View.GONE);
}
@Override
public void ShowToast(String message) {
Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(View view) {
try {
PackageManager pm = getPackageManager();
PackageInfo pi = pm.getPackageInfo(getPackageName(), PackageManager.GET_ACTIVITIES);
String versionName = pi.versionName;
mPresenter.checkVersion(versionName);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
}
}
再貼一下布局文件
<?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"
android:background="#ffffff"
>
<include
android:id="@+id/toolbar"
layout="@layout/view_toolbar"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/toolbar"
android:visibility="gone"
>
</android.support.v7.widget.RecyclerView>
<Button
android:id="@+id/btn_getVersion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="400dp"
android:text="檢查版本"
/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone"
/>
</RelativeLayout>
build.gradle
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "com.qianmo.myview2"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.0.0'
compile 'com.android.support:design:25.+'
compile 'com.android.support:recyclerview-v7:25.+'
compile 'com.android.support:cardview-v7:25.+'
compile 'com.balysv:material-ripple:1.0.2'
compile 'com.jakewharton:butterknife:8.2.1'
apt 'com.jakewharton:butterknife-compiler:8.2.1'
compile 'io.reactivex:rxjava:1.1.0'
compile 'io.reactivex:rxandroid:1.1.0'
compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta4'
compile 'com.squareup.retrofit2:adapter-rxjava:2.0.0-beta4'
compile 'com.google.code.gson:gson:2.6.2'
}
好了,這樣我們就把MVP簡單的封裝了,有沒有很簡單啊,下一篇接着封裝Retrofit+Rxjava+Dagger。
see you next time·······
