演示效果如下:
实现内容:
- 控制动画是竖向或者横向
- 控制动画初始是从底部/左边开始,或者从上边/右边开始
- 控制动画的时常
- 可以自定义动画素材
具体实现:
自定义属性:
<declare-styleable name="ScanView" tools:ignore="ResourceName">
<!--扫描的图片-->
<attr name="unit_scan_img" format="reference" />
<!--动画时长-->
<attr name="anim_duration" format="integer" />
</declare-styleable>
自定义 View:
实现原理,就是对 bitmap 做 Translate、preScale 转换,然后不断的 draw bitmap。其中 Translate 是为了实现位移效果,preScale 则是为了来回转换方向。
class ScanView : View {
/**
* 扫描的图片drawable
*/
private var scanImg: Drawable? = null
private lateinit var paint: Paint
/**
* 控件的宽
*/
private var viewWidth: Int = 0
/**
* 控件的高
*/
private var viewHeight: Int = 0
private var bitmapMatrix = Matrix()
private var scanBitmap: Bitmap? = null
/**
* 扫描图片需要显示的高度
*/
private var showBitmapHeight: Float = 0F
/**
* 控制动画是竖向或者横向
*/
private var isVertical = true
/**
* 控制动画初始是从底部/左边开始(true),或者从上边/右边开始(false)
*/
private var isStartFromBottom = true
private var isPositive = true
fun setVertical(isVertical: Boolean) {
this.isVertical = isVertical
stopScanAnimAndReset()
setScanBitmap()
}
fun setStartFromBootom(isFromBottom: Boolean) {
this.isStartFromBottom = isFromBottom
stopScanAnimAndReset()
}
/**
* 属性动画
*/
private var valueAnimator: ValueAnimator? = null
/**
* 动画时长
*/
private var animDuration: Long = 1000L
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
initAttribute(context, attrs)
init()
}
private fun initAttribute(context: Context, attrs: AttributeSet?) {
attrs?.let {
val typedArray = context.obtainStyledAttributes(attrs, R.styleable.ScanView)
scanImg = typedArray.getDrawable(R.styleable.ScanView_unit_scan_img)
animDuration = typedArray.getInt(R.styleable.ScanView_anim_duration, 1000).toLong()
typedArray.recycle()
}
}
fun setAnimDuration(time: Long) {
animDuration = time
}
private fun init() {
paint = Paint(Paint.ANTI_ALIAS_FLAG)
paint.style = Paint.Style.FILL
}
private fun getBitmapFromDrawable(drawable: Drawable): Bitmap? {
var unitImgBitmap = drawable.toBitmap()
if (unitImgBitmap.isRecycled) {
return null
}
if (unitImgBitmap.isRecycled) {
return null
}
// 处理横置的时候图片的旋转(因为视觉给的图一般是一个竖向的图,因此在横置的时候,手动将图片同步横置)
if (!isVertical) {
val matrix = Matrix(