歡迎頁輪播動畫

来源:https://www.cnblogs.com/LiuZhen/archive/2023/04/19/17333741.html
-Advertisement-
Play Games

如圖,引導開始,球從上落下,同時淡入文字,然後文字開始輪播,最後一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實現 首先新建一個類 TextTranslationXGuideView,用於動畫展示 文本是類似的,最後會有個圖片箭頭動畫,佈局很簡單,就是一個 TextVi ...


如圖,引導開始,球從上落下,同時淡入文字,然後文字開始輪播,最後一頁時停止,點擊進入首頁。

在來看看效果圖。

重力球先不講,主要歡迎輪播簡單實現

首先新建一個類 TextTranslationXGuideView,用於動畫展示

文本是類似的,最後會有個圖片箭頭動畫,佈局很簡單,就是一個 TextView 跟 ImageView,直接寫 xml 佈局里方便了

所以 TextTranslationXGuideView 直接繼承 FrameLayout,然後動態添加佈局,控制動畫

val root = LayoutInflater.from(context)
            .inflate(R.layout.login_layout_text_translation_x_guide, this, false)
        root.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
        addView(root)
        mBinding = LoginLayoutTextTranslationXGuideBinding.bind(root)
login_layout_text_translation_x_guide
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:background="@color/white"
    tools:layout_marginStart="@dimen/dp_24">

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:fontFamily="@font/misans_bold"
        android:lineSpacingExtra="@dimen/dp_20"
        android:textColor="@color/bl_black"
        android:textSize="@dimen/sp_36"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="歡迎xxx\n111" />

    <ImageView
        android:id="@+id/iv_guide1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="@dimen/dp_30"
        android:src="@drawable/login_guide_text_right_black"
        android:visibility="gone"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/tv_content"
        tools:visibility="visible" />

    <ImageView
        android:id="@+id/iv_guide2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/login_guide_text_right_end"
        android:visibility="gone"
        app:layout_constraintBottom_toBottomOf="@+id/iv_guide1"
        app:layout_constraintStart_toEndOf="@+id/iv_guide1"
        app:layout_constraintTop_toTopOf="@+id/iv_guide1"
        app:tint="@color/c_f4f4f4"
        tools:visibility="visible" />

</androidx.constraintlayout.widget.ConstraintLayout>

文字顏色換行等通過 span 設置,所以需要一個類去配置

data class TextTranslationXGuideBean(
        val content: String, //內容
        val bright: String?, //高亮文本
        val brightColor: Int = R.color.bl_black //高亮字體顏色
    )

輪播配置成動態的,所以這裡使用一個集合去存儲

private val guideList = mutableListOf<TextTranslationXGuideBean>()
/**
     * 添加單個引導文本
     * @param content 內容
     * @param bright 高亮文本
     * @param brightColor 高亮字體顏色
     * */
    fun addTextGuide(
        content: String,
        bright: String? = null,
        brightColor: Int? = null
    ): TextTranslationXGuideView {
        guideList.add(TextTranslationXGuideBean(content, bright, brightColor ?: R.color.bl_black))
        return this
    }

然後在動態設置內容跟圖片

/** 設置引導內容 */
    private fun setGuideContent(bean: TextTranslationXGuideBean) {
        mBinding?.tvContent?.text = bean.content
        val span = SpanUtils.with(mBinding?.tvContent)
            .append(bean.content)
            .setForegroundColor(resources.getColor(R.color.bl_black, null))
        bean.bright?.let {
            span.append("\n${bean.bright}")
                .setForegroundColor(resources.getColor(bean.brightColor, null))
        }
        span.create()
    }

接下來需要兩個動畫,一個淡入,一個平移(TextView 自帶的跑馬燈不好控制,後期如果更換方案改動也大)

private var mTranslationAnimator: ValueAnimator? = null
private var mFlickerAnimator: ValueAnimator? = null

init {
        initView()
        initTranslationAnimation()
        initGuideRightAnimate()
    }

平移動畫重覆執行,輪播顯示,通過下標控制,顯示 guideList 中的數據,如果輪播到最後一條,展示箭頭閃爍動畫

private fun initTranslationAnimation() {
        val point = -ScreenUtils.getScreenWidth().toFloat()
        mTranslationAnimator = ValueAnimator.ofFloat(0f, point)
        mTranslationAnimator?.duration = 300
        mTranslationAnimator?.interpolator = LinearInterpolator()
        mTranslationAnimator?.addUpdateListener { animation ->
            val scrollX = animation.animatedValue as Float
            translationX = scrollX
            if (scrollX <= point) {
                mTranslationAnimator?.cancel()
                alpha = 0f
                translationX = 0f
                nextGuide()
            }
        }
    }

private fun initTranslationAnimation() {
        val point = -ScreenUtils.getScreenWidth().toFloat()
        mTranslationAnimator = ValueAnimator.ofFloat(0f, point)
        mTranslationAnimator?.duration = 300
        mTranslationAnimator?.interpolator = LinearInterpolator()
        mTranslationAnimator?.addUpdateListener { animation ->
            val scrollX = animation.animatedValue as Float
            translationX = scrollX
            if (scrollX <= point) {
                mTranslationAnimator?.cancel()
                alpha = 0f
                translationX = 0f
                nextGuide()
            }
        }
    }


/** 開始時調用 */
    fun initGuide() {
        position = 0
        if (guideList.size > 0) {
            guideList.getOrNull(position)?.let {
                setGuideContent(it)
            }
            //漸入
            alpha = 0f
            startAlphaAnimation(1500) {
                startTranslationAnimator()
            }
        }
    }

結束時清楚緩存跳轉首頁

fun clear() {
        guideList.clear()
        mTranslationAnimator?.cancel()
        mTranslationAnimator = null
        mFlickerAnimator?.cancel()
        mFlickerAnimator = null
    }

全部實現

/** 登錄引導動畫 */
class TextTranslationXGuideView(context: Context, attrs: AttributeSet?) :
    FrameLayout(context, attrs) {

    private var mBinding: LoginLayoutTextTranslationXGuideBinding? = null

    private var mTranslationAnimator: ValueAnimator? = null
    private var mFlickerAnimator: ValueAnimator? = null

    private val guideList = mutableListOf<TextTranslationXGuideBean>()
    private var position = 0//當前顯示的引導索引

    var clickRight: (() -> Unit)? = null

    init {
        initView()
        initTranslationAnimation()
        initGuideRightAnimate()
    }

    private fun initView() {
        val root = LayoutInflater.from(context)
            .inflate(R.layout.login_layout_text_translation_x_guide, this, false)
        root.layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
        addView(root)
        mBinding = LoginLayoutTextTranslationXGuideBinding.bind(root)
        mBinding?.ivGuide1?.setOnThrottledClickListener {
            clickRight?.invoke()
        }
        mBinding?.ivGuide2?.setOnThrottledClickListener {
            clickRight?.invoke()
        }
    }

    private fun initTranslationAnimation() {
        val point = -ScreenUtils.getScreenWidth().toFloat()
        mTranslationAnimator = ValueAnimator.ofFloat(0f, point)
        mTranslationAnimator?.duration = 300
        mTranslationAnimator?.interpolator = LinearInterpolator()
        mTranslationAnimator?.addUpdateListener { animation ->
            val scrollX = animation.animatedValue as Float
            translationX = scrollX
            if (scrollX <= point) {
                mTranslationAnimator?.cancel()
                alpha = 0f
                translationX = 0f
                nextGuide()
            }
        }
    }

    private fun startTranslationAnimator() {
        mTranslationAnimator?.start()
    }

    private fun initGuideRightAnimate() {
        mFlickerAnimator = ValueAnimator.ofFloat(0f, 1f)
        mFlickerAnimator?.duration = 600
        mFlickerAnimator?.interpolator = LinearInterpolator()
        mFlickerAnimator?.repeatMode = ValueAnimator.REVERSE
        mFlickerAnimator?.repeatCount = ValueAnimator.INFINITE
        mFlickerAnimator?.addUpdateListener { animation ->
            val alpha = animation.animatedValue as Float
            mBinding?.ivGuide2?.alpha = alpha
        }
    }

    private fun startGuideRightAnimator() {
        mBinding?.ivGuide2?.visibility = View.VISIBLE
        mBinding?.ivGuide2?.alpha = 0f
        mFlickerAnimator?.start()
    }

    /** 開始時調用 */
    fun initGuide() {
        position = 0
        if (guideList.size > 0) {
            guideList.getOrNull(position)?.let {
                setGuideContent(it)
            }
            //漸入
            alpha = 0f
            startAlphaAnimation(1500) {
                startTranslationAnimator()
            }
        }
    }

    /** 下一個引導 */
    private fun nextGuide() {
        position += 1
        //是否為最後一條數據
        val isEndGuide = position == guideList.size - 1
        //第一個圖標需要先展示
        mBinding?.ivGuide1?.visibility = if (isEndGuide) View.VISIBLE else View.GONE
        guideList.getOrNull(position)?.let {
            setGuideContent(it)
            startAlphaAnimation {
                if (position < guideList.size - 1) {
                    //如果有,迴圈執行下一個引導
                    startTranslationAnimator()
                } else {
                    //最後一個,執行漸變閃爍動畫
                    startGuideRightAnimator()
                }
            }
        }
    }

    private fun startAlphaAnimation(duration: Long = 1000L, endListener: (() -> Unit)) {
        animate().setDuration(duration).alpha(1f)
            .setListener(object : Animator.AnimatorListener {
                override fun onAnimationStart(p0: Animator?) {}

                override fun onAnimationEnd(p0: Animator?) {
                    endListener.invoke()
                }

                override fun onAnimationCancel(p0: Animator?) {}

                override fun onAnimationRepeat(p0: Animator?) {}
            })
    }

    /** 設置引導內容 */
    private fun setGuideContent(bean: TextTranslationXGuideBean) {
        mBinding?.tvContent?.text = bean.content
        val span = SpanUtils.with(mBinding?.tvContent)
            .append(bean.content)
            .setForegroundColor(resources.getColor(R.color.bl_black, null))
        bean.bright?.let {
            span.append("\n${bean.bright}")
                .setForegroundColor(resources.getColor(bean.brightColor, null))
        }
        span.create()
    }

    /**
     * 添加單個引導文本
     * @param content 內容
     * @param bright 高亮文本
     * @param brightColor 高亮字體顏色
     * */
    fun addTextGuide(
        content: String,
        bright: String? = null,
        brightColor: Int? = null
    ): TextTranslationXGuideView {
        guideList.add(TextTranslationXGuideBean(content, bright, brightColor ?: R.color.bl_black))
        return this
    }

    fun clear() {
        guideList.clear()
        mTranslationAnimator?.cancel()
        mTranslationAnimator = null
        mFlickerAnimator?.cancel()
        mFlickerAnimator = null
    }

    data class TextTranslationXGuideBean(
        val content: String, //內容
        val bright: String?, //高亮文本
        val brightColor: Int = R.color.bl_black //高亮字體顏色
    )

}
TextTranslationXGuideView
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 0.linux的目錄結構 1.用戶和用戶組的信息存儲 1.1. 用戶的基本信息文件/etc/passwd 1.1.1. 用戶名 1.1.2. 密碼 1.1.3. UID 1.1.4. GID 1.1.5. 註釋性描述 1.1.6. 宿主目錄 1.1.7. 預設shell 1.2. 用戶的密碼信息文件 ...
  • 功能02-商鋪查詢緩存 3.商鋪詳情緩存查詢 3.1什麼是緩存? 緩存就是數據交換的緩衝區(稱作Cache),是存儲數據的臨時地方,一般讀寫性能較高。 緩存的作用: 降低後端負載 提高讀寫效率,降低響應時間 緩存的成本: 數據一致性成本 代碼維護成本 運維成本 3.2需求說明 如下,當我們點擊商店詳 ...
  • 關於MySQL的二進位日誌(binlog),我們都知道二進位日誌(binlog)非常重要,尤其當你需要point to point災難恢復的時侯,所以我們要對其進行備份。關於二進位日誌(binlog)的備份,可以基於flush logs方式先切換binlog,然後拷貝&壓縮到到遠程伺服器或本地伺服器 ...
  • 功能實現02 2.功能01-簡訊登錄 2.1基於Session實現登錄 2.1.1思路分析 2.1.2代碼實現 2.1.2.1發送簡訊驗證碼 發送簡訊驗證碼: 發送驗證碼的介面為:http://127.0.0.1:8080/api/user/code?phone=xxxxx<手機號> 請求方式:PO ...
  • 本文將對幾種緩存與資料庫保證數據一致性的使用方式進行分析。為保證高併發性能,以下分析場景不考慮執行的原子性及加鎖等強一致性要求的場景,僅追求最終一致性。 ...
  • GreatSQL社區原創內容未經授權不得隨意使用,轉載請聯繫小編並註明來源。 GreatSQL是MySQL的國產分支版本,使用上與MySQL一致。 作者: 奧特曼愛小怪獸 文章來源:GreatSQL社區原創 往期回顧 MySQL8.0 優化器介紹(一) MySQL8.0 優化器介紹(二) 本篇將進一 ...
  • 承蒙大家的支持,剛上市的《MySQL實戰》已經躍居京東自營資料庫圖書熱賣榜第 1 名,收到的反饋也普遍不錯。對該書感興趣的童鞋可通過右邊的鏈接購買。目前,京東自營有活動,只需 5 折。 主從延遲作為 MySQL 的痛點已經存在很多年了,以至於大家都有一種錯覺:有 MySQL 複製的地方就有主從延遲。 ...
  • 前言:ETL(是Extract-Transform-Load的縮寫,即數據抽取、轉換、裝載的過程),對於企業應用來說,我們經常會遇到各種數據的處理、轉換、遷移的場景。今天特地給大家彙總了一些目前市面上比較常用的ETL數據遷移工具,希望對你會有所幫助。 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...