1,其實早就想把這些東西給封裝封裝的,一直沒有時間,今天恰好項目進入到測試階段了,Bug同事在哪兒測試的飛起,但發現提bug的盡然是我(得意臉),而後上午把ios的包測試了一下,順便把服務器給測掛了(別問我是怎麼作到的),如今服務器的同事還在拿着刀滿街找我吶。好了不扯了,就想標題寫了,一直想把這一塊揉在一塊兒寫寫,那就趁這個機會吧。java
先看看今天咱們要實現的效果:react
2,有些童鞋就很氣憤了,麻蛋,褲子都脫了,你給我看這個!!!!android
其實我也想多寫點的啊,還想把App下載寫上呢,沒事,咱們慢慢一點點的來,,先來看一下咱們接口的數據吧ios
{ "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就很好解決了。api
看一下咱們的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 ,封裝app
①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·······