关于MVP设计模式 和 BaseRecyclerViewAdapterHelperV2.4.4 Android

最近LZ在研究MVP设计模式,以前写代码的时候 只是做一些封装 基本没有接触过设计模式 , 尝试过MVP设计模式之后  突然感觉逼格高了一个等级,个人觉得只要肯下功夫 android 还是一门比较容易的语言. 好了直接开始我的MVP之旅 . 
关于MVP设计模式的一些概念就不在详细说明了,不了解的先自行百度吧! 
Model:       负责数据的检索,持久化等操作

View:         负责UI的绘制和用户的交互

Presenter: 作为Model和View的中间协调部分,负责两者之间的业务逻辑处理

先上基类: BasePresenter map集合是为okhttp的post请求准备的 后续会有


/**
 *
 * <p/>
 * 明确泛型 V 是一个View角色要实现的接口类型
 * (所有的View接口都要继承IBaseView接口可以把所有接口都要拥有的方法放到IBaseView)
 *
 * @param <T>代表view接口
 *           Created by ${苗春良}
 *                    on 2016/11/23.
 */
public abstract class BasePresenter<T> {

    /**
     * post请求参数集合  使用前请清空
     */
    protected Map<String,String> map=new HashMap<>();
    //View接口类型的弱引用
    protected Reference<T> mViewRef;

    /**
     * @param view  presenter 与view进行绑定
     */
    public void attachView(T view){

        mViewRef=new WeakReference<T>(view);

    }

    protected T getView(){

        return mViewRef.get();
    }

    /**
     * @return 是否关联
     */
    public boolean isViewAttached(){

        return mViewRef!=null&&mViewRef.get()!=null;
    }


    /**
     * 解除关联
     */
    public void detachView(){

        if (mViewRef!=null){

            mViewRef.clear();
            mViewRef=null;
        }

    }

}

基类 BaseActivity

/**
 * Created by ${苗春良}
 * on 2016/11/23.
 */
