依赖
implementation 'com.github.Cutta:GifView:1.4' //在项目中buildscript下添加 maven { url 'https://jitpack.io' }
xml布局
<?xml version="1.0" encoding="utf-8"?>
<com.m.k.seetaoism.widgets.MvpLoadingView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/mvp_loading_iv_gif_bg_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:background="@drawable/mvp_loading_bg"
android:elevation="24dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.cunoraz.gifview.library.GifView
android:id="@+id/mvp_loading_gif_view"
android:layout_width="90dp"
android:layout_height="90dp"
android:elevation="18dp"
app:gif="@drawable/mvp_loading"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/mvp_loading_tv_error_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:text="加载失败,请重试" />
<Button
android:id="@+id/mvp_loading_btn_retry"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="64dp"
android:layout_marginTop="24dp"
android:layout_marginEnd="64dp"
android:background="@drawable/mvp_loading_retry_button_bg_selector"
android:text="点击重试"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/mvp_loading_gif_view" />
<ImageView
android:id="@+id/mvp_loading_iv_error_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:background="@drawable/mvp_loging_error"
app:layout_constraintBottom_toTopOf="@+id/mvp_loading_gif_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.493"
app:layout_constraintStart_toStartOf="parent" />
<androidx.constraintlayout.widget.Group
android:id="@+id/mvp_loading_group_error"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone"
app:constraint_referenced_ids="mvp_loading_tv_error_content,mvp_loading_btn_retry,mvp_loading_iv_error_icon" />
</com.m.k.seetaoism.widgets.MvpLoadingView>
mvp_loading_retry_button_bg_selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:layout_width="match_parent" android:layout_height="match_parent" android:shape="rectangle">
<corners android:radius="20dp" />
<stroke android:width="1dp" android:color="#ffffff" />
<solid android:color="#dbdbdb" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<corners android:radius="20dp"/>
<stroke android:color="#dbdbdb" android:width="1dp"/>
<solid android:color="#ffffff"/>
</shape>
</item>
</selector>
MvpLoadingView 类
package com.example.loadinglib;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.IntDef;
import androidx.appcompat.widget.ContentFrameLayout;
import androidx.constraintlayout.widget.ConstraintLayout;
import androidx.constraintlayout.widget.ConstraintSet;
import androidx.constraintlayout.widget.Group;
import com.cunoraz.gifview.library.GifView;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class MvpLoadingView extends ConstraintLayout {
public static final int MODE_POP = 1;
public static final int MODE_FULL = 2;
private static final int MODE_ERROR= 3;// 错误
private ImageView mGifBackgroundView;
private GifView mGifView;
private Group mErrorPage;
private ImageView mErrorIcon;
private TextView mErrorMessage;
private Button mRetry;
private ViewGroup mParent;
private OnRetryCallBack mRetryCallBack;
private OnCancelCallBack mCancelCallBack;
private int mCurrentMode;
private int mPreMode;
private boolean isEnableBackCancel; // 按返回键是否支持关闭 loading
@Retention(RetentionPolicy.SOURCE)
@IntDef({MODE_POP, MODE_FULL})
public @interface LoadingMode {
}
public MvpLoadingView(Context context) {
super(context);
}
public MvpLoadingView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MvpLoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@SuppressLint("ResourceType")
@Override
protected void onFinishInflate() {
super.onFinishInflate();
setId(10000);
mGifBackgroundView = findViewById(R.id.mvp_loading_iv_gif_bg_view);
mGifView = findViewById(R.id.mvp_loading_gif_view);
mErrorPage = findViewById(R.id.mvp_loading_group_error);
mErrorIcon = findViewById(R.id.mvp_loading_iv_error_icon);
mErrorMessage = findViewById(R.id.mvp_loading_tv_error_content);
mRetry = findViewById(R.id.mvp_loading_btn_retry);
mRetry.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mRetryCallBack != null){
mRetryCallBack.onRetry();
showLoading(mPreMode);
}
}
});
setFocusableInTouchMode(true);
requestFocus();
setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// 如果支持网络取消,那么关闭自己
// 如果不支持,不拦截
if(isEnableBackCancel){
if(mCancelCallBack != null){
mCancelCallBack.onCancel(); // 回到外面调用者去取消网络请求
}
closeLoading();
return true;
}else{
return false;
}
}
});
}
public void setEnableBackCancel(boolean enableBackCancel) {
isEnableBackCancel = enableBackCancel;
}
public void setCancelCallBack(OnCancelCallBack cancelCallBack) {
this.mCancelCallBack = cancelCallBack;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return false;
}
public void setParentContainer(ViewGroup parent) {
mParent = parent;
}
public static MvpLoadingView inject(ViewGroup parent) {
// 为了避免重复添加,那么判断一下传进来的 parent 上是否已经添加了loading view
View child;
for (int i = parent.getChildCount() - 1; i >= 0; i--) {
child = parent.getChildAt(i);
if (child instanceof MvpLoadingView) {
return (MvpLoadingView) child;
}
}
MvpLoadingView loadingView = (MvpLoadingView) LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_loading_view, parent, false);
loadingView.setParentContainer(parent);
return loadingView;
}
public void showLoading(@LoadingMode int mode) {
if (this.getParent() == null) {
String parentClassName = mParent.getClass().getName();
if (parentClassName.equals(RelativeLayout.class.getName()) || parentClassName.equals(FrameLayout.class.getName()) || parentClassName.equals(ContentFrameLayout.class.getName())) {
mParent.addView(this, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
} else if (mParent instanceof ConstraintLayout) {
mParent.addView(this);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone((ConstraintLayout) mParent);
constraintSet.connect(getId(), ConstraintSet.START, ConstraintSet.PARENT_ID, ConstraintSet.START);
constraintSet.connect(getId(), ConstraintSet.TOP, ConstraintSet.PARENT_ID, ConstraintSet.TOP);
constraintSet.connect(getId(), ConstraintSet.END, mParent.getId(), ConstraintSet.END);
constraintSet.connect(getId(), ConstraintSet.BOTTOM, mParent.getId(), ConstraintSet.BOTTOM);
constraintSet.applyTo((ConstraintLayout) mParent);
}
}
if(mCurrentMode == mode){
return;
}
mPreMode = mCurrentMode;
mCurrentMode = mode;
mErrorPage.setVisibility(GONE);
if (mode == MODE_POP) {
setBackgroundColor(Color.TRANSPARENT);
mGifBackgroundView.setVisibility(VISIBLE);
} else {
setBackgroundColor(Color.WHITE);
mGifBackgroundView.setVisibility(GONE);
}
mGifView.setVisibility(VISIBLE);
mGifView.play();
}
public void onError(){
onError("加载失败,请重试",null);
}
public void onError(OnRetryCallBack onRetryCallBack){
onError("加载失败,请重试",onRetryCallBack);
}
public void onError(String errMessage,OnRetryCallBack onRetryCallBack){
mRetryCallBack = onRetryCallBack;
mGifView.pause();
mGifView.setVisibility(GONE);
mGifBackgroundView.setVisibility(GONE);
mErrorPage.setVisibility(VISIBLE);
if(mCurrentMode != MODE_FULL){
setBackgroundColor(Color.WHITE);
}
mErrorMessage.setText(errMessage);
mRetry.setVisibility(mRetryCallBack == null ? GONE : VISIBLE);
mPreMode = mCurrentMode;
mCurrentMode = MODE_ERROR;
}
public void closeLoading() {
if (mParent != null) {
mParent.removeView(this);
}
}
public interface OnRetryCallBack{
void onRetry();
}
public interface OnCancelCallBack{
void onCancel();
}
}