Android 实现自动滚动布局

前言

在平时的开发中,有时会碰到这样的场景,设计上布局的内容会比较紧凑,导致部分机型上某些布局中的内容显示不完全,或者在数据内容多的情况下,单行无法显示所有内容。这时如果要进行处理,无非就那几种方式:换行、折叠、缩小、截取内容、布局自动滚动等。而这里可以简单介绍下布局自动滚动的一种实现方式。

1. 布局自动滚动的思路

要实现滚动的效果,在Android中无非两种,吸附式的滚动或者顺滑式的滚动,吸附式就是类似viewpager换页的效果,如果需求上是要实现这样的效果,可以使用viewpager进行实现,这个类型比较简单,这里就不过多介绍。另一种是顺滑的,非常丝滑的缓慢移动的那种,要实现这种效果,可以使用RecyclerView或者ScrollView来实现。我这里主要使用ScrollView会简单点。

滑动的控件找到了,那要如何实现丝滑的自动滚动呢?我们都知道ScrollView能用scrollTo和scrollBy去让它滚动到某个位置,但如何去实现丝滑的效果?

这里就用到了属性动画, 我之前的文章也提到过属性动画的强大 juejin.cn/post/714419...

所以我这边会使用ScrollView和属性动画来实现这个效果

2. 最终效果

可以写个Demo来看看最终的效果

这就是一个横向自动滚动的效果。

3. 代码实现

先写个接口定义自动滚动的行为

kotlin 复制代码
interface Autoscroll {

    // 开始自动滚动
    fun autoStart()
    
    // 停止自动滚动
    fun autoStop()

}

然后自定义一个View继承ScrollView,方便阅读,在代码中加了注释

kotlin 复制代码
// 自定义View继承HorizontalScrollView,我这里演示横向滚动的,纵向可以使用ScrollView
class HorizontalAutoscrollLayout @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : HorizontalScrollView(context, attrs, defStyleAttr), Autoscroll {

    // 一些流程上的变量,可以自己去定义,变量多的情况也可以使用builder模式
    var isLoop = true        // 滚动到底后,是否循环滚动
    var loopDelay = 1000L    // 滚动的时间
    var duration = 1000L     // 每一次滚动的间隔时间

    private var offset: Int = 0
    val loopHandler = Handler(Looper.getMainLooper())
    var isAutoStart = false

    private var animator: ValueAnimator? = null

    override fun autoStart() {
        // 需要计算滚动距离所以要把计算得代码写在post里面,等绘制完才拿得到宽度
        post {
            var childView = getChildAt(0)
            childView?.let {
                offset = it.measuredWidth - width
            }

            // 判断能否滑动,这里只判断了一个方向,如果想做两个方向的话,多加一个变量就行
            if (canScrollHorizontally(1)) {
                animator = ValueAnimator.ofInt(0, offset)
                    .setDuration(duration)
                // 属性动画去缓慢改变scrollview的滚动位置,抽象上也可以说改变scrollview的属性
                animator?.addUpdateListener {
                    val currentValue = it.animatedValue as Int
                    scrollTo(currentValue, 0)
                }
                animator?.addListener(object : Animator.AnimatorListener {
                    override fun onAnimationStart(animation: Animator) {

                    }

                    override fun onAnimationEnd(animation: Animator) {
                        // 动画结束后判断是否要重复播放
                        if (isLoop) {
                            loopHandler?.postDelayed({
                                if (isAutoStart) {
                                    scrollTo(0, 0)
                                    autoStart()
                                }
                            }, loopDelay)
                        }
                    }

                    override fun onAnimationCancel(animation: Animator) {

                    }

                    override fun onAnimationRepeat(animation: Animator) {

                    }

                })
                animator?.start()
                isAutoStart = true
            }

        }
    }

    // 动画取消
    override fun autoStop() {
        animator?.cancel()
        isAutoStart = false
        loopHandler.removeCallbacksAndMessages(null)
    }

}

能看到实现这个功能,写的代码不会很多。其中主要需要注意一些点:

(1)属性动画要熟,我这里只是简单的效果,但如果你对属性动画能熟练使用的话,你还可以做到加速、减速等效果

(2)页面关闭的时候要调用autoStop去关闭动画

(3)这里是用scrollTo去实现滚动的效果,scrollBy也可以,但是写法就不是这样了

从代码可以看出没什么难点,都是比较基础的知识,比较重要的知识就是属性动画,熟练的话做这种效果的上限就很高。其他的像这里为什么用post,为什么用scrollTo,这些就是比较基础的知识,就不扩展讲了。

最后看看使用的地方,先是Demo的布局

ini 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:gravity="center_vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.kylin.testproject.HorizontalAutoscrollLayout
        android:id="@+id/auto_scroll"
        android:layout_width="150dp"
        android:layout_height="wrap_content">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            android:gravity="center_vertical"
            >

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="20sp"
                android:text="小日本"
                />

            <ImageView
                android:layout_width="36dp"
                android:layout_height="36dp"
                android:scaleType="fitXY"
                android:src="@drawable/a"
                />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="20sp"
                android:text="排放核废水污染海洋"
                />

            <ImageView
                android:layout_width="36dp"
                android:layout_height="36dp"
                android:scaleType="fitXY"
                android:src="@drawable/b"
                />

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="20sp"
                android:text=",必遭天谴!!"
                />

        </LinearLayout>

    </com.kylin.testproject.HorizontalAutoscrollLayout>

</LinearLayout>

然后在开始播放自动滚动(注意页面关闭的时候要手动停止)

kotlin 复制代码
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.main)

    val autoScroll: HorizontalAutoscrollLayout = findViewById(R.id.auto_scroll)
    autoScroll.duration = 3000
    autoScroll.loopDelay = 2000
    autoScroll.autoStart()
}

4. 总结

代码比较简单,而且都加上了注释,所以没有其他要说明的。

前段时间太忙,所以这几个月都没时间写文章。想了一下,这个还是要坚持,如果有时间的话抽出点时间一天写一点,得保持一个常更新的状态。

相关推荐
阿巴斯甜19 小时前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker20 小时前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq952721 小时前
Andorid Google 登录接入文档
android
黄林晴1 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab1 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android
张小潇2 天前
AOSP15 Input专题InputReader源码分析
android
_小马快跑_2 天前
Kotlin | 协程调度器选择:何时用CoroutineScope配置,何时用launch指定?
android