AndroidBanner - ViewPager

news2024/11/25 10:28:40

解决banner 不可见依旧轮播的问题

思考一下:什么时候可以轮播,什么时候不可以轮播

 

当Banner添加到屏幕上,且对用户可见的时候,可以开始轮播
当Banner从屏幕上移除,或者Banner不可见的时候,可以停止轮播
当手指触摸到Banner时,停止轮播
当手指移开时,开始轮播

所以,我们需要知道什么时候View可见,不可见,添加到屏幕上和从屏幕上移除,幸运的是,这些,android都提供了对应的接口来获取。

OnAttachStateChangeListenner

该接口可以通知我们view添加到屏幕上或者从屏幕上被移除,或者可以直接重写view的onAttachedToWindow和onDetachedFromWindow方法

// view提供的接口,可以通过 addOnAttachStateChangeListener 添加舰艇
public interface OnAttachStateChangeListener {  
	public void onViewAttachedToWindow(@NonNull View v);  
	public void onViewDetachedFromWindow(@NonNull View v);  
}

// 复写view的方法
override fun onAttachedToWindow() {  
    super.onAttachedToWindow()  
}  
  
override fun onDetachedFromWindow() {  
    super.onDetachedFromWindow()  
}

这里我们通过复写方法的方式处理

onVisibilityChanged

view 提供了方法,可以复写该方法,获取到view 的可见性变化

protected void onVisibilityChanged(@NonNull View changedView, @Visibility int visibility) {  
}

onWindowVisibilityChanged

view 提供了方法,可以复习该方法,当前widow的可见性发生变化的时候,会调用通知给我们

protected void onWindowVisibilityChanged(@Visibility int visibility) {  
    if (visibility == VISIBLE) {  
        initialAwakenScrollBars();  
    }  
}

我们根据上面的api,可以封装一个接口,来监听View的可见性

VisibleChangeListener

interface VisibleChangeListener {  
    /**  
     * view 可见  
     */  
    fun onShown()  
  
    /**  
     * view 不可见  
     */  
    fun onDismiss()  
}

Banner重写方法,进行调用

override fun onVisibilityChanged(changedView: View, visibility: Int) {  
    Log.e(TAG, "onVisibilityChanged ${changedView == this}, vis: $visibility")  
    dispatchVisible(visibility)  
}  
  
override fun onWindowVisibilityChanged(visibility: Int) {  
    super.onWindowVisibilityChanged(visibility)  
    Log.e(TAG, "onWindowVisibilityChanged $visibility")  
    dispatchVisible(visibility)  
}  
  
override fun onAttachedToWindow() {  
    super.onAttachedToWindow()  
    Log.e(TAG, "onAttachedToWindow ")  
    this.mAttached = true  
}  
  
override fun onDetachedFromWindow() {  
    Log.e(TAG, "onDetachedFromWindow ")  
    super.onDetachedFromWindow()  
    this.mAttached = false  
}

private fun dispatchVisible(visibility: Int) {  
    val visible = mAttached && visibility == VISIBLE  
    if (visible) {  
        prepareLoop()  
    } else {  
        stopLoop()  
    }  
    mVisibleChangeListener?.let {  
        when (visible) {  
            true -> it.onShown()  
            else -> it.onDismiss()  
        }  
    }  
}

页面滚动时处理banner轮播

滚动监听,如果是scrollview,就监听滚动事件处理即可。如果是listview,recyclerview可以选择监听onscrollstatechanged,更高效。
下面是scrollview的监听处理

mBinding.scrollView.setOnScrollChangeListener(object :OnScrollChangeListener{  
    override fun onScrollChange(  
        v: View?,  
        scrollX: Int,  
        scrollY: Int,  
        oldScrollX: Int,  
        oldScrollY: Int  
    ) {  
        val visible = mBinding.vpBanner.getGlobalVisibleRect(Rect())  
        Log.e(TAG,"banner visible : $visible")  
        if(visible){  
            mBinding.vpBanner.startLoop()  
        }else{  
            mBinding.vpBanner.stopLoop()  
        }  
    }  
})

点击事件的处理

首先要声明一个点击事件回调接口

interface PageClickListener {  
    fun onPageClicked(position: Int)  
}

重写banner的onTouch事件,将移动距离小于100,且按压时间小于500ms的事件认为是点击事件

private var mMoved = false  
private var mDownX = 0F  
private var mDownY = 0F  
  
/**  
 * 当前事件流结束时,恢复touch处理的相关变量  
 */  
private fun initTouch() {  
    this.mMoved = false  
    this.mDownX = 0F  
    this.mDownY = 0F  
}  
  
private fun calculateMoved(x: Float, y: Float, ev: MotionEvent) {  
    mClickListener?.let {  
        // 超过500ms(系统默认的时间) 我们认为不是点击事件  
        if (ev.eventTime - ev.downTime >= 500) {  
            return  
        }  
        // 移动小于阈值我们认为是点击  
        if (sqrt(((x - mDownX).pow(2) + (y - mDownY).pow(2))) >= MOVE_FLAG) {  
            return  
        }  
        val count = adapter?.count ?: 0  
        if (count == 0) {  
            return  
        }  
        // 由于我们实现无限轮播的方式是重新设置当前选中的item,这里要将currentItem重新映射回去  
        val index = when (currentItem) {  
            in 1..count - 2 -> currentItem - 1  
            0 -> count - 1  
            else -> 0  
        }  
        it.onPageClicked(index)  
    }  
}  
  
override fun onTouchEvent(ev: MotionEvent?): Boolean {  
    when (ev?.action) {  
        MotionEvent.ACTION_DOWN -> {  
            this.mDownY = ev.y  
            this.mDownX = ev.x  
            stopLoop()  
        }  
        MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {  
            val y = ev.y  
            val x = ev.x  
            calculateMoved(x, y, ev)  
            initTouch()  
            prepareLoop()  
        }  
    }  
    return super.onTouchEvent(ev)  
}

曝光打点的处理

监听page切换,当page变化的时候,从实际展示的数据队列中取出数据进行曝光。

class ExposureHelper(private val list: List<*>, private var last: Int = -1) :  
    ViewPager.OnPageChangeListener {  
  
    private var mStart: AtomicBoolean = AtomicBoolean(false);  
  
    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) =  
        Unit  
  
    override fun onPageSelected(position: Int) {  
        Log.e(TAG, "$position $last")  
        if (last >= 0) {  
            exposure()  
        }  
        last = position  
    }  
  
    override fun onPageScrollStateChanged(state: Int) = Unit  
  
    /**  
     * 开始曝光  
     * @param current Int  
     */    fun startExposure(current: Int) {  
        mStart.set(true)  
        last = current  
    }  
  
    /**  
     * 停止曝光  
     */  
    fun endExposure() {  
        if (mStart.get()) {  
            mStart.set(false)  
            exposure()  
        }  
    }  
  
    /**  
     * 实际执行数据上报的处理  
     */  
    private fun exposure() {  
        val data = list[last]  
        Log.e(TAG, "data:$data")  
    }  
  
    companion object {  
        private const val TAG = "ExposureHelper"  
    }  
}

VPAdapter 对外提供实际展示的数据集

private val mData = mutableListOf<T>()  
  
fun setData(data: List<T>) {  
    mData.clear()  
    if (this.loop && data.size > 1) {  
        // 数组组织一下,用来实现无限轮播  
        mData.add(data[data.size - 1])  
        mData.addAll(data)  
        mData.add(data[0])  
    } else {  
        mData.addAll(data)  
    }  
}

fun getShowDataList():List<T>{  
    return mData  
}

在Banner中的配置使用 

private var mExposureHelper: ExposureHelper? = null

/**  
 * 自动轮播  
 */  
fun startLoop() {  
    if (mLoopHandler == null) {  
        mLoopHandler = Handler(Looper.getMainLooper()) { message ->  
            return@Handler when (message.what) {  
                LOOP_NEXT -> {  
                    loopNext()  
                    true  
                }  
                else -> false  
            }  
        }  
    }  
    if (mLoopHandler?.hasMessages(LOOP_NEXT) != true) {  
        Log.e(TAG, "startLoop")  
        mLoopHandler?.sendEmptyMessageDelayed(LOOP_NEXT, mLoopDuration)  
    }  
    // 开始轮播时开始曝光(可见时会触发轮播)  
    mExposureHelper?.startExposure(currentItem)  
}  
  
fun stopLoop() {  
    // 停止轮播时结束曝光(不可见时会停止轮播)  
    mExposureHelper?.endExposure()  
    mLoopHandler?.removeMessages(LOOP_NEXT)  
}

fun bindExposureHelper(exposureHelper: ExposureHelper?) {  
    mExposureHelper = exposureHelper  
    mExposureHelper?.let {  
        addOnPageChangeListener(it)  
    }  
    mExposureHelper?.startExposure(currentItem)  
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/824466.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

排序进行曲-v2.0

小程一言 这篇文章是在排序进行曲1.0之后的续讲&#xff0c; 0之后的续讲,英语在上一篇讲的排序的基本概念与分类0之后的续讲, 英语在上一篇讲的排序的基本概念与分类这片主要是对0之后的续讲,英语在上一篇讲的排序的基本概念与分类这 篇主要是对几个简单的排序进行细致的分析…

Mr. Cappuccino的第55杯咖啡——Mybatis一级缓存二级缓存

Mybatis一级缓存&二级缓存 概述一级缓存特点演示前准备效果演示在同一个SqlSession中在不同的SqlSession中 源代码怎么禁止使用一级缓存一级缓存在什么情况下会被清除 二级缓存特点演示前准备效果演示在不同的SqlSession中 源代码怎么关闭二级缓存 一级缓存&#xff08;Spr…

【css】css调整文本间距

text-indent 属性用于指定文本第一行的缩进letter-spacing 属性用于指定文本中字符之间的间距line-height 属性用于指定行之间的间距&#xff1a;word-spacing 属性用于指定文本中单词之间的间距&#xff0c;这个和letter-spacing类似white-space 属性指定元素内部空白的处理方…

Java面向对象之方法的使用

文章目录 一、什么是方法二、方法的声明格式三、方法的分类四、方法的调用五、方法的注意点六、方法的重载七、可变形参的方法八、方法参数的值传递机制九、递归方法 一、什么是方法 方法(method)是类或对象行为特征的抽象&#xff0c;用来完成某个功能操作。在某些语言中也称…

JavaScript将CSV文件转为JSON

说在前面 相信平时大家或多或少都会有遇到过类似的情况&#xff1a; &#x1f64e;&#xff08;需求小姐姐&#xff09; &#x1f64e;‍♂&#xff08;我&#xff09; &#x1f64e;&#xff1a;这里我们需要做一个省市联动下拉框&#xff0c;你看看可以做吗&#xff1f; &am…

基于SSM 应急指挥平台-计算机毕设 附源码 13263

SSM应急指挥平台 摘 要 科技进步的飞速发展引起人们日常生活的巨大变化&#xff0c;电子信息技术的飞速发展使得电子信息技术的各个领域的应用水平得到普及和应用。信息时代的到来已成为不可阻挡的时尚潮流&#xff0c;人类发展的历史正进入一个新时代。在现实运用中&#xff0…

DFS之连通性--迷宫

思路&#xff1a;就是通过dfs进行搜索&#xff0c;看是否能从给定的起点到给定的终点&#xff0c;当然也可以用bfs写&#xff0c;这里用到dfs写的 #include<bits/stdc.h> using namespace std; int n; char g[1010][1010]; int ax,ay,bx,by; int dx[4]{1,0,-1,0}; int dy…

pgSql 报错:timestamp without time zone >= character varying

在这里补充一点&#xff0c;数据库映射实体类不要使用LocalDateTime,驱动版本大概率不支持&#xff0c;出现奇奇怪怪的错&#xff1b; 在mysql 使用时&#xff0c;String 类型会隐式转成Date类型&#xff0c;使用mybatis-plus拼接就不会报错&#xff0c;换成pgSql就出现这个错…

模板方法模式:优化代码复用与扩展性的设计模式

模板方法模式&#xff1a;优化代码复用与扩展性的设计模式 什么是模板方法模式&#xff1f; 模板方法模式是一种行为型设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;将一些步骤的具体实现延迟到子类中。模板方法模式通过将算法的通用部分抽象出来&#xff0c;以模板…

华为OD机试真题 Java 实现【最小传输时延Ⅱ】【2023 B卷 200分】,附详细解题思路

目录 专栏导读一、题目描述二、输入描述三、输出描述四、解题思路五、Java算法源码六、效果展示1、输入2、输出3、说明 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试&#xff08;JAVA&#xff09;真题&#xff08;A卷B卷&#…

计算一组数据的方差statistics.pvariance()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算一组数据的方差 statistics.pvariance() 选择题 下列说法错误的是? import statistics data [1, 2] print("【显示】data ", data) print(【执行】statistics.pvariance(data…

个人中心 - 实现修改用户头像、用户名或密码

目录 1. 修改用户头像 1.1 获取原来的用户头像和用户名 1.2 实现保存头像 2. 修改用户名或密码 1. 修改用户头像 本文是针对之前的一篇项目博客 - 博客系统 做的一个扩展功能. 1.1 获取原来的用户头像和用户名 想要修改头像, 那么就得先获取数据库中原来的头像, 此处顺便…

从零开始 Spring Cloud 8:Docker

从零开始 Spring Cloud 8&#xff1a;Docker 图源&#xff1a;laiketui.com Docker 可以帮助我们更方便地部署 Spring Cloud 应用。 环境准备 准备 Docker 环境可以参考 这篇文章。 操作镜像 docker 的镜像相关操作主要涉及以下命令&#xff1a; docker pull&#xff0c;…

什么是服务网格?

背景&#xff1a; 服务网格这个概念出来很久了&#xff0c;从 2017 年被提出来&#xff0c;到 2018 年正式爆发&#xff0c;很多云厂商和互联网企业都在纷纷向服务网格靠拢。像蚂蚁集团、美团、百度、网易等一线互联 网公司&#xff0c;都有服务网格的落地应用。服务网格是微服…

开源-基于ch9374b的KVM设计

文章目录 简介功能特性设计图实现功能开源链接 简介 平时总有一种需求&#xff0c;就是我在调试树莓派的时候&#xff0c;经常要在pc电脑和开发板之间来回操作&#xff0c;因此就需要两套键盘和鼠标&#xff0c;但是我的桌子实在是太小了&#xff0c;两套键鼠不能并排放置&…

浅谈Struts2请求解析过程

0x00前言 在使用Struts2的时候需要在web.xml中配置一个过滤器&#xff0c;来拦截用户发起的请求&#xff0c;并进行一些预处理&#xff0c;根据配置文件把请求分配给对应的action并将请求中的参数与action中的字段进行对应赋值。例如下面的例子&#xff0c;通过配置StrutsPrepa…

二叉树的前,中,后序的非递归实现(c++)

前言 对于二叉树来说&#xff0c;遍历它有多种方式&#xff0c;其中递归遍历是比较简单的&#xff0c;但是非递归的实现就有一定的难度&#xff0c;在这里介绍一种非递归实现二叉树遍历的方式。 1.前序遍历 1.1思路 其实对于二叉树的非递归实现&#xff0c;实际上就是用代码来…

Spring中Bean的实例化详细流程

还是举个例子&#xff0c;我有一个朋友小汪他远赴南方某城市打工。然后安定下来后他的朋友很想来家里玩&#xff0c;但是呢我这个朋友家里搞的很乱&#xff0c;所以他不好意思请朋友来家里玩。这时我的另一个朋友说那请一个保姆把家里好好整理一下就可以了&#xff0c;然后给他…

【LeetCode】一、链表反转

题目 题目&#xff1a;给定单链表头节点&#xff0c;将单链表的链接顺序反转过来 例&#xff1a; 输入&#xff1a;1->2->3->4->5 输出&#xff1a;5->4->3->2->1 要求&#xff1a;按照两种方式实现 解决办法 方式一&#xff1a;&#xff08;直接迭…

腾讯云服务器CVM镜像操作系统大全_win_linux

腾讯云CVM服务器的公共镜像是由腾讯云官方提供的镜像&#xff0c;公共镜像包含基础操作系统和腾讯云提供的初始化组件&#xff0c;公共镜像分为Windows和Linux两大类操作系统&#xff0c;如TencentOS Server、Windows Server、OpenCloudOS、CentOS Stream、CentOS、Ubuntu、Deb…