欢迎光临散文网 会员登陆 & 注册

Android实现太极图、跑马灯(自定义View)

2023-07-30 23:57 作者:老黑535  | 我要投稿

ANDROID 自定义 VIEW

虽然常用的 view 能够满足大部分需求,但是通过自定义 view 通常能够更加方便的达到相应目的。比如通过自定义 TextView 来实现一些文本动画,只需要重写相应的部分代码即可达到预期,比通过各种 view 组合实现更加方便。本文章将通过自定义 TextView 实现一个文本动画效果,以及实现旋转太极动画进行自定义 View 介绍。

实现自定义 View 的方式

  • 继承 View

  • 继承已实现的 View ,如:TextView、ImageView等

旋转太极

通过旋转太极图的案例介绍 SurfaceView 的使用以及好处。

SurfaceView与View的区别在于:

  • View为主动更新视图;SurfaceView为被动更新视图。

  • View刷新在主线程中;SurfaceView则开启子线程进行刷新。

  • View在绘图时没有实现双缓冲机制,SurfaceView在底层机制中就实现了双缓冲机制。

实现过程

  • 继承 SurfaceView 和实现 SurfaceHolder.Callback 接口

class TaiChiView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

   override fun surfaceCreated(holder: SurfaceHolder) {
       // SurfaceView创建时候调用(仅一次)
   }

   override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
       // SurfaceView发生改变时调用
   }

   override fun surfaceDestroyed(holder: SurfaceHolder) {
       // SurfaceView发生销毁时调用(仅一次)
   }

}

  • 接下来为绘制太极图需要的变量进行一个声明和初始化

class TaiChiView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

   private lateinit var mBlackPaint: Paint
   private lateinit var mWhitePaint: Paint
   private lateinit var mCanvas: Canvas
   private var mHolder: SurfaceHolder

   init {
       mBlackPaint = Paint().apply {
           isAntiAlias = true
           strokeWidth = 2f
           color = Color.BLACK
           style = Paint.Style.FILL
       }
       mWhitePaint = Paint().apply {
           isAntiAlias = true
           strokeWidth = 2f
           color = Color.WHITE
           style = Paint.Style.FILL
       }
       mHolder = holder.apply {
           addCallback(this@TaiChiView)
       }
   }

   // 省略的代码见上一节
...

}

  • 定义绘制函数,并在 surfaceCreated 方法中调用

class TaiChiView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

   ...

   private fun draw() {
       // 获取画布
       mCanvas = mHolder.lockCanvas()

       mCanvas.drawColor(Color.LTGRAY)

       // 定义曲线绘制的矩形,这里将太极图居中绘制,且大圆半径为屏幕宽度一半
       val rectLeft = width / 4f
       val rectTop = height / 2f - rectLeft
       val rectRight = width * 3 / 4f
       val rectBottom = height / 2f + rectLeft

       // 绘制左右半圆
       mCanvas.drawArc(RectF(rectLeft, rectTop, rectRight, rectBottom), 270f, -180f, true, mBlackPaint)
       mCanvas.drawArc(RectF(rectLeft, rectTop, rectRight, rectBottom), 270f, 180f, true, mWhitePaint)

       // 在左右半圆里绘制新的半圆,达到太极图的分割效果
       mCanvas.drawArc(
           RectF(width / 2f - rectLeft / 2f, rectTop, width / 2f + rectLeft / 2f, height / 2f),
           270f,
           -180f,
           true,
           mWhitePaint
       )
       mCanvas.drawArc(
           RectF(width / 2f - rectLeft / 2f, height / 2f, width / 2f + rectLeft / 2f, rectBottom),
           270f,
           180f,
           true,
           mBlackPaint
       )

       // 绘制太极图的两个小圆
       mCanvas.drawCircle(width / 2f, height / 2f - rectLeft / 2f, rectLeft / 4f, mBlackPaint)
       mCanvas.drawCircle(width / 2f, height / 2f + rectLeft / 2f, rectLeft / 4f, mWhitePaint)


       mHolder.unlockCanvasAndPost(mCanvas)
   }

   override fun surfaceCreated(holder: SurfaceHolder) {
       draw()
   }

   ...
}

  • 到此,一个太极图便成功绘制,效果图如下:

太极图(静态)
  • 接下来给绘制的太极图添加旋转动画,新增角度变量声明和动画播放开关。

class TaiChiView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

   ...
   
   // 添加动画
   private var mDegrees = 0f
   private var isRunning = true

   ...

}

  • 修改图形绘制方法,让画布围绕图形中旋转。

class TaiChiView(context: Context) : SurfaceView(context), SurfaceHolder.Callback {

   ...

   private fun draw() {
       mCanvas = mHolder.lockCanvas()

       mCanvas.drawColor(Color.LTGRAY)

       val rectLeft = width / 4f
       val rectTop = height / 2f - rectLeft
       val rectRight = width * 3 / 4f
       val rectBottom = height / 2f + rectLeft

       mCanvas.save()
       // 设置旋转角度
       mCanvas.rotate(mDegrees, width / 2f, height/2f)

       // 绘制太极图的代码路基
       ...

       // restore方法和save方法数量一致
       mCanvas.restore()
       mHolder.unlockCanvasAndPost(mCanvas)
   }

   override fun surfaceCreated(holder: SurfaceHolder) {
       // 启用协程,不断绘制太极图,改变旋转角度
       CoroutineScope(Dispatchers.Default).launch {
           while (isRunning) {
               draw()
               mDegrees++
           }
       }
   }
...

   override fun surfaceDestroyed(holder: SurfaceHolder) {
       // SurfaceView销毁则停止动画
       isRunning = false
   }

}

  • 最后,一个会动的太极图就成功绘制完成,效果如图:

太极图(动态)

文本跑马灯

使用 TextView 实现跑马灯存在一个焦点的问题,如果不设置焦点,那么跑马灯无法运行起来。通过自定义 TextView 方式重写 isFocused 方法即可解决问题。

class MarqueeText(context: Context) : TextView(context) {

   init {
       // 初始化以下textview已提供的跑马灯属性
       ellipsize = TextUtils.TruncateAt.MARQUEE
       isSingleLine = true
       // 永久循环
       marqueeRepeatLimit = -1
       isSelected = true
   }

   // 重写isFocused方法,让textview失踪获取焦点
   override fun isFocused(): Boolean = true
}

注意:TextView 跑马灯需要文本足够长才能够动起来。

效果如图:

文本跑马灯

项目地址:https://github.com/laoheix/lao_hei_dome.git

使用Android Compose显示,存在SurfaceView跳转后Canvas为空的异常,后续修复。

Android实现太极图、跑马灯(自定义View)的评论 (共 条)

分享到微博请遵守国家法律