当前位置: 首页 > news >正文

纵向循环缓慢滚动图片

工具类:

class SrcLoopScrollFrameLayout : FrameLayout {companion object {/*** 滚动方向*/@IntDef(OUT_SLIDE_TOP, OUT_SLIDE_BOTTOM, OUT_SLIDE_LEFT, OUT_SLIDE_RIGHT)@Retention(AnnotationRetention.SOURCE)annotation class ScrollOrientation/*** 滚动方向常量* 0:往上滚出* 1:往下滚出* 2:往左滚出* 3:往右滚出*/const val OUT_SLIDE_TOP = 0const val OUT_SLIDE_BOTTOM = 1const val OUT_SLIDE_LEFT = 2const val OUT_SLIDE_RIGHT = 3/*** 重绘间隔时间*/private const val DEFAULT_DRAW_INTERVALS_TIME = 5L}/*** 间隔时间内平移距离*/private var mPanDistance = 0f/*** 间隔时间内平移增距*/private var mIntervalIncreaseDistance = 0.5f/*** 填满当前view所需bitmap个数*/private var mBitmapCount = 0/*** 是否开始滚动*/private var mIsScroll = false/*** 滚动方向,默认往上滚出*/@ScrollOrientationprivate var mScrollOrientation = OUT_SLIDE_TOP/*** 遮罩层颜色*/@ColorIntprivate var mMaskLayerColor = 0private var mDrawable: Drawable? = nullprivate var mSrcBitmap: Bitmap? = nullprivate lateinit var mPaint: Paintprivate lateinit var mMatrix: Matrixconstructor(context: Context) : super(context) {initView(context, null, 0)}constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {initView(context, attrs, 0)}constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context,attrs,defStyleAttr) {initView(context, attrs, defStyleAttr)}private fun initView(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {val array = context.theme.obtainStyledAttributes(attrs,R.styleable.SrcLoopScrollFrameLayout,defStyleAttr,0)val speed = array.getInteger(R.styleable.SrcLoopScrollFrameLayout_speed, 3)mScrollOrientation =array.getInteger(R.styleable.SrcLoopScrollFrameLayout_scrollOrientation, OUT_SLIDE_TOP)mIntervalIncreaseDistance *= speedmDrawable = array.getDrawable(R.styleable.SrcLoopScrollFrameLayout_src)mIsScroll = array.getBoolean(R.styleable.SrcLoopScrollFrameLayout_isScroll, true)mMaskLayerColor =array.getColor(R.styleable.SrcLoopScrollFrameLayout_maskLayerColor, Color.TRANSPARENT)array.recycle()setWillNotDraw(false)mPaint = Paint()mMatrix = Matrix()}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)if (mDrawable == null || mDrawable !is BitmapDrawable) {return}if (visibility == GONE) {return}if (w == 0 || h == 0) {return}if (mSrcBitmap == null) {val bitmap = (mDrawable as BitmapDrawable).bitmap//调整色彩模式进行质量压缩val compressBitmap = bitmap.copy(Bitmap.Config.RGB_565, true)//缩放 BitmapmSrcBitmap = scaleBitmap(compressBitmap)//计算至少需要几个 bitmap 才能填满当前 viewwhen (mScrollOrientation) {OUT_SLIDE_TOP, OUT_SLIDE_BOTTOM -> {mBitmapCount = measuredHeight / mSrcBitmap!!.height + 1}OUT_SLIDE_LEFT, OUT_SLIDE_RIGHT -> {mBitmapCount = measuredWidth / mSrcBitmap!!.width + 1}}if (!compressBitmap.isRecycled) {compressBitmap.isRecycledSystem.gc()}}}override fun onDraw(canvas: Canvas) {super.onDraw(canvas)if (mSrcBitmap == null) {return}val length = if (scrollOrientationIsVertical()) mSrcBitmap!!.height else mSrcBitmap!!.widthif (length + mPanDistance != 0f) {//第一张图片未完全滚出屏幕mMatrix.reset()when (mScrollOrientation) {OUT_SLIDE_TOP -> {mMatrix.postTranslate(0f, mPanDistance)}OUT_SLIDE_BOTTOM -> {mMatrix.postTranslate(0f, measuredHeight - length - mPanDistance)}OUT_SLIDE_LEFT -> {mMatrix.postTranslate(mPanDistance, 0f)}OUT_SLIDE_RIGHT -> {mMatrix.postTranslate(measuredWidth - length - mPanDistance, 0f)}}canvas.drawBitmap(mSrcBitmap!!, mMatrix, mPaint)}if (length + mPanDistance < (if (scrollOrientationIsVertical()) measuredHeight else measuredWidth)) {//用于补充留白的图片出现在屏幕for (i in 0 until mBitmapCount) {mMatrix.reset()when (mScrollOrientation) {OUT_SLIDE_TOP -> {mMatrix.postTranslate(0f, (i + 1) * length + mPanDistance)}OUT_SLIDE_BOTTOM -> {mMatrix.postTranslate(0f, measuredHeight - (i + 2) * length - mPanDistance)}OUT_SLIDE_LEFT -> {mMatrix.postTranslate((i + 1) * length + mPanDistance, 0f)}OUT_SLIDE_RIGHT -> {mMatrix.postTranslate(measuredWidth - (i + 2) * length - mPanDistance, 0f)}}canvas.drawBitmap(mSrcBitmap!!, mMatrix, mPaint)}}//绘制遮罩层if (mMaskLayerColor != Color.TRANSPARENT) {canvas.drawColor(mMaskLayerColor)}//延时重绘实现滚动效果if (mIsScroll) {handler.postDelayed(mRedrawRunnable, DEFAULT_DRAW_INTERVALS_TIME)}}/*** 重绘*/private val mRedrawRunnable = Runnable {val length = if (scrollOrientationIsVertical()) mSrcBitmap!!.height else mSrcBitmap!!.widthif (length + mPanDistance <= 0) {//第一张已完全滚出屏幕,重置平移距离mPanDistance = 0f}mPanDistance -= mIntervalIncreaseDistanceinvalidate()}/*** 设置背景图 bitmap* 通过该方法设置的背景图,当 屏幕翻转/暗黑模式切换 等涉及到 activity 重构的情况出现时,需要在 activity 重构后重新设置背景图* @srcBitmap 背景图* @isMassReduce 是否进行质量压缩(通过调整色彩模式实现,默认进行压缩)*/fun setSrcBitmap(srcBitmap: Bitmap, isMassReduce: Boolean = true) {val oldScrollStatus = mIsScrollif (oldScrollStatus) {stopScroll()}val compressBitmap: Bitmap =if (isMassReduce && srcBitmap.config != Bitmap.Config.RGB_565) {//调整色彩模式进行质量压缩srcBitmap.copy(Bitmap.Config.RGB_565, true)} else {srcBitmap}//按当前View宽度比例缩放 BitmapmSrcBitmap = scaleBitmap(compressBitmap)//计算至少需要几个 bitmap 才能填满当前 viewmBitmapCount =if (scrollOrientationIsVertical()) measuredHeight / mSrcBitmap!!.height + 1 else measuredWidth / mSrcBitmap!!.width + 1if (!srcBitmap.isRecycled) {srcBitmap.isRecycledSystem.gc()}if (!compressBitmap.isRecycled) {compressBitmap.isRecycledSystem.gc()}if (oldScrollStatus) {startScroll()}}/*** 开始滚动*/fun startScroll() {if (mSrcBitmap != null && mIsScroll) {return}mIsScroll = truehandler.postDelayed(mRedrawRunnable, DEFAULT_DRAW_INTERVALS_TIME)}/*** 停止滚动*/fun stopScroll() {if (!mIsScroll) {return}mIsScroll = falsehandler.removeCallbacks(mRedrawRunnable)}/*** 设置滚动方向*/fun setScrollOrientation(@ScrollOrientation scrollOrientation: Int) {mPanDistance = 0fmScrollOrientation = scrollOrientationif (mSrcBitmap != null) {if (mDrawable != null && mDrawable is BitmapDrawable) {val bitmap = (mDrawable as BitmapDrawable).bitmapif (!bitmap.isRecycled) {setSrcBitmap(bitmap)return}}setSrcBitmap(mSrcBitmap!!)}}/*** 切换滚动方向*/fun changeScrollOrientation() {mPanDistance = 0fif (mScrollOrientation == OUT_SLIDE_RIGHT) {mScrollOrientation = OUT_SLIDE_TOP} else {mScrollOrientation++}if (mSrcBitmap != null) {if (mDrawable != null && mDrawable is BitmapDrawable) {val bitmap = (mDrawable as BitmapDrawable).bitmapif (!bitmap.isRecycled) {setSrcBitmap(bitmap)return}}setSrcBitmap(mSrcBitmap!!)}}/*** 判断是否为竖直滚动*/private fun scrollOrientationIsVertical(): Boolean {return mScrollOrientation == OUT_SLIDE_TOP || mScrollOrientation == OUT_SLIDE_BOTTOM}/*** 缩放 Bitmap*/private fun scaleBitmap(originBitmap: Bitmap): Bitmap? {val width = originBitmap.widthval height = originBitmap.heightval newHeight: Intval newWidth: Intif (scrollOrientationIsVertical()) {newWidth = measuredWidthnewHeight = newWidth * height / width} else {newHeight = measuredHeightnewWidth = newHeight * width / height}return Bitmap.createScaledBitmap(originBitmap, newWidth, newHeight, true)}
}

styles.xml

    <declare-styleable name="SrcLoopScrollFrameLayout"><attr name="src" /><attr name="maskLayerColor" /><attr name="isScroll" /><attr name="speed" /><attr name="scrollOrientation" /></declare-styleable>

使用:

<SrcLoopScrollFrameLayout 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="match_parent"android:layout_height="match_parent"android:background="@color/white"app:layout_constraintTop_toTopOf="parent"app:scrollOrientation="toTop"app:src="@drawable/img_gui_bg">//正常布局代码块</SrcLoopScrollFrameLayout>


文章转载自:

http://pPd6pq5o.spsqr.cn
http://MfEOPgFU.spsqr.cn
http://FdHtlAnH.spsqr.cn
http://DxB254Ae.spsqr.cn
http://uo0lIHAS.spsqr.cn
http://0rEk8BWJ.spsqr.cn
http://urpjE7Td.spsqr.cn
http://t3f6i3zR.spsqr.cn
http://iC3RusjX.spsqr.cn
http://LpHzKjFU.spsqr.cn
http://om5qY5vj.spsqr.cn
http://2AregGFJ.spsqr.cn
http://aP26AmCE.spsqr.cn
http://f1J0Cp5q.spsqr.cn
http://CKj3UArX.spsqr.cn
http://NxJ2puEG.spsqr.cn
http://8QU8RabQ.spsqr.cn
http://FFqKn2qN.spsqr.cn
http://NZ2nzlRe.spsqr.cn
http://4XnM9F1G.spsqr.cn
http://bNbc5kFd.spsqr.cn
http://AkmFslLH.spsqr.cn
http://xiaYULST.spsqr.cn
http://f9lBRsI0.spsqr.cn
http://SmO3YNQz.spsqr.cn
http://IRMAD1BF.spsqr.cn
http://qOggluXR.spsqr.cn
http://VLAPKcGa.spsqr.cn
http://h4280bHY.spsqr.cn
http://6soGIftN.spsqr.cn
http://www.dtcms.com/a/374084.html

相关文章:

  • 项目日记 -日志系统 -明确目标、规划模块并完成项目文档
  • 【C++上岸】C++常见面试题目--网络篇(第二十二期)
  • 数据治理系列(一):数据治理的整体框架与发展趋势
  • 【LeetCode 每日一题】1504. 统计全 1 子矩形
  • FastGPT源码解析 Agent知识库文本资料处理详解和代码分析
  • php 实现 导入excel 带图片导入
  • JP4-7-MyLesson后台前端(五)
  • 【系统分析师】第17章-关键技术:嵌入式系统分析与设计(核心总结)
  • Centos9安装rocketmq
  • Docker | 一种使用 docker-compose 命令将 YAML 定义的配置文件导入到 Docker 的方法
  • 编译器构造:模拟器,汇编与反汇编
  • 自由学习记录(96)
  • Cy5-Tyramide, Cyanine 5 Tyramide;1431148-26-3
  • JMeter接口测试全流程解析
  • ARM处理器的小常识
  • Go语言极速入门与精要指南从零到精通的系统化学习路径
  • RK3576 android14 usb_audio_policy_configuration.xml解析
  • 本地安装部署svn服务,并设置外网远程访问内网svn,含路由器转发和端口映射工具教程
  • idea2025构建springboot项目能运行的样例
  • 【底层机制】std::unordered_map 扩容机制
  • Cpp::STL—位图bitset的使用与模拟实现(39)
  • 链表 (C/C++)
  • WinEdt编译tex文件失败解决办法
  • C语言第12讲
  • commons-email
  • (堆)347. 前 K 个高频元素
  • GitHub Release Monitor部署指南:实时追踪开源项目更新与自动通知
  • 重新定义音频编程:SoundFlow如何以模块化设计革新.NET音频开发生态
  • SQL 注入与防御-第八章:代码层防御
  • Miniflux 安全升级:绑定域名并开启 HTTPS