Android自定义加载框使用Dialog遇到的问题

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1天,点击查看活动详情

相信每个Android开发者都使用过loading加载框,甚至自己也会封装一个全局弹窗,用于网络请求来提高用户体验。网络上也有不少第三方库,这里记录一下本人自己封装和使用加载框遇到的问题。

1. 初期使用Dialog+ProgressBar或者Dialog+ImageView,其实都是通过xml动画使图片重复旋转达到效果。网络请求开始则显示Dialog,网络请求结束关闭Dialog,注意视图的回收。
class ProgresDialog(context: Context) : Dialog(context, R.style.dialog_progress) {
    private var rotateAnimation: Animation? = null
    private var lin: LinearInterpolator? = null
    override fun show() {
        super.show()
        findViewById<View>(R.id.iv).startAnimation(rotateAnimation)
    }

    override fun dismiss() {
        findViewById<View>(R.id.iv).clearAnimation()
        super.dismiss()
    }

    init {
        if (rotateAnimation == null) rotateAnimation =
            AnimationUtils.loadAnimation(context, R.anim.rotate_anim)
        if (lin == null) lin = LinearInterpolator()
        rotateAnimation!!.interpolator = lin
        setContentView(R.layout.dialog_porgress)
        setCanceledOnTouchOutside(false)
    }
}

布局:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <LinearLayout
        android:layout_gravity="center"
        android:layout_width="120dp"
        android:layout_height="80dp"
        android:background="@drawable/dialog_shape_bg"
        android:gravity="center"
        android:orientation="vertical">
        <ProgressBar
            android:visibility="gone"
            android:layout_marginTop="5dp"
            android:layout_width="35dp"
            android:layout_height="35dp"
            android:indeterminate="true"
            android:indeterminateBehavior="repeat"
            android:indeterminateDrawable="@drawable/loading_ani" />
        <ImageView
            android:id="@+id/iv"
            android:src="@mipmap/icon_loading"
            android:layout_marginTop="5dp"
            android:layout_width="35dp"
            android:layout_height="35dp"/>
    </LinearLayout>

</FrameLayout>

这里需要注意xml属性设置,结束角度为359°,否则动画不自然,其他属性根据自己想要的效果配置即可。

动画部分:

<rotate xmlns:android="http://schemas.android.com/apk/res/android">
    <rotate
        android:fromDegrees="0"
        android:toDegrees="359"
        android:pivotX="50%"
        android:pivotY="50%"
        android:duration="1800"
        android:repeatCount="-1">
    </rotate>
</rotate>

这种组装模式局限性很大,只能实现一些简单动画,毕竟是靠图片配合的补间动画。但往往现在的app都是定制的加载动画,所以第一种方式并不能满足我们的需求。下面介绍下后期使用的loading动画效果。

2.进阶版Dialog+LottieAnimation,可以轻松实现复杂动画,解放程序员的双手,是一大福利啊!当然动画素材还得靠美工或者自己寻找喜欢的素材(对美工要求比较高,能够制作导出动画的gson文件)。这里贴下自己用过的免费素材地址

布局几乎不变,将图片或者ProgressBar替换为视图LottieAnimationView即可,配置相当方便,具体使用方式参考Github示例

class ProgresDialog(context: Context) : Dialog(context, R.style.dialog_progress){

    private var animationView: LottieAnimationView

    override fun show() {
        super.show()
        animationView.playAnimation()
    }

    override fun dismiss() {
        animationView.cancelAnimation()
        super.dismiss()
    }
    init {
        setContentView(R.layout.dialog_porgress)
        animationView = findViewById(R.id.animation_view)
        setCanceledOnTouchOutside(false)
    }

}

布局:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center">
    <com.lihang.ShadowLayout
        android:layout_gravity="center"
        app:hl_cornerRadius="5dp"
        app:hl_layoutBackground="#7C7B7B"
        android:layout_width="130dp"
        android:layout_height="wrap_content">
            <com.airbnb.lottie.LottieAnimationView
                android:id="@+id/animation_view"
                android:layout_width="130dp"
                android:layout_height="120dp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:lottie_autoPlay="true"
                app:lottie_loop="true"
                app:lottie_speed="1"
                app:lottie_fileName="loading.json" />
    </com.lihang.ShadowLayout>


</LinearLayout>

替换为LottieAnimationView后使用起来的动画很是酷炫,相当完美(想象一下,如果没有它的出现,实现同样动画效果不得要程序员的老命啊)。担心网络json加载慢的可以像我一样将json直接放在本地,xml中直接引用即可。毕竟网络加载提示的地方几乎每个页面都有,这样也可减少资源消耗。


就在我美滋滋的在沸点摸鱼的时候,测试突然发消息过来说我的加载框翻车了......

1675326085076.jpg

What?加载框居然跑到屏幕左上角去了,也不是所有机型都会复现,就那么一款机型必现。查看布局也没任何问题,生命周期也没问题。也是试过了各种方案总会在那款机子上复现,最后终于发现是progresDialog.hide()的锅。只需要将它修改为dismiss()后便一切恢复正常。

viewLifecycleOwner.lifecycleScope.launch {
    viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
        //监听加载弹窗
        viewModel.loadEvent.collectLatest{
            if (it){
                progresDialog.show()
            }else{
                progresDialog.hide()
            }
        }
    }
}

查看hide源码发现只是做了一个隐藏操作,而dismiss相当于回收视图。至于为什么会出现在左上角也是不得而知,怀疑是父布局测量问题,有没有大神知道的帮小弟解惑呀?

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 1天,点击查看活动详情