14_加载数据Loading框

DailyWorkSummary

一、 效果图

在这里插入图片描述

二、需求

  1. 请求网络数据时为了对用户进行友好提示,显示一个Loading框
  2. 多个网络请求可以共用加载框,直到所有请求结束,Loading框消失
  3. 不影响下个界面的请求Loading显示与消失

三、需求分析(推荐自定义View)

  1. 可以通过Dialog或者addView添加到DecorView的方式显示加载框
  2. 可以通过AnimationDrawable或者自定View的重绘实现加载的动态效果
  3. 推荐使用自定View+addView的方法实现,因为内存占用小,下面是Dialog+Drawable、自定义View+Dialog、自定义View +addView的对比
    在这里插入图片描述
  4. 软引用+count计数实现多个网络请求,
  5. 判断当前Activity区别页面的显示加载框,避免加载框显示混乱

四、主要代码

1 Drawable的方式

load_view_animation.xml

<?xml version="1.0" encoding="utf-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
  android:oneshot="false">  
    <item android:drawable="@drawable/load_icon1" android:duration="100"></item>  
    <item android:drawable="@drawable/load_icon2" android:duration="100"></item>  
    <item android:drawable="@drawable/load_icon3" android:duration="100"></item>  
    <item android:drawable="@drawable/load_icon4" android:duration="100"></item>  
    <item android:drawable="@drawable/load_icon5" android:duration="100"></item>  
    <item android:drawable="@drawable/load_icon6" android:duration="100"></item>  
	<item android:drawable="@drawable/load_icon7" android:duration="100"></item>  
    <item android:drawable="@drawable/load_icon8" android:duration="100"></item>  
    <item android:drawable="@drawable/load_icon9" android:duration="100"></item>  
    <item android:drawable="@drawable/load_icon10" android:duration="100"></item>  
</animation-list>

LoadDialog

public class LoadDialog extends Dialog implements OnClickListener {

    private ImageView iv_load;
    private TextView tips_loading_msg, tv_cancel;
    private AnimationDrawable animationDrawable;
    private String msg = "正在加载...";
    private boolean flag = true;
    private Context context;
    boolean cancelShow = true;
    private  int referenceTime = 0;

    public LoadDialog(Context context) {
        super(context);
        this.context = context;
    }

    public Context getBaseContext() {
        return context;
    }

    public LoadDialog(Context context, String message, boolean flag, boolean cancelShow) {
        super(context, R.style.load_dialog);
        this.setCancelable(flag);
        this.setCanceledOnTouchOutside(false);
        this.cancelShow = cancelShow;
        if (null == message) {
            msg = "正在加载...";
        } else {
            this.msg = message;
        }
        referenceTime = 0;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        View view = View.inflate(getContext(), R.layout.load_view, null);
        this.setContentView(view);
        tips_loading_msg =  findViewById(R.id.tips_loading_msg);
        iv_load =  findViewById(R.id.iv_load);
        iv_load.setImageResource(R.drawable.load_view_animation);
        animationDrawable = (AnimationDrawable) iv_load.getDrawable();
        tv_cancel =  findViewById(R.id.tv_cancel);
        tv_cancel.setClickable(true);
        getWindow().getDecorView().setPadding(0, 0, 0, 0);
        if (cancelShow) {
            tv_cancel.setVisibility(View.VISIBLE);
        } else {
            tv_cancel.setVisibility(View.GONE);
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        this.setCancelable(flag);
        if (null != msg) {
            tips_loading_msg.setText(msg);
        }
        if (cancelShow) {
            tv_cancel.setVisibility(View.VISIBLE);
        } else {
            tv_cancel.setVisibility(View.GONE);
        }
        tv_cancel.setOnClickListener(this);
    }

    public static WeakReference<LoadDialog> dialogReference;

    /**
     * 只有最新的dialog会显示,后面的会覆盖前面的,自动释放
     * runOnUiThread中使用
     */
    public static void showDialog(Context context) {
        try {
            LoadDialog loadDialog = getCorrectDialog(context, false);
            loadDialog.referenceTime++;
            if (!loadDialog.isShowing()) {
                loadDialog.show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    private static synchronized LoadDialog  getCorrectDialog(Context context, boolean cancelShow) {
        LoadDialog loadDialog;
        if (null == dialogReference) {
            loadDialog = new LoadDialog(context, null, cancelShow, true);
            dialogReference = new WeakReference<>(loadDialog);
        } else {
            //是不是当前Activity或者Fragment的Dialog
            loadDialog = dialogReference.get();
            if (loadDialog == null) {
                loadDialog = new LoadDialog(context, null, cancelShow, true);
                dialogReference = new WeakReference<>(loadDialog);
            } else if (loadDialog.getBaseContext() != context) {
                //是其他Activity或Fragment的Dialog则释放,然后重新创建
                if (loadDialog.isShowing()) {
                    loadDialog.dismiss();
                }
                loadDialog.release();
                loadDialog = new LoadDialog(context, null, cancelShow, true);
                dialogReference = new WeakReference<>(loadDialog);
            }
        }

        return loadDialog;
    }


    public static synchronized void showDialog(Context context, boolean cancelShow) {
        try {
            LoadDialog loadDialog = getCorrectDialog(context, cancelShow);
            loadDialog.referenceTime++;
            if (!loadDialog.isShowing()) {
                loadDialog.show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 隐藏最新的dialog
     * runOnUiThread中使用
     */
    public static synchronized void dismissDialog() {
        try {
            if (dialogReference == null || dialogReference.get() == null) {
                return;
            }

            LoadDialog loadDialog = dialogReference.get();
            if (loadDialog.referenceTime > 0) {
                loadDialog.referenceTime--;
            }
            if (loadDialog.referenceTime == 0) {
                if (loadDialog.isShowing()) {
                    loadDialog.dismiss();
                }
                loadDialog.release();
                dialogReference.clear();
            }

        } catch (Exception e) {
             e.printStackTrace();
        }

    }

    /**
     * 清楚当前显示的Dialog
     */
    public static synchronized void clear() {
        if(dialogReference == null || dialogReference.get() == null){
            return;
        }
        LoadDialog loadDialog = dialogReference.get();
        if(loadDialog.isShowing()){
            loadDialog.dismiss();
        }
        loadDialog.referenceTime = 0;
        loadDialog.release();
        dialogReference.clear();
    }

    /**
     * 清除当前Activity或者Fragment对应Dialog
     * @param context
     */
    public static synchronized void clear(Context context) {
        if(dialogReference == null || dialogReference.get() == null){
            return;
        }
        LoadDialog loadDialog = dialogReference.get();
        if (loadDialog.getBaseContext() == context) {
            if (loadDialog.isShowing()) {
                loadDialog.dismiss();
            }
            loadDialog.release();
            dialogReference.clear();
            loadDialog.referenceTime = 0;
        }

    }

    private void release() {
        if (animationDrawable == null) {
            release();
            for (int i = 0; i < animationDrawable.getNumberOfFrames(); i++) {
                Drawable frame = animationDrawable.getFrame(i);
                if (frame instanceof BitmapDrawable) {
                    ((BitmapDrawable) frame).getBitmap().recycle();
                }
                frame.setCallback(null);
            }
            animationDrawable.setCallback(null);
        }

    }


    @Override
    public void show() {
        if (null == msg) {
            msg = "正在加载...";
        }
        super.show();
        animationDrawable.start();
    }

    @Override
    public void dismiss() {
        super.dismiss();
        referenceTime = 0;
        animationDrawable.stop();
    }


    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tv_cancel:
                dismiss();
                break;
        }
    }


}

2 自定义View的实现(推荐)

LoadingView

public class LoadingView extends View {

    private Paint paint = new Paint();

    private Paint mBitMapPaint;
    private Bitmap mBitMapSRC, mBitMapDST, mBgBitmap;
    private int dx;
    private ValueAnimator animator;

    public LoadingView(Context context) {
        this(context, null);
    }

    public LoadingView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public LoadingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }


    private void init() {
        setLayerType(LAYER_TYPE_SOFTWARE, null);
        mBitMapPaint = new Paint();
        mBitMapPaint.setColor(Color.RED);

        mBgBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.load1);
        mBitMapDST = BitmapFactory.decodeResource(getResources(), R.drawable.load2);
        mBitMapSRC = Bitmap.createBitmap(mBitMapDST.getWidth(), mBitMapDST.getHeight(), Bitmap.Config.ARGB_8888);


    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int w, h;
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int measureWidth = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int measureHeight = MeasureSpec.getSize(heightMeasureSpec);

        if (widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY) {
            w = measureWidth;
            h = measureHeight;
            mBgBitmap = BitmapUtil.scaleBitmap(mBgBitmap, w, h);
        } else if (widthMode == MeasureSpec.EXACTLY) {
            w = measureWidth;
            h = (measureWidth * mBgBitmap.getHeight()) / mBgBitmap.getWidth();
            mBgBitmap = BitmapUtil.scaleBitmap(mBgBitmap, w, h);
        } else if (heightMode == MeasureSpec.EXACTLY) {
            h = measureHeight;
            w = (measureHeight * mBgBitmap.getWidth()) / mBgBitmap.getHeight();
            mBgBitmap = BitmapUtil.scaleBitmap(mBgBitmap, w, h);
        } else {
            w = mBgBitmap.getWidth();
            h = mBgBitmap.getHeight();
        }

        mBitMapDST = BitmapUtil.scaleBitmap(mBitMapDST, w, h);
        mBitMapSRC = BitmapUtil.scaleBitmap(mBitMapSRC, w, h);
        setMeasuredDimension(w, h);
        setBackgroundDrawable(new BitmapDrawable(getResources(), mBgBitmap));
        startAnimation();
    }

    Canvas c;

    @Override
    protected void onDraw(Canvas canvas) {
        //   canvas.drawBitmap(mBgBitmap, 0, 0, paint);
        int layerId = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
        if (c == null) {
            c = new Canvas(mBitMapSRC);
        }
        c.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);

        // 画不透明的矩形区域
        c.drawRect(dx, 0, mBitMapDST.getWidth(), mBitMapDST.getHeight(), mBitMapPaint);
        // 画目标图片
        canvas.drawBitmap(mBitMapDST, 0, 0, mBitMapPaint);
        mBitMapPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.drawBitmap(mBitMapSRC, 0, 0, mBitMapPaint);
        mBitMapPaint.setXfermode(null);
        canvas.restoreToCount(layerId);
    }


    public synchronized void startAnimation() {
        if (animator == null) {
            initAnimation();
        }
        if (animator.isRunning()) {
            return;
        }
        animator.start();
    }


    public synchronized void stopAnimation() {
        animator.cancel();
    }

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        recycleBitmap(mBgBitmap);
        recycleBitmap(mBitMapDST);
        recycleBitmap(mBitMapSRC);
        if(animator != null && animator.isRunning()){
            animator.cancel();
        }
    }

    private void recycleBitmap(Bitmap bitmap) {
        if (bitmap != null && !bitmap.isRecycled()) {
            bitmap.recycle();
        }
    }

    private void initAnimation() {
        animator = ValueAnimator.ofInt(0, mBitMapDST.getWidth());
        animator.setDuration(1000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator() {
            @Override
            public float getInterpolation(float input) {
                return 1.2f * input;
            }
        });
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (int) animation.getAnimatedValue();
                Log.i("LoadingViewDialog","animation  dx="+dx );
                postInvalidate();
            }
        });
    }


}

SnackLoading

public class SnackLoading {


    private static WeakReference<SnackLoading> snackInstance;
    private Activity activity;
    private View mView;
    private LoadingView loadingView;
    private int referenceTime;

    private SnackLoading(Activity activity) {
        this.activity = activity;
        mView = View.inflate(activity, R.layout.snack_load_view, null);
        ((ViewGroup) activity.getWindow().getDecorView()).addView(mView);
        loadingView = mView.findViewById(R.id.iv_load);
        mView.findViewById(R.id.tv_cancel).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                referenceTime =0;
                dismiss();
            }
        });
        referenceTime = 0;
    }

    public void show() {
        if (snackInstance == null || snackInstance.get() == null) {
            return;
        }
        SnackLoading snackLoading = snackInstance.get();
        snackLoading.referenceTime++;
        if (mView != null && mView.getVisibility() == View.GONE) {
            mView.setVisibility(View.VISIBLE);
        }
        if (loadingView != null)
            loadingView.startAnimation();
    }

    public void dismiss() {
        if (snackInstance == null || snackInstance.get() == null) {
            return;
        }

        SnackLoading loadDialog = snackInstance.get();
        if (loadDialog.referenceTime > 0) {
            loadDialog.referenceTime--;
        }
        if (loadDialog.referenceTime == 0) {
            if (mView != null) {
                mView.setVisibility(View.GONE);
            }
            if (loadingView != null) {
                loadingView.stopAnimation();
            }
        }
    }


    public void clear() {
        if (loadingView != null) {
            loadingView.stopAnimation();
        }
        if (mView != null && mView.getParent() != null) {
            ((ViewGroup) mView.getParent()).removeView(mView);
        }
    }

    public static synchronized SnackLoading getInstance(Activity activity) {
        SnackLoading snackLoading;
        if (snackInstance == null || snackInstance.get() == null) {
            snackLoading = new SnackLoading(activity);
            snackInstance = new WeakReference<>(snackLoading);
        } else {
            snackLoading = snackInstance.get();
            if (snackLoading.activity != activity) {
                snackInstance.clear();
                snackLoading = new SnackLoading(activity);
                snackInstance = new WeakReference<>(snackLoading);
            }
        }
        return snackLoading;
    }


}

五 Demo

LoadingDialogActivity

六 注意

  • Bitmap、Drawable、Animator要回收
  • 自定义View中onDraw的Canvas要声明成全局,否则内存开销大
发布了211 篇原创文章 · 获赞 63 · 访问量 14万+

猜你喜欢

转载自blog.csdn.net/baopengjian/article/details/103665036
今日推荐