前言:
MVP作为一种MVC的演化版本在Android开发中受到了越来越多的关注,但是MVP到现在为止并没有统一的标准或者框架。网络或github上也有很多相应的模板,但是并不是自己想要的,所以自己便简单地封装下。

先看下效果图:

mvp架构模式应用 mvp android 架构_mvp


1、项目结构:

mvp架构模式应用 mvp android 架构_ide_02


所用到的依赖build.gradle中加入:

//网络请求
    implementation 'com.squareup.retrofit2:retrofit:2.6.0'
    //数据解析
    implementation 'com.squareup.retrofit2:converter-gson:2.6.0'
    //加载框
    implementation 'com.wang.avi:library:2.1.3'

AndroidManifest.xml清单文件加入:

<uses-permission android:name="android.permission.INTERNET"/>

2、代码部分

①基类activity和fragment:

public abstract class BaseMvpActivity<V extends BaseView, P extends BasePresenter<V>>
        extends FragmentActivity implements BaseDelegate<V, P> {

    protected P mPresenter;

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);//竖屏
        super.onCreate(savedInstanceState);
        setContentView(this.setLayoutId());
        mPresenter = createPresenter();
        initView();
    }

    /**
     * 设置布局的ID
     */
    protected abstract int setLayoutId();

    /**
     * 初始化操作
     */
    protected abstract void initView();

    @Override
    public P getPresenter() {
        return mPresenter;
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }

    /**
     * 吐司
     */
    public void showToast(String msg) {
        Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
    }
}
public abstract class BaseMvpFragment<V extends BaseView, P extends BasePresenter<V>>
        extends Fragment implements BaseDelegate<V, P> {

    protected P mPresenter;
    private View mView;
    protected BaseMvpActivity mActivity;

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        //内存不足的情况会导致Fragment调用getActivity()的地方却返回null空指针异常
        this.mActivity = (BaseMvpActivity) context;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (mView != null) {
            ViewGroup parent = (ViewGroup) mView.getParent();
            if (parent != null) {
                parent.removeView(mView);
            }
        } else {
            mPresenter = createPresenter();
            mView = inflater.inflate(setLayoutId(), container, false);
            initView(mView, savedInstanceState);
        }
        return mView;
    }

    /**
     * 设置布局的ID
     */
    protected abstract int setLayoutId();

    /**
     * 初始化操作
     */
    protected abstract void initView(View view, Bundle savedInstanceState);

    @Override
    public P getPresenter() {
        return mPresenter;
    }

    @Override
    public void onDestroy() {
        mView = null;
        super.onDestroy();
        if (mPresenter != null) {
            mPresenter.detachView();
        }
    }
}

②:处理基类:

/**
 * 功能描述:委托类
 */
public interface BaseDelegate<V extends BaseView, P extends BasePresenter<V>> {

    /**
     * 初始化presenter
     */
    P createPresenter();

    /**
     * 获取presenter
     */
    P getPresenter();
}
public abstract class BaseModel<P> {

    protected P mPresenter;

    public BaseModel(P presenter) {
        this.mPresenter = presenter;
    }
}
/**
 * 功能描述:主要负责View与Model的交互
 */
public interface BasePresenter<V extends BaseView> {

    /**
     * 绑定接口
     */
    //void attachView(BaseMvpActivity context, V view);

    void attachView(V view);

    /**
     * 释放接口
     */
    void detachView();
}
public interface BaseView {

}

个人信息实体类:

/**
 * 个人信息实体类
 */
public class UserInfoBean {

    /**
     * createTime : 2019-08-17 08:18:20
     * userName : HelloWord
     * password : e10adc3949ba59abbe56e057f20f883e
     * mobile : 16666666666
     * birthday : 2001-05-17
     * height : 150
     * weight : 60
     * sex : BOY
     */

    private String createTime;
    private String userName;
    private String password;
    private String mobile;
    private String birthday;
    private int height;
    private int weight;
    private String sex;

    public String getCreateTime() {
        return createTime;
    }

    public void setCreateTime(String createTime) {
        this.createTime = createTime;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWeight() {
        return weight;
    }

    public void setWeight(int weight) {
        this.weight = weight;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "UserInfoBean{" +
                "createTime='" + createTime + '\'' +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", mobile='" + mobile + '\'' +
                ", birthday='" + birthday + '\'' +
                ", height=" + height +
                ", weight=" + weight +
                ", sex='" + sex + '\'' +
                '}';
    }
}

④:请求类(关键部分):

/**
 * 添加请求头
 */
public class HeaderInterceptor implements Interceptor {
    
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        Request.Builder builder = request.newBuilder()
                .addHeader("Content-Type", "text/plain;charset=UTF-8");
        builder = builder.url(request.url().newBuilder()
                //建议缓存到Appaction中再取值即可
                .addQueryParameter("token", "传入自己的token")
                .build());
        return chain.proceed(builder.build());
//        Request request = chain.request()
//                .newBuilder()
//                .addHeader("Content-Type", "application/text;charset=UTF-8")
//                .addHeader("Accept-Encoding", "gzip, deflate")
//                .addHeader("Connection", "keep-alive")
//                .addHeader("Accept", "*/*")
//                .addHeader("Cookie", "add cookies here")
//                .addHeader("Content-Type", "text/plain;charset=UTF-8")
//                .build();
//        return chain.proceed(request);
    }
}
/**
 * 打印请求链接、请求时间
 */
public class LogingInterceptor implements Interceptor {

    private static final String TAG = "LogingInterceptor";

    @Override
    public Response intercept(Chain chain) throws IOException {

        //一秒的10亿分之一
        //1纳秒=0.000001 毫秒
        //1纳秒=0.00000 0001秒
        long t1 = System.nanoTime();
        Response response = chain.proceed(chain.request());
        long t2 = System.nanoTime();

        Log.e(TAG,String.format(Locale.getDefault(), "Received response for %s in %.1fms%n%s",
                response.request().url(), (t2 - t1) / 1e6d, response.headers()));

        MediaType mediaType = response.body().contentType();
        String content = response.body().string();

        Log.e(TAG,content);

        return response.newBuilder()
                .body(okhttp3.ResponseBody.create(mediaType, content))
                .build();
    }
}
/**
 * 功能描述:回调接口
 * 自行修改,根据对应code或状态设置是否请求成功
 */
public class HttpBaseCallBack<T> {

    private static final String TAG = "HttpBaseCallBack";
    protected Call<HttpBaseDto<T>> mCall;
    private BaseMvpActivity mActivity;
    //默认显示加载框
    private boolean isLoading = true;

    private LoadingDialog dialog;

    public HttpBaseCallBack(BaseMvpActivity context, Call call) {
        this.mCall = call;
        this.mActivity = context;
    }

    public HttpBaseCallBack(BaseMvpActivity context, Call call, boolean isLoading) {
        this.mCall = call;
        this.mActivity = context;
        this.isLoading = isLoading;
    }

    /**
     * 处理返回数据
     */
    public void getInstanceResponse(final ResponseListener listener) {
        if (isLoading) {
            showLoading();
        }
        mCall.enqueue(new Callback<HttpBaseDto<T>>() {
            @Override
            public void onResponse(Call<HttpBaseDto<T>> call, Response<HttpBaseDto<T>> response) {
                //关闭弹窗
                dismissLoading();
                String errorMsg = response.body().getMsg();
                //请求成功
                if (response.isSuccessful() && null == response.errorBody()) {
                    //此处是Code码为0请求成功(根据后台返回自行修改)
                    if (response.body().code.equals("0")) {
                        listener.onSuccess(response.body().getData());
                    } else {
                        listener.onFailure(errorMsg);
                        mActivity.showToast(errorMsg);
                    }
                } else {
                    //请求失败,回调错误信息
                    listener.onFailure(errorMsg);
                    mActivity.showToast(errorMsg);
                }
            }

            @Override
            public void onFailure(Call<HttpBaseDto<T>> call, Throwable t) {
                dismissLoading();
                listener.onFailure(t.getMessage());
                mActivity.showToast("获取失败,请稍后再试~");
            }
        });
    }

    /**
     * 接口回调
     */
    public interface ResponseListener<T> {

        //请求成功
        void onSuccess(T t);

        //请求失败
        void onFailure(String msg);
    }

    /**
     * 显示加载提示
     */
    private void showLoading() {
        if (dialog == null) {
            dialog = new LoadingDialog(mActivity);
        }
        dialog.show();
    }

    /**
     * 关闭加载提示
     */
    private void dismissLoading() {
        if (dialog != null) {
            dialog.dismiss();
            dialog.cancel();
            dialog = null;
        }
    }
}
public class HttpBaseDto<T> implements Serializable {

    //根据返回的不同自行修改、请求状态
    public String code;

    //请求成功或失败
    public String msg;

    //数据源
    private T data;

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public T getData() {
        return data;
    }

    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "HttpBaseDto{" +
                "code='" + code + '\'' +
                ", msg='" + msg + '\'' +
                ", data=" + data +
                '}';
    }
}

注意code、msg、data根据后端返回自行修改

/**
 * 功能描述:请求连接
 */
public interface RetrofitClient {

    /**
     * POST请求
     */
    @FormUrlEncoded
    @POST("app/login")
    Call<HttpBaseDto<UserInfoBean>> postRequest(@Field("account") String account,
                                                @Field("password") String password);

    /**
     * GET请求
     */
    @GET("app/login")
    Call<HttpBaseDto<UserInfoBean>> getRequest(@Query("account") String account,
                                               @Query("password") String password);

    /**
     * 多个参数请求(GET方式同理)
     */
    @FormUrlEncoded
    @POST("app/login")
    Call<HttpBaseDto<UserInfoBean>> mapRequest(@FieldMap HashMap<String, String> hashMap);

    /**
     * 上传图片
     */
    @Multipart
    @POST("app/file/uploadImg")
    Call<ResponseBody> uploadImg(@Part("img") RequestBody file);

}
/**
 * 功能描述:Retrofit基本配置
 */
public class RetrofitFactory {

    private static RetrofitClient mRetrofitClient;
    private static Retrofit.Builder mBuilder;

    /**
     * 双重单例
     */
    public static RetrofitClient getInstance() {
        if (null == mRetrofitClient) {
            synchronized (RetrofitClient.class) {
                if (null == mRetrofitClient) {
                    OkHttpClient client = new OkHttpClient.Builder()
                            //连接超时10 、单位秒
                            .connectTimeout(15, TimeUnit.SECONDS)
                            //读取时间
                            .readTimeout(10, TimeUnit.SECONDS)
                            //写入时间
                            .writeTimeout(10, TimeUnit.SECONDS)
                            //添加拦截器
                            .addInterceptor(new LogingInterceptor())
                            .addInterceptor(new HeaderInterceptor())
                            .build();
                    mBuilder = new Retrofit.Builder()
                            //请求链接
                            .baseUrl(AppConfig.BASE_URL)
                            //添加Gson解析器(若返回不是gson格式可自定义解析器 )
                            //继承Converter.Factory 实现其方法
                            .addConverterFactory(GsonConverterFactory.create())
                            .client(client);
                    mRetrofitClient = mBuilder.build().create(RetrofitClient.class);
                }
            }
        }
        return mRetrofitClient;
    }
}

3、使用方法:
新建interfaceview、model、presenter3个包(名字自行修改)

/**
 * 功能描述:成功失败回调
 */
public interface IRequestView extends BaseView {

    void onRequestSuccess(UserInfoBean dto);

    void onRequestFailed(String msg);

    void onUploadImgSuccess(String url);

    void onUploadImgFailed(String msg);

}
public class RequestModel extends BaseModel<RequestPresenter> {

    private static final String TAG = "RequestModel";
    private BaseMvpActivity mActivity;

    public RequestModel(RequestPresenter presenter, BaseMvpActivity mActivity) {
        super(presenter);
        this.mActivity = mActivity;
    }

    /**
     * POST和GET请求(注意密码MD5加密)
     */
    public void getRequest(boolean type, String account, String password) {
        RetrofitClient client = RetrofitFactory.getInstance();
        Call<HttpBaseDto<UserInfoBean>> call;
        if (type) {
            call = client.postRequest(account, Md5Utils.encryption(password));
        } else {
            call = client.getRequest(account, Md5Utils.encryption(password));
        }
        new HttpBaseCallBack<>(mActivity, call).getInstanceResponse(
                new HttpBaseCallBack.ResponseListener<UserInfoBean>() {
                    @Override
                    public void onSuccess(UserInfoBean dto) {
                        //请求成功
                        if (null != dto) {
                            mPresenter.OnRequestSuccess(dto);
                        }
                    }

                    @Override
                    public void onFailure(String msg) {
                        //请求失败
                        mPresenter.OnRequestFailed(msg);
                    }
                });
    }

    /**
     * 多个参数请求
     */
    public void mapRequest(UserInfoBean bean) {
        HashMap<String, String> hashMap = new HashMap<>();
        if (!TextUtils.isEmpty(bean.getMobile())) {
            hashMap.put("account", bean.getMobile());
        }
        if (!TextUtils.isEmpty(bean.getPassword())) {
            hashMap.put("password", Md5Utils.encryption(bean.getPassword()));
        }
        if (!TextUtils.isEmpty(bean.getUserName())) {
            hashMap.put("userName", bean.getUserName());
        }
        if (!TextUtils.isEmpty(bean.getBirthday())) {
            hashMap.put("birthday", bean.getBirthday());
        }
        Call<HttpBaseDto<UserInfoBean>> call = RetrofitFactory.getInstance().mapRequest(hashMap);
        new HttpBaseCallBack<>(mActivity, call).getInstanceResponse(
                new HttpBaseCallBack.ResponseListener<UserInfoBean>() {
                    @Override
                    public void onSuccess(UserInfoBean dto) {
                        //请求成功
                        if (null != dto) {
                            mPresenter.OnRequestSuccess(dto);
                        }
                    }

                    @Override
                    public void onFailure(String msg) {
                        //请求失败
                        mPresenter.OnRequestFailed(msg);
                    }
                });
    }

    /**
     * 上传图片
     */
    public void uploadImg(String path) {
        File file = new File(path);
        RequestBody requestFile = RequestBody.create(MediaType.parse("image/jpg"), file);
        Call<ResponseBody> call = RetrofitFactory.getInstance().uploadImg(requestFile);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                Log.e(TAG, "onResponse: " + response.body().toString());
                Log.e(TAG, "onResponse: " + call.toString());
                Log.e(TAG, "onResponse: " + response.message());
                Log.e(TAG, "onResponse: " + response.message());
                if (null != response.body()) {
                    mPresenter.OnUploadImgSuccess(response.body().toString());
                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Log.e(TAG, "onFailure: " + t.getMessage());
                mPresenter.OnUploadImgFailed(t.getMessage());
            }
        });
    }
}
public class RequestPresenter implements BasePresenter<IRequestView> {

    private IRequestView mView;
    private RequestModel mModel;

    public RequestPresenter(IRequestView view, BaseMvpActivity mActivity) {
        attachView(view);
        mModel = new RequestModel(this, mActivity);
    }

    /**
     * POST、GET请求
     */
    public void getRequest(boolean type, String account, String password) {
        mModel.getRequest(type,  account,  password);
    }

    /**
     * 多参数请求
     */
    public void mapRequest(UserInfoBean bean) {
        mModel.mapRequest(bean);
    }

    /**
     * 上传图片
     */
    public void uploadImg(String path) {
        mModel.uploadImg(path);
    }

    @Override
    public void attachView(IRequestView view) {
        this.mView = view;
    }

    @Override
    public void detachView() {
        this.mView = null;

    }

    public void OnRequestSuccess(UserInfoBean dto) {
        if (null != mView) {
            mView.onRequestSuccess(dto);
        }
    }

    public void OnRequestFailed(String msg) {
        if (null != mView) {
            mView.onRequestFailed(msg);
        }
    }

    public void OnUploadImgSuccess(String url) {
        if (null != mView) {
            mView.onUploadImgSuccess(url);
        }
    }

    public void OnUploadImgFailed(String msg) {
        if (null != mView) {
            mView.onUploadImgFailed(msg);
        }
    }
}

Activity中的写法:

public class MainActivity extends BaseMvpActivity<IRequestView, RequestPresenter> implements View.OnClickListener, IRequestView {

    private TextView contText1, contText2, contText3;
    private static final String TAG = "MainActivity";
    //判断请求方式
    private int requestType;

    @Override
    protected int setLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initView() {
        findViewById(R.id.btn_post).setOnClickListener(this);
        findViewById(R.id.btn_get).setOnClickListener(this);
        findViewById(R.id.btn_map).setOnClickListener(this);
        findViewById(R.id.btn_upload).setOnClickListener(this);
        contText1 = findViewById(R.id.cont_text1);
        contText2 = findViewById(R.id.cont_text2);
        contText3 = findViewById(R.id.cont_text3);
    }

    @Override
    public RequestPresenter createPresenter() {
        return new RequestPresenter(this, this);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_post:
                requestType = 1;
                //POST请求
                mPresenter.getRequest(true, "16666666666", "123456");
                break;
            case R.id.btn_get:
                requestType = 2;
                //GET请求
                mPresenter.getRequest(false, "16666666666", "123456");
                break;
            case R.id.btn_map:
                requestType = 3;
                //多个参数请求
                UserInfoBean bean = new UserInfoBean();
                bean.setMobile("16666666666");
                bean.setPassword("123456");
                mPresenter.mapRequest(bean);
                break;
            case R.id.btn_upload:
                //上传图片(path:本地图片路径)
                String path = "/storage/emulated/0/Android/data/com.helloword.demo/1567324566927.jpg";
                mPresenter.uploadImg(path);
                break;
        }
    }

    @Override
    public void onRequestSuccess(UserInfoBean bean) {
        //请求成功
        String str = bean.toString();
        Log.e(TAG, "onRequestSuccess: " + str);
        switch (requestType) {
            case 1:
                contText1.setText(str);
                break;
            case 2:
                contText2.setText(str);
                break;
            case 3:
                contText3.setText(str);
                break;
        }
    }

    @Override
    public void onRequestFailed(String msg) {
        //请求失败
    }

    @Override
    public void onUploadImgSuccess(String url) {
        //上传成功
        Log.e(TAG, "onUploadImgSuccess: 图片地址" + url);
    }

    @Override
    public void onUploadImgFailed(String msg) {
        //上传失败
    }
}

对应布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/cont_text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="15dp" />

    <Button
        android:id="@+id/btn_post"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="POST请求" />

    <TextView
        android:id="@+id/cont_text2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="15dp" />

    <Button
        android:id="@+id/btn_get"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="GET请求" />

    <TextView
        android:id="@+id/cont_text3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="15dp" />

    <Button
        android:id="@+id/btn_map"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="多参数为空请求" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="描述:有多个参数时,为空时不携带键值,不能传“null”或双引号" />

    <Button
        android:id="@+id/btn_upload"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="上传图片" />

</LinearLayout>

用到的工具类与加载框:

public class AppConfig {

    /**
     * 请求链接
     */
    public static final String BASE_URL = "http://198.110.8080/helloword/";
}
/**
 * 功能描述:MD5加密工具
 */
public class Md5Utils {

    /**
     * 32位小写加密
     * @param plain
     * @return
     */
    public static String encryption(String plain) {
        String re_md5 = new String();
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update(plain.getBytes());
            byte b[] = md.digest();

            int i;

            StringBuffer buf = new StringBuffer("");
            for (int offset = 0; offset < b.length; offset++) {
                i = b[offset];
                if (i < 0)
                    i += 256;
                if (i < 16)
                    buf.append("0");
                buf.append(Integer.toHexString(i));
            }

            re_md5 = buf.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return re_md5;
    }

    /***
     * MD5加密 生成32位md5码
     * @param inStr 待加密字符串
     * @return 返回32位md5码
     */
    public static String md5Encode(String inStr) throws Exception {
        MessageDigest md5 = null;
        try {
            md5 = MessageDigest.getInstance("MD5");
        } catch (Exception e) {
            System.out.println(e.toString());
            e.printStackTrace();
            return "";
        }

        byte[] byteArray = inStr.getBytes("UTF-8");
        byte[] md5Bytes = md5.digest(byteArray);
        StringBuffer hexValue = new StringBuffer();
        for (int i = 0; i < md5Bytes.length; i++) {
            int val = ((int) md5Bytes[i]) & 0xff;
            if (val < 16) {
                hexValue.append("0");
            }
            hexValue.append(Integer.toHexString(val));
        }
        return hexValue.toString().toLowerCase();
    }
}
/**
 * 功能描述:加载框(https://github.com/81813780/AVLoadingIndicatorView)
 */
public class LoadingDialog extends Dialog {

    private AVLoadingIndicatorView view;

    public LoadingDialog(@NonNull BaseMvpActivity context) {
        super(context, R.style.MyAlertDialog);
        setContentView(R.layout.dialog_loading);
        view = findViewById(R.id.loading_view);
    }
    
    @Override
    public void show() {
        super.show();
        view.show();
    }

    @Override
    public void dismiss() {
        super.dismiss();
        view.hide();
    }
}

style:

<style name="MyAlertDialog" parent="@android:style/Theme.Dialog">
        <item name="android:windowFrame">@null</item>
        <!-- 边框 -->
        <item name="android:windowIsFloating">true</item>
        <!-- 是否浮现在activity之上 -->
        <item name="android:windowIsTranslucent">false</item>
        <!-- 半透明 -->
        <item name="android:windowNoTitle">true</item>
        <!-- 无标题 -->
        <item name="android:windowBackground">@android:color/transparent</item>
        <!-- 背景透明 -->
        <item name="android:backgroundDimEnabled">true</item>
        <!-- 模糊 -->
        <item name="android:background">@android:color/transparent</item>
        <!-- 背景色 -->
    </style>

颜色值:

<color name="translucent_color">#99000000</color>
 <color name="transparent">#00000000</color>
 <color name="white_bg_color">#ffffff</color>

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/loading_shape"
    android:gravity="center"
    android:orientation="vertical"
    android:paddingLeft="30dp"
    android:paddingTop="20dp"
    android:paddingRight="30dp"
    android:paddingBottom="20dp">

    <!--LineScalePartyIndicator  x -->
    <com.wang.avi.AVLoadingIndicatorView
        android:id="@+id/loading_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:visibility="visible"
        app:indicatorColor="@color/white_bg_color"
        app:indicatorName="PacmanIndicator" />

    <TextView
        android:id="@+id/dialog_msg"
        android:textColor="@color/white_bg_color"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="加载中..."
        android:textSize="12sp" />

</LinearLayout>

样式loading_shape:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <corners android:radius="10dp" />

    <solid android:color="@color/translucent_color" />
</shape>

后端返回样例:

{
	"code": "0",
	"msg": "成功",
	"data": {
		"createTime": "2019-08-17 08:18:20",
		"userName": "HelloWorld",
		"mobile": "16666666666",
		"password": "e10adc3949ba59abbe56e057f20f883e",
		"token": "e10adc3949ba59abbe56e057f20f883e",
		"birthday": "2001-05-17",
		"height": 0,
		"weight": 0,
		"sex": "BOY"
	}
}

如果是数组的方式返回:

{
	"code": "0",
	"msg": "成功",
	"data": [
		"createTime": "2019-08-17 08:18:20",
		"userName": "HelloWorld",
		"mobile": "16666666666",
		"password": "e10adc3949ba59abbe56e057f20f883e",
		"token": "e10adc3949ba59abbe56e057f20f883e",
		"birthday": "2001-05-17",
		"height": 0,
		"weight": 0,
		"sex": "BOY"
	]
}

代码处修改:

void onGetListSuccess(List<UserInfoBean> dto);
void onGetListFailed(String msg);

/**
  * 数组形式
  */
  @Multipart
  @POST("app/getList")
  Call<HttpBaseDto<List<UserInfoBean>>> getList(@Field("pageSize") int pageSize,
                                                @Field("pageNo") int pageNo);

public void getList(int pageSize, int pageNo) {
        Call<HttpBaseDto<List<UserInfoBean>>> call = RetrofitFactory.getInstance().getList(pageSize, pageNo);
        new HttpBaseCallBack<>(mActivity, call).getInstanceResponse(
                new HttpBaseCallBack.ResponseListener<List<UserInfoBean>>() {
                    @Override
                    public void onSuccess(List<UserInfoBean> dto) {
                    }

                    @Override
                    public void onFailure(String msg) {
                    }
                });
    }