Android--帶你一點點封裝項目 MVP+BaseActivity+Retrofit+Dagger+RxJava(一)

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·······

相關文章
相關標籤/搜索