banner前后两张图实现滑动前景图背景图渐变效果(类似自如app的banner效果)

使用方法:

mBinding.bnBanner.setOnPageChangeListener(object : OnPageChangeListener {
            val viewScrollGradientHelper = BannerScrollGradientHelper(mBinding.bannerCurrentBg, mBinding.bannerWillShowBg)
            override fun onPageScrollStateChanged(p0: Int) {
            }

            override fun onPageScrolled(position: Int, offsetPercent: Float, offsetPx: Int) {
                // offsetPercent 代表当前滑动的比例,往后滑动【从0到1区间渐渐变大】,往前滑动【从1到0区间渐渐变小】
                // 开始渐变滑动
                viewScrollGradientHelper.startGradientByOffsetPercent(requireContext(), position, offsetPercent, offsetPx, bean.bannerBgUrlList) {
                    // banner滑动到某个位置的埋点
                    dataTrackOnBanner(bean.list, position)
                }
            }

            override fun onPageSelected(position: Int) {

            }

        })

思路:

根据banner滑动监听,滑动的比例offsetPercent (【offsetPercent】0~1,往下一张滑动从0到1变大,往上一张滑动从1到0变小) 逐渐改变透明度;两张背景图交替变化为当前图控件和将要显示的控件(为了处理图片闪烁问题,透明度瞬间改变有闪烁);

资源准备:

1.banner控件:来自三方控件

// gradle中引入
api 'com.youth.banner:banner:1.4.10'

2. 布局(嵌套到你的父布局中,显示banner的位置):

<!-- banner底部背景 -->
<FrameLayout
    android:id="@+id/frame_banner"
    android:layout_width="match_parent"
    android:layout_height="225dp"
    android:visibility="gone"
    app:layout_constraintTop_toTopOf="parent"
    tools:visibility="visible">
    
    <!-- 背景图A 当前背景图 -->
    <ImageView
        android:id="@+id/banner_current_bg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:alpha="1"
        android:scaleType="centerCrop" />
    <!-- 背景图B 将要展示的背景图 -->
    <ImageView
        android:id="@+id/banner_will_show_bg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:alpha="0"
        android:scaleType="centerCrop" />
    
    <!-- 前景图banner -->
    <com.youth.banner.Banner
        android:id="@+id/bn_banner"
        android:layout_width="match_parent"
        android:layout_height="225dp"
        android:layout_gravity="center"
        app:indicator_height="3dp"
        app:indicator_margin="3dp"
        app:indicator_width="9dp"
        app:scroll_time="1600"
        tools:background="@drawable/banner_placeholder"
        tools:visibility="gone" />
</FrameLayout>

3. 滑动渐变帮助类:

package com.xxx.helper

import android.content.Context
import android.widget.ImageView
import com.czb.chezhubang.base.utils.GlideUtils
import kotlin.math.roundToInt

/**
 * @author: lwp
 * @date: 6/22/21
 * @description: banner 滑动渐变帮助类
 */
class BannerScrollGradientHelper constructor(private val mBannerCurrentBg: ImageView, private val mBannerWillShowBg: ImageView) {

    /**
     * 上次记录的偏移量百分比(0f~1f)取值
     */
    private var preOffsetPercent: Float = 0f

    /**
     * 上次加载最终位置图的index,用来去重同样值的回调
     */
    private var preLoadFinalImagePosition = -1

    /**
     * 真正的当前显示的图的ImageView控件(根据当前透明度计算,透明度为1的为当前图的ImageView)
     */
    private var currentImageView: ImageView? = null

    /**
     * 真正的将要显示的下张图的ImageView控件((根据最终停止滑动的最后一次回调的透明度计算,透明度为1的为下张显示图的ImageView)
     */
    private var willShowImageView: ImageView? = null

    /**
     * banner滚动方向 true是下一张,false是上一张
     */
    private var bannerWillScrollToNext = true

    /**
     * 判断banner的onScroll回调里是否设置了方向的值,用来去除重复赋值
     */
    private var isJudgeScrollAndSetScrollNextValue = false

    /**
     * 上次滚动中加载图的位置index
     */
    private var preLoadImagePositionOnScrolling = -1


    /**
     * @param context 上下文
     * @param position 位置
     * @param offsetPercent 滑动的偏移百分比(0~1之间)
     * @param offsetPx 滑动偏移的像素
     * @param imageUrlList 渐变的list URL
     * @param onScrollFinishListener 滚动完毕回调
     */
    fun startGradientByOffsetPercent(context: Context, position: Int,
                                     offsetPercent: Float,
                                     offsetPx: Int,
                                     imageUrlList: MutableList<String>,
                                     onScrollFinishListener: () -> Unit) {

        // 滑动的图的数组长度
        val imageUrlListSize = imageUrlList.size

        if (currentImageView == null) {
            currentImageView = getCurrentImageView()
        }
        if (willShowImageView == null) {
            willShowImageView = getWillShowImageView()
        }
        if (offsetPercent > 0f && preOffsetPercent > 0) {
            // 获取将要滑动的方向
            if (!isJudgeScrollAndSetScrollNextValue) {
                isJudgeScrollAndSetScrollNextValue = true
                bannerWillScrollToNext = offsetPercent - preOffsetPercent > 0
            }
            // 判断滑动方向
            if (bannerWillScrollToNext) {
                if (preLoadImagePositionOnScrolling != position) {
                    preLoadImagePositionOnScrolling = position
                    /**
                     * 当往后一张滑动时候,position的值是当前张的值
                     */
                    GlideUtils.loadImage(context, currentImageView, imageUrlList[position % imageUrlListSize])
                    // 下一个
                    GlideUtils.loadImage(context, willShowImageView, imageUrlList[(position + 1) % imageUrlListSize])
                }
                // 逐渐改变透明度,根据滑动偏移量比例
                // 当前page渐渐透明
                currentImageView?.alpha = 1 - offsetPercent
                // 下一张page渐渐显示
                willShowImageView?.alpha = offsetPercent
            } else {
                // preLoadImagePositionOnScrolling:去重,避免重复执行
                if (preLoadImagePositionOnScrolling != position) {
                    preLoadImagePositionOnScrolling = position
                    /**
                     * 当往前一张滑动时候,position的值是上一张的值
                     */
                    // 当前图
                    GlideUtils.loadImage(context, currentImageView, imageUrlList[(position + 1) % imageUrlListSize])
                    // 上一张
                    GlideUtils.loadImage(context, willShowImageView, imageUrlList[position % imageUrlListSize])
                }
                // 逐渐改变透明度,根据滑动偏移量比例
                currentImageView?.alpha = offsetPercent
                willShowImageView?.alpha = 1 - offsetPercent
            }
        }
        // 记录上次的滑动百分比,大于0表示开始滑动了
        preOffsetPercent = offsetPercent

        // offsetPercent和offsetPx 等于0代表最后一次滑动回调;preLoadFinalImagePosition去重,避免重复执行
        if (offsetPercent == 0f && offsetPx == 0 && position != preLoadFinalImagePosition) {
            preLoadFinalImagePosition = position
            // 最后一次滑动
            onTheLastScrollCallback(context, imageUrlList, position)
            // 滑动完成回调
            onScrollFinishListener?.invoke()
        }
    }

    /**
     * 最后的滑动回调
     */
    private fun onTheLastScrollCallback(context: Context, imageUrlList: MutableList<String>, position: Int) {
        // 展示最终的图片
        showFinalImage(context, imageUrlList, position)
    }

    /**
     * 展示最终的图片
     */
    private fun showFinalImage(context: Context, imageUrlList: MutableList<String>, position: Int) {
        // 设置最终的透明度,四舍五入,接近0的透明度为0,接近1的透明度为1,两个控件分别对应其中的一种情况,不会同时为0或1
        mBannerWillShowBg.alpha = mBannerWillShowBg.alpha.roundToInt() * 1f
        mBannerCurrentBg.alpha = mBannerCurrentBg.alpha.roundToInt() * 1f
        val willShowAlpha = mBannerWillShowBg.alpha
        // 展示alpha为1的控件,避免自动滑动的时候闪烁
        GlideUtils.loadImage(context, if (willShowAlpha == 1f) {
            mBannerWillShowBg
        } else {
            mBannerCurrentBg
        }, imageUrlList[position.let {
            if (it >= imageUrlList.size) {
                0
            } else {
                it
            }
        }])
        // 透明度为0的设置为空图资源
        if (willShowAlpha == 0f) {
            mBannerWillShowBg
        } else {
            mBannerCurrentBg
        }.setImageResource(0)

        // 重置一些记录变量
        resetRecordData()
    }

    /**
     * 重置记录的数据
     */
    private fun resetRecordData() {
        isJudgeScrollAndSetScrollNextValue = false
        preLoadImagePositionOnScrolling = -1

        currentImageView = null
        willShowImageView = null
    }

    /**
     * @return 将返回透明度alpha为1的控件
     */
    private fun getCurrentImageView(): ImageView {
        return if (mBannerCurrentBg.alpha == 1f) {
            mBannerCurrentBg
        } else {
            mBannerWillShowBg
        }
    }

    /**
     * @return 将返回透明度alpha为0的控件
     */
    private fun getWillShowImageView(): ImageView {
        return if (mBannerCurrentBg.alpha == 0f) {
            mBannerCurrentBg
        } else {
            mBannerWillShowBg
        }
    }


}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <html lang="en"> <head> <title>Nivo Slider Demo</title> <link rel="stylesheet" href="images/themes/default/default.css" type="text/css" media="screen" /> <link rel="stylesheet" href="images/themes/pascal/pascal.css" type="text/css" media="screen" /> <link rel="stylesheet" href="images/themes/orman/orman.css" type="text/css" media="screen" /> <link rel="stylesheet" href="css/nivo-slider.css" type="text/css" media="screen" /> <link rel="stylesheet" href="css/style.css" type="text/css" media="screen" /> </head> <body> <div id="wrapper"> <a href="http://dev7studios.com" id="dev7link" title="Go to dev7studios">dev7studios</a> <div class="slider-wrapper theme-default"> <div class="ribbon"></div> <div id="slider" class="nivoSlider"> <img src="images/toystory.jpg" alt="" /> <a href="http://dev7studios.com"><img src="images/up.jpg" alt="" title="This is an example of a caption" /></a> <img src="images/walle.jpg" alt="" /> <img src="images/nemo.jpg" alt="" title="#htmlcaption" /> </div> <div id="htmlcaption" class="nivo-html-caption"> <strong>This</strong> is an example of a HTML caption with <a href="#">a link</a>. </div> </div> </div> <script type="text/javascript" src="js/jquery-1.6.1.min.js"></script> <script type="text/javascript" src="js/jquery.nivo.slider.pack.js"></script> <script type="text/javascript"> $(window).load(function() { $('#slider').nivoSlider(); }); </script> </body> </html>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值