public abstract class BaseActivity<V, T extends BasePresenter<V>> extends AppCompatActivity {
    protected T mPresenter;
    /**
     * 两次点击的间隔时间
     */
    private static final int QUIT_INTERVAL = 2000;
    /**
     * 上次点击返回键的时间
     */
    private long lastBackPressed;
    @SuppressWarnings("unchecked")
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mPresenter = createPresenter();
        //presenter层和view层绑定
        mPresenter.attachView((V) this);
        initVariables();
        initViews(savedInstanceState);
        initData();
        initToolBar(isShowToolBar());
        //开启友盟测试
        MobclickAgent.setDebugMode(false);
    }

    /**
     * @return 是否显示标题栏
     */
    protected abstract boolean isShowToolBar();

    /**
     * 初始化titlebar
     */
    private void initToolBar(boolean isShow) {
        if (!isShow) {
            return;
        } else {
            TextView mTvTitle = (TextView)findViewById(R.id.tv_title);
            Toolbar mToolBar = (Toolbar)findViewById(R.id.toolbar);
            initSetting(mToolBar, mTvTitle);
        }

    }

    /**如果没有标题栏 此方法不实现
     *
     * 初始化设置  居中:  将mToolbar设置为空   标题使用mTvTitle设置
     * 居左:  mToolbar设置标题     mTvTitle设置为空
     */
    protected abstract void initSetting(Toolbar mToolbar, TextView mTvTitle);

    /**
     * 加载数据操作
     */
    protected abstract void initData();

    /**
     * @param savedInstanceState 初始化布局
     */
    protected abstract void initViews(Bundle savedInstanceState);

    /**
     * 初始化变量 接收从其他界面传递过来的参数
     */
    protected abstract void initVariables();


    /**
     * @return 创建presenter
     */
    protected abstract T createPresenter();

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null)
            //解除绑定
            mPresenter.detachView();
    }
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
            long backPressed = System.currentTimeMillis();
            if (backPressed - lastBackPressed > QUIT_INTERVAL) {
                lastBackPressed = backPressed;
                Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();
            } else {
                finish();
                MobclickAgent.onKillProcess(this);
                System.exit(0);
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    public void onBackPressed() {
        long backPressed = System.currentTimeMillis();
        if (backPressed - lastBackPressed > QUIT_INTERVAL) {
            lastBackPressed = backPressed;
            Toast.makeText(this, "再按一次退出", Toast.LENGTH_LONG).show();
        } else {
            finish();
            MobclickAgent.onKillProcess(this);
            System.exit(0);
        }
    }
    /**
     * 关闭当前界面 启动新界面
     *
     * @param clazz 启动界面方法  带有动画效果
     */
    protected void startActivity(Class clazz) {
        Intent intent = new Intent(UIUtils.getContext(), clazz);
        startActivity(intent);
        finish();
        overridePendingTransition(R.anim.activity_open, R.anim.activity_close);
    }
    public void onResume() {
        super.onResume();

        mPresenter.attachView((V) this);
        MobclickAgent.onResume(this);
    }
    public void onPause() {
        super.onPause();
        MobclickAgent.onPause(this);
    }

BaseActivity中 主要对presenter和view进行绑定 和 解绑的操作 由于项目中需要修改标题 因此使用了toolbar 布局也很简单

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/toolbar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/toolBarHeight"
    android:background="?attr/colorPrimary"
    android:minHeight="?attr/actionBarSize"
    android:paddingTop="@dimen/toolBarPaddingTop"
    app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
    app:theme="@style/ThemeOverlay.AppCompat.ActionBar">

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_gravity="center"
        android:text="首页"
        android:textColor="@android:color/white"
        android:textSize="20sp"
        android:textStyle="bold" />


</android.support.v7.widget.Toolbar>

使用方式也很简单 代码中注释的很详细了只需要在需要的界面include布局 , 修改一下是否显示就行了
上一个Contract契约类 LZ认为这种写法看起来很爽 有什么功能一次性可以全看完
这个是一个baserecyclerViewAdapterHelperV2.4.4做的一个list列表 其实view层很好理解 就是直接与用户进行交互, 比如说用户开启一个新的界面 界面可交互时先刷新数据 刷新数据 就有展示进度条 隐藏进度条(当然不是每个都这样 自行修改) 获取数据成功 失败 ==的操作
model层的操作很简单 处理数据 包括获取数据 缓存数据 保存数据等等的操作 由于项目暂时没有加入缓存 就一笔带过吧

presenter层的操作就一条 调用model层获取数据的方法 OnLoadNewsListListener 这个借口是自己写的观测数据的接口 下面也有


/**
 * Created by ${苗春良}
 * on 2016/11/23.
 * 信息列表 契约类
 */
public class 自己改名{

    public interface View {

        void showProgress();
        void addData(List<BackvehicleBean.ListBean> newsList);
        void hideProgress();
        void showLoadFailMsg();
        void showNetWorkException();
    }

    public interface Presenter{
        void loadData(int page);
    }

    public interface Moudel {
        void getDataFromNet(String url, Map<String, String> map, OnLoadNewListListener onLoadNewListListener);
    }
}

OnLoadNewsListListener 新数据监听接口

/**
 * 公用接口
 * Created by ${苗春良}
 * on 2016/11/22.
 */
public interface OnLoadNewListListener<T> {

    /**
     * @param list 获取数据成功 拿到集合
     */
    void onSuccess(List<T> list);

    /**
     * 获取数据失败
     * @param msg
     * @param e
     */
    void onFailure(String msg, Exception e);


    /** 网络连接失败监听
     * @param msg
     */
    void onNetWorkException(String msg);
}

还有一个 提交数据监听的接口


/**
 * 提交数据接口
 * Created by ${苗春良}
 * on 2016/11/23.
 */
public interface OnCommitDataListener {

    /**
     * 提交数据成功
     */
    void onSuccess();


    /**
     * @param msg 提交数据失败  msg错误信息
     */
    void onFailure(String msg);
}

model层代码

/**
 * Created by root on 2016/11/23
 */

public class 实现 implements Contract.Model {


    private BackvehicleBean mBackvehicleBean;
    private List<BackvehicleBean.ListBean> mList;
    private HttpUtil.HttpCallBack mHttpCallBack;
    private String TAG = "miao";

    @Override
    public void getDataFromNet(String url, Map<String, String> map, final OnLoadNewsListListener onLoadNewsListListener) {
        Log.i(TAG, "getDataFromNet: 调用");
        HttpUtil httpUtil = new HttpUtil();
        mHttpCallBack = new HttpUtil.HttpCallBack() {
            @Override
            public void onError(String meg) {
                onLoadNewsListListener.onNetWorkException("网络连接失败!");
            }

            @Override
            public void onSusscess(String data) {
                Log.i(TAG, "onSusscess: 连接成功" + data);
                //请求成功 解析数据
                try {
                    JSONObject jsonObject = new JSONObject(data);
                    String code = jsonObject.getString("code");
                    if (code.equals("S")) {
                        processData(jsonObject.getString("data"), onLoadNewsListListener);
                    } else if (code.equals("F")) {
                        //请求失败
                        UIUtils.showToast(jsonObject.getString("msg"));
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    onLoadNewsListListener.onFailure("网络连接失败",e);
                    Log.i("miao", "异常: 解析异常 异常信息在上面");
                }

            }
        };
        httpUtil.postMap(url, map, mHttpCallBack);
    }
    //解析数据的方法
    private void processData(String data, OnLoadNewsListListener onLoadNewsListListener) {
        Gson gson = new Gson();
        mBackvehicleBean = gson.fromJson(data, BackvehicleBean.class);
        /**从服务器上获取到的新的数据*/
        mList = mBackvehicleBean.getList();
        //将新的数据放到接口里
        onLoadNewListListener.onSuccess(mList);
    }

}

操作很简单 就不解释了 自己看注释吧! 网络请求封装也给大家吧 在网上找的一个OKhttp封装 是哪位大神的不记得了



/**
 * 网络请求工具类
 * post请求  json数据为body
 * post请求  map是body
 * get 请求
 * Created by root on 2016/11/11.
 *
 * @author mcl
 */
public class HttpUtil {
    private OkHttpClient client;
    //超时时间
    public static final int TIMEOUT = 1000 * 50;
    //json请求
    public static final MediaType JSON = MediaType
            .parse("application/json; charset=utf-8");

    public HttpUtil() {
        this.init();
    }

    private void init() {
        client = new OkHttpClient();

        //设置超时
        client.newBuilder().connectTimeout(TIMEOUT, TimeUnit.SECONDS).
                writeTimeout(TIMEOUT, TimeUnit.SECONDS).readTimeout(TIMEOUT, TimeUnit.SECONDS)
                .build();
    }

    /**
     * post请求  json数据为body
     */
    public void postJson(String url, String json, final HttpCallBack callBack) {
        RequestBody body = RequestBody.create(JSON, json);
        final Request request = new Request.Builder().url(url).post(body).build();

        OnStart(callBack);

        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                OnError(callBack, e.getMessage());
            }

            @Override
            public void onResponse(okhttp3.Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    onSuccess(callBack, response.body().string());
                } else {
                    OnError(callBack, response.message());
                }
            }
        });
    }

    /**
     * post请求  map是body
     *
     * @param url
     * @param map
     * @param callBack
     */
    public void postMap(String url, Map<String, String> map, final HttpCallBack callBack) {
        FormBody.Builder builder = new FormBody.Builder();

        //遍历map
        if (map != null) {
            for (Map.Entry<String, String> entry : map.entrySet()) {
                builder.add(entry.getKey(), entry.getValue().toString());
            }
        }
        RequestBody body = builder.build();
        Request request = new Request.Builder().url(url).post(body).build();
        OnStart(callBack);
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                OnError(callBack, e.getMessage());
            }

            @Override
            public void onResponse(okhttp3.Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    onSuccess(callBack, response.body().string());
                } else {
                    OnError(callBack, response.message());
                }
            }

        });
    }

    /**
     * get 请求
     *
     * @param url
     * @param callBack
     */
    public void getJson(String url, final HttpCallBack callBack) {
        Request request = new Request.Builder().url(url).build();
        OnStart(callBack);
        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {
                OnError(callBack, e.getMessage());
            }

            @Override
            public void onResponse(okhttp3.Call call, Response response) throws IOException {
                if (response.isSuccessful()) {
                    //onsuccess(final HttpCallBack callBack, final String data);
                    onSuccess(callBack, response.body().string());
                } else {
                    OnError(callBack, response.message());
                }
            }

        });
    }

    public void OnStart(HttpCallBack callBack) {
        if (callBack != null) {
            callBack.onstart();
        }
    }

    public void onSuccess(final HttpCallBack callBack, final String data) {
        if (callBack != null) {
            UIUtils.runInMainThread(new Runnable() {
                @Override
                public void run() {
                    callBack.onSusscess(data);
                }
            });

        }
    }

    public void OnError(final HttpCallBack callBack, final String msg) {
        if (callBack != null) {
            UIUtils.runInMainThread(new Runnable() {
                @Override
                public void run() {
                    callBack.onError(msg);
                }
            });
        }


    }

    /**
     * 请求开始 回调
     * 请求成功回调
     * 请求失败回调
     */
    public static abstract class HttpCallBack {
        //开始
        public void onstart() {
        }
        //成功回调
        public abstract void onSusscess(String data);

        //失败
        public void onError(String meg) {
        }

        ;
    }


}

接下来 presenter层实现 由于presenter层是连接view和model层的中间枢纽(就这么叫吧) 因此必然会持有这两层的引用对象 在构造方法中 创建和获取 注意实现的接口

import com.city.wuliubang.backtrip.base.BasePresenter;
import com.city.wuliubang.backtrip.contract.BackTripInfoContract;
import com.city.wuliubang.backtrip.listener.OnLoadNewsListListener;
import com.city.wuliubang.backtrip.model.BackTripInfoModelImpl;
import com.city.wuliubang.backtrip.utils.http.BranchPath;
import com.city.wuliubang.backtrip.utils.http.Constant;

import java.util.List;

/**
 * 处理数据
* Created by root on 2016/11/23
*/

public class BackTripInfoPresenterImpl extends BasePresenter implements BackTripInfoContract.Presenter,OnLoadNewsListListener {
    private BackTripInfoContract.View returnView;
    private final BackTripInfoModelImpl mBackTripInfoModelImpl;
    private String TAG="miao";
    //构造方法获取 view层和model层的对象
    public BackTripInfoPresenterImpl(BackTripInfoContract.View view) {
        returnView=view;
        mBackTripInfoModelImpl = new BackTripInfoModelImpl();
    }

    @Override
    public void loadData(int page) {
        map.clear();
        map.put("page",String.valueOf(page));
        mBackTripInfoModelImpl.getDataFromNet(Constant.ROOTPATH + BranchPath.SELECTBACKINFO,map,this);
        returnView.showProgress();
    }
    @Override
    public void onSuccess(List list) {
        returnView.addData(list);
        returnView.hideProgress();
    }

    @Override
    public void onFailure(String msg, Exception e) {
        returnView.showLoadFailMsg();
        returnView.hideProgress();
    }

    @Override
    public void onNetWorkException(String msg) {
        returnView.showNetWorkException();
    }
}

最后 View层Activity view层要持有presenter层的对象 基类baseactivity中的 creatPresenter方法

/**
 * 回程信息列表界面 view层
 */
public class 列表界面extends BaseActivity implements BackTripInfoContract.View, SwipeRefreshLayout.OnRefreshListener, BaseQuickAdapter.RequestLoadMoreListener {

    private ArrayList<BackvehicleBean.ListBean> mList = new ArrayList<>();
    private SwipeRefreshLayout mSwipeRefreshWidget;
    private RecyclerView mRecyclerView;
    private LinearLayoutManager mLayoutManager;
    private QuickAdapter mAdapter;
    private int pageIndex = 1;
    private BackTripInfoPresenterImpl mBackTripPresenter;
    private boolean isRefresh = true;
    private SwipeRefreshLayout.OnRefreshListener onRefreshListener;


    @Override
    protected boolean isShowToolBar() {
        return true;
    }

    @Override
    protected void initSetting(Toolbar mToolbar, TextView mTvTitle) {
        //初始化标题栏
        mToolbar.setTitle("");
        mTvTitle.setText("返程车列表");
        mToolbar.setNavigationIcon(R.mipmap.back_arrows);
        //点击返回  关闭当前界面 回到前一个界面
        mToolbar.setNavigationOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(BackTripApplyActivity.class);
            }
        });
    }

    @Override
    protected void initData() {
        //加载更多监听
        mAdapter.setOnLoadMoreListener(this);
        //下拉刷新监听
        mSwipeRefreshWidget.setOnRefreshListener(this);
    }

    @Override
    protected void initViews(Bundle convertView) {
        setContentView(R.layout.activity_base_recycler_list);
        mSwipeRefreshWidget = (SwipeRefreshLayout) findViewById(R.id.srf_refresh);
        mSwipeRefreshWidget.setOnRefreshListener(this);
        mSwipeRefreshWidget.setColorSchemeResources(R.color.google_blue,
                R.color.google_green, R.color.google_red,
                R.color.google_yellow);
        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        /**RecyclerView的尺寸在每次改变时,比如你加任何些东西。
         setHasFixedSize 的作用就是确保尺寸是通过用户输入从而确保RecyclerView的尺寸是一个常数。
         RecyclerView 的Item宽或者高不会变。每一个Item添加或者删除都不会变。
         如果你没有设置setHasFixedSized没有设置的代价将会是非常昂贵的。
         因为RecyclerView会需要而外计算每个item的size,
         源码:
         void triggerUpdateProcessor() {
         if (mPostUpdatesOnAnimation && mHasFixedSize && mIsAttached) {
         ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
         } else {
         mAdapterUpdateDuringMeasure = true;
         requestLayout();
         }
         }
         requestLayout();为重绘布局 消费高
         */
        mRecyclerView.setHasFixedSize(true);
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);
        mAdapter = new QuickAdapter(mList);
//        mAdapter.openLoadAnimation(new BaseAnimation() {
//            @Override
//            public Animator[] getAnimators(View view) {
//
//                return new Animator[]{
//                        ObjectAnimator.ofFloat(view, "scaleY", 0.9f, 1.2f, 1),
//                        ObjectAnimator.ofFloat(view, "scaleX", 0.9f, 1.2f, 1),
//                        ObjectAnimator.ofFloat(view,"")
//                };
//            }
//        });
        mAdapter.openLoadAnimation(BaseQuickAdapter.SCALEIN);
        // 注意  此处必须使用此方法  防止布局的match_parent被强改成wrap_content
        mAdapter.setEmptyView(infalteView(R.layout.layout_empty_view));
        //设置适配器
        mRecyclerView.setAdapter(mAdapter);
        //刷新数据 初始化界面数据

    }

    @Override
    protected void initVariables() {

    }


    @Override
    protected BasePresenter createPresenter() {
        mBackTripPresenter = new BackTripInfoPresenterImpl(this);
        return mBackTripPresenter;
    }

    @Override
    public void showProgress() {

    }

    @Override
    public void addData(List<BackvehicleBean.ListBean> newsList) {
        //服务器每次返回15条 小于15条添加脚布局 证明已经没有更多数据
        //判断是否为刷新操作
        if (isRefresh) {
            mAdapter.removeAllFooterView();
            mAdapter.setNewData(newsList);
        } else {
            mAdapter.addData(newsList);
            if (newsList.size() == 0 || newsList.size() < 15) {
                mAdapter.addFooterView(View.inflate(getApplicationContext(), R.layout.end_view, null));
            }
            //重置为刷新操作
            isRefresh = true;
        }
    }

    @Override
    public void hideProgress() {
        mSwipeRefreshWidget.setRefreshing(false);
    }

    @Override
    public void showLoadFailMsg() {
        mAdapter.showLoadMoreFailedView();
    }

    @Override
    public void showNetWorkException() {
        mSwipeRefreshWidget.setRefreshing(false);
        UIUtils.showToast("网络连接失败");
    }


    @Override
    public void onRefresh() {
        //刷新只加载第一页数据
        pageIndex = 1;
        mBackTripPresenter.loadData(pageIndex);
    }


    @Override
    public void onLoadMoreRequested() {
        //加载操作
        isRefresh = false;
        //更新索引
        pageIndex += 1;
        //调用presenter层获取数据 加载第一页的数据
        mBackTripPresenter.loadData(pageIndex);
    }


    /**
     * @param layoutId 布局id
     * @return view对象
     */
    private View infalteView(int layoutId) {
        LayoutInflater inflater = LayoutInflater.from(this);
        //防止布局的match_parent被强改成wrap_content
        return inflater.inflate(layoutId, (ViewGroup) mRecyclerView.getParent(), false);//参3prarent跟view没有添加关系.
    }

    @Override
    public void onResume() {
        super.onResume();
        onRefresh();
    }
}

关于MVP设计模式的就差不多了
接下来简单说一下V2.4.4版本的BaseRecyclerViewAdapterHelper的刷新和加载以及QuickAdapter

//服务器每次返回15条 小于15条添加脚布局 证明已经没有更多数据
        //判断是否为刷新操作
        if (isRefresh) {
            mAdapter.removeAllFooterView();
            mAdapter.setNewData(newsList);
        } else {
            mAdapter.addData(newsList);
            if (newsList.size() == 0 || newsList.size() < 15) {
                mAdapter.addFooterView(View.inflate(getApplicationContext(), R.layout.end_view, null));
            }
            //重置为刷新操作
            isRefresh = true;
        }

注意setNewData方法和addData方法 是啥我就不说了 一眼就能看出来 不要纠结newList.size()的判断条件

好,到这里就基本完成了 另外推荐大家一个 代码混淆的studio插件AndroidProguard(一个非常牛逼的大神写的) 自己百度去吧 至于效果 阿哈哈哈哈哈哈哈!谁用谁知道!
本人刚出道菜鸟一枚 有什么建议欢迎交流 QQ1773345343

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值