AndroidBanner - ViewPager

解决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.mzph.cn/news/18232.shtml

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

相关文章

P2404 自然数的拆分问题

题目 思路 最简单的dfs题之一 只需要一点点小优化 代码 #include<bits/stdc.h> using namespace std; const int maxn55; int n; int ans[maxn],s; void print(int tot) { for(int i1;i<tot-1;i) cout<<ans[i]<<""; cout<<ans[tot-1]&…

微信小程序,商城底部工具栏的实现

效果演示&#xff1a; 前提条件&#xff1a; 去阿里云矢量图标&#xff0c;下载8个图标&#xff0c;四个黑&#xff0c;四个红&#xff0c;如图&#xff1a; 新建文件夹icons&#xff0c;把图标放到该文件夹&#xff0c;然后把该文件夹移动到该项目的文件夹里面。如图所示 app…

jmeter之接口测试(http接口测试)

基础知识储备 一、了解jmeter接口测试请求接口的原理 客户端--发送一个请求动作--服务器响应--返回客户端 客户端--发送一个请求动作--jmeter代理服务器---服务器--jmeter代理服务器--服务器 二、了解基础接口知识&#xff1a; 1、什么是接口&#xff1a;前端与后台之间的…

动手学深度学习(一)预备知识

目录 一、数据操作 1. N维数组样例 2. 访问元素 3. 基础函数 &#xff08;1&#xff09; 创建一个行向量 &#xff08;2&#xff09;通过张量的shape属性来访问张量的形状和元素总数 &#xff08;3&#xff09;reshape()函数 &#xff08;4&#xff09;创建全0、全1、…

CorelDraw怎么做立体字效果?CorelDraw制作漂亮的3d立体字教程

1、打开软件CorelDRAW 2019&#xff0c;用文本工具写上我们所需要的大标题。建议字体选用比较粗的适合做标题的字体。 2、给字填充颜色&#xff0c;此时填充的颜色就是以后立体字正面的颜色。我填充了红色&#xff0c;并加上了灰色的描边。 3、选中文本&#xff0c;单击界面左侧…

java 企业工程管理系统软件源码+Spring Cloud + Spring Boot +二次开发+ MybatisPlus + Redis

&#xfeff; Java版工程项目管理系统 Spring CloudSpring BootMybatisVueElementUI前后端分离 功能清单如下&#xff1a; 首页 工作台&#xff1a;待办工作、消息通知、预警信息&#xff0c;点击可进入相应的列表 项目进度图表&#xff1a;选择&#xff08;总体或单个&am…

Metric3D:Towards Zero-shot Metric 3D Prediction from A Single Image

参考代码&#xff1a;Metric3D 介绍 在如MiDas、LeReS这些文章中对于来源不同的深度数据集使用归一化深度作为学习目标&#xff0c;则在网络学习的过程中就天然失去了对真实深度和物体尺寸的度量能力。而这篇文章比较明确地指出了影响深度估计尺度变化大的因素就是焦距 f f f…

TypeC拓展设计方案|TypeC转HDMI设计方案|CS5261/CS5265芯片设计参数对比

集睿智远CS5261/CS5265都可以用于设计TypeC转HDMI方案&#xff0c;低成本TypeC扩展坞设计方案&#xff0c;而两者也有些差异&#xff1a;1.CS5261支持DP1.4输入&#xff0c;一个HDMI1.4输出&#xff0c;即HDMI输出为4K30HZ ;CS5265DP1.4到HDMI2.0转换芯片&#xff0c;即HDMI输出…

ansible安装lnmp(集中式)

文章目录 一、安装nginx二、安装mysql三、安装php测试&#xff1a; 一、安装nginx - name: the nginx playhosts: webserversremote_user: roottasks:- name: stop firewalld #关闭防火墙service: namefirewalld statestopped enabledno- name: selinux stopc…

Python元编程-装饰器介绍、使用

目录 一、Python元编程装饰器介绍 二、装饰器使用 1. 实现认证和授权功能 2.实现缓存功能 3.实现日志输出功能 三、附录 1. logging.basicConfig介绍 2. 精确到毫秒&#xff0c;打印时间 方法一&#xff1a;使用datetime 方法二&#xff1a;使用time 一、Python元编程…

C# 根据图片的EXIF自动调整图片方向

PropertyItems 代码 /// <summary>/// 根据图片exif调整方向/// </summary>/// <param name"img"></param>public void RotateImage(Bitmap img){var exif img.PropertyItems;byte orien 0;var item exif.Where(m > m.Id 274).ToArra…

关于综合能源智慧管理系统的架构及模式规划的研究

安科瑞 华楠 摘 要&#xff1a;探讨了国内外能源互联网的研究发展&#xff0c;分析了有关综合智慧能源管理系统的定位&#xff0c;以及系统的主要特点&#xff0c;研究了综合智慧能源管理系统的构架以及模式规划。 关键词&#xff1a;综合能源&#xff1b;智慧管理系统&#…

前端开发:基于cypress的自动化实践

如何在vue中使用cypress如何运行cypress如何编写测试用例如何解决测试数据的问题遇到的元素定位的问题如何看待cypresscypress是否为最佳工具测试怎么办&#xff1f; 如何在vue中使用cypress vue提供了vue-cli 可以快速的创建vue项目。 vue create hello-world在选择安装项里…

【李宏毅机器学习·学习笔记】Deep Learning General Guidance

本节课可视为机器学习系列课程的一个前期攻略&#xff0c;这节课主要对Machine Learning 的框架进行了简单的介绍&#xff1b;并以training data上的loss大小为切入点&#xff0c;介绍了几种常见的在模型训练的过程中容易出现的情况。 课程视频&#xff1a; Youtube&#xff1…

Java并发系列之二:悲观锁机制

什么是锁 在并发环境下&#xff0c;会出现多个线程对同一个资源进行争抢的情况&#xff0c;假设A线程对资源正在进行修改&#xff0c;此时B线程此时又对资源进行了修改&#xff0c;这就可能会导致数据不一致的问题。为了解决这个问题&#xff0c;很多编程语言引入了锁机制&…

前端学习--vue2--插槽

写在前面&#xff1a; 这个用法是在使用组件和创建组件中 文章目录 介绍简单使用多个插槽省写默认/后备内容作用域插槽常用实例Element-ui的el-table 废弃用法slot attributeslot-scope attribute 介绍 我们在定义一些组件的时候&#xff0c;由于组件内文字想要自定义&#…

ssh安全远程管理

目录 1、什么是ssh 2、ssh登陆 3、ssh文件传输 1、什么是ssh ssh是 Secure Shell 的缩写&#xff0c;是一个建立在应用层上的安全远程管理协议。ssh 是目前较为可靠的传输协议&#xff0c;专为远程登录会话和其他网络服务提供安全性。利用ssh 协议可以有效防止远程管理过程中…

机器学习笔记之优化算法(二)线搜索方法(方向角度)

机器学习笔记之优化算法——线搜索方法[方向角度] 引言回顾&#xff1a;线搜索方法从方向角度观察线搜索方法场景构建假设1&#xff1a;目标函数结果的单调性假设2&#xff1a;屏蔽步长 α k \alpha_k αk​对线搜索方法过程的影响假设3&#xff1a;限定向量 P k \mathcal P_k …

0基础学习VR全景平台篇 第76篇:全景相机-圆周率全景相机如何直播推流

圆周率科技&#xff0c;成立于2012年&#xff0c;是中国最早投身嵌入式全景算法研发的团队之一&#xff0c;亦是全球市场占有率最大的全景算法供应商。相继推出一体化智能屏、支持一键高清全景直播的智慧全景相机--Pilot Era和Pilot One&#xff0c;为用户带来实时畅享8K的高清…

PyTorch代码实战入门

人这辈子千万不要马虎两件事 一是找对爱人、二是选对事业 因为太阳升起时要投身事业 太阳落山时要与爱人相拥 一、准备数据集 蚂蚁蜜蜂数据集 蚂蚁蜜蜂的图片&#xff0c;文件名就是数据的label 二、使用Dataset加载数据 打开pycharm&#xff0c;选择Anaconda创建的pytorch环…