课堂笔记
- 创建 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);
......