zoukankan      html  css  js  c++  java
  • Android--带你一点点封装项目 MVP+BaseActivity+Retrofit+Dagger+RxJava(一)

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

  • 相关阅读:
    Python进阶(二)
    python基础(四)
    Python基础(三)
    python基础(二)
    Python基础(一)
    backbone之extend方法(刚明白了点)
    js中url相关
    bootstrap导航、导航条及导航翻页相关详解
    bootstrap中css组件(除导航条)
    bootstrap基础排版优化
  • 原文地址:https://www.cnblogs.com/wjtaigwh/p/6043829.html
Copyright © 2011-2022 走看看