课堂笔记
- 创建 LoadingView 继承 AppCompatImageView ,用 this 代替 super 把构造方法指向参数最多的那个,然后设置一张图片
public class LoadingView extends AppCompatImageView {
    public LoadingView(Context context) {
        this(context, null);
    }
    public LoadingView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setImageResource(R.mipmap.loading);
    }
}
- 重写 onDraw 方法,让画布以中心点旋转模拟加载过程
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.rotate(duration, getWidth() / 2, getHeight() / 2);
        super.onDraw(canvas);
    }
- duration 是旋转角度,写个开始旋转方法,在里面循环增加角度,并刷新 view
    private void startRotate(){
        post(new Runnable() {
            @Override
            public void run() {
                duration += 10;
                if (duration >= 360) {
                    duration = 0;
                }
                invalidate();
                postDelayed(this, 500);
            }
        });
    }
- 定义 boolean 值,让画布在 onAttachedToWindow 开始旋转,在 onDetachedFromWindow 停止旋转,所有代码
public class LoadingView extends AppCompatImageView {
    private float duration = 0;
    private boolean mNeedRotate = true;
    public LoadingView(Context context) {
        this(context, null);
    }
    public LoadingView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public LoadingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setImageResource(R.mipmap.loading);
    }
    private void startRotate(){
        post(new Runnable() {
            @Override
            public void run() {
                duration += 10;
                if (duration >= 360) {
                    duration = 0;
                }
                invalidate();
                if (getVisibility() != VISIBLE && !mNeedRotate) {
                    removeCallbacks(this);
                } else {
                    postDelayed(this, 500);
                }
            }
        });
    }
    private void stopRotate(){
        mNeedRotate = false;
    }
    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        mNeedRotate = true;
        startRotate();
    }
    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        stopRotate();
    }
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.rotate(duration, getWidth() / 2, getHeight() / 2);
        super.onDraw(canvas);
    }
}
- 最后把 LoadingView 添加到布局 fragment_loading 中
<?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:gravity="center"
    android:orientation="vertical">
    <com.xiaobu.taobaouni.ui.custom.LoadingView
        android:layout_width="33dp"
        android:layout_height="33dp" />
    <TextView
        android:layout_marginTop="10dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/text_loading_tips"/>
</LinearLayout>
课后感悟
这两节其实看了很久....... - 在 onDetachedFromWindow 方法中调用 stopRotate() 真的有必要吗?在执行 onDetachedFromWindow 方法时,view 就要被销毁,所以肯定不会显示旋转的 LoadingView,所以老师在视频里 TestActivity 旋转的 Log 停止就是因为按了返回键,销毁了 view,所以加 boolean 没有作用 - 在【领取联盟】中正常运行会怎么样呢?我们可以打个 Log 看看
private void startRotate(){
        post(new Runnable() {
            @Override
            public void run() {
                duration += 10;
                if (duration >= 360) {
                    duration = 0;
                }
                invalidate();
                LogUtils.e(LoadingView.class,"getVisibility() -----"+(getVisibility() != VISIBLE));
                if (getVisibility() != VISIBLE && !mNeedRotate) {
                    removeCallbacks(this);
                    LogUtils.e(LoadingView.class,"removeCallbacks -----");
                } else {
                    postDelayed(this, 500);
                    LogUtils.e(LoadingView.class,"postDelayed -----");
                }
            }
        });
    }
- 运行比较长时间,已经跳转到成功界面了,因为我的时间间隔时0.5秒... Log 如下  
- 可以看出 postDelayed(this, 500) 一直在运行,removeCallbacks(this) 始终没有运行,而且 getVisibility() != VISIBLE 也一直等于 false ,那就说明 LoadingView 始终是显示的
- 在 BaseFragment 中不是已经让不显示的 view GONE 掉了吗,为什么 LoadingView 还是 VISIBLE?因为 GONE 的布局是 LoadingView 的父布局 LinearLayout,虽然 LoadingView 不显示了,但它的状态还是 VISIBLE,我们可以改一下 fragment_loading 设置成只有 LoadingView
<?xml version="1.0" encoding="utf-8"?>
<com.xiaobu.taobaouni.ui.custom.LoadingView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="33dp"
    android:layout_height="33dp"
    android:gravity="center"
    android:orientation="vertical"/>
- 运行一下  Log 如下  
- 从 Log 可以看出:加载数据时,LoadingView 可见,postDelayed 一直在运行,图标在旋转,当加载完成时,LoadingView 不可见,postDelayed 停止运行,removeCallbacks 运行了一次
- 但又出现一个问题,loading图标不在界面中央,如果设置 match_parent 的话,图标虽然在中央了,但又大又模糊  
- fragment_loading 是手动添加到“坑”里的,我们先要找到它的父布局,LoadingView 是在 BaseFragment 中 loadStatesView 方法里添加的,设置父布局的布局属性内部居中就可以了
    private void loadStatesView(LayoutInflater inflater, ViewGroup container) {
        //加载状态的view
        mLoadingView = loadLoadingView(inflater, container);
        mBaseContainer.addView(mLoadingView);
        FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) mLoadingView.getLayoutParams();
        layoutParams.gravity = Gravity.CENTER;
        mLoadingView.setLayoutParams(layoutParams);
	......




