自定义View之条状佛珠

该文章描述了一个名为LinearBead的自定义View,它在Android中使用手势检测和动画实现了一种线性佛珠滚动的效果。View通过处理Bitmap和Animator来绘制和移动佛珠,同时支持设置佛珠的数量和样式。当用户执行向上的fling手势时,佛珠会沿着线性路径滚动。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.fenghuajueli.module_host.widget

import android.animation.Animator
import android.animation.Animator.AnimatorListener
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Rect
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import com.blankj.utilcode.util.LogUtils
import com.fenghuajueli.module_host.R
import kotlin.math.abs


class LinearBead(context: Context,attr: AttributeSet): View(context,attr) ,
        GestureDetector.OnGestureListener{
    private var mBeadNumber = 6
    private var mBeadStyle = arrayOf(
            R.mipmap.fz_6,
            R.mipmap.fz_7,
            R.mipmap.fz_8,
            R.mipmap.fz_9,
            R.mipmap.fz_10,
            R.mipmap.fz_11,
    )
    private lateinit var mBitPaint: Paint
    private lateinit var mRopeBitmap:Bitmap
    private var mBeadBitmaps: MutableList<Bitmap> = arrayListOf()
    private var mBeadBitmapSize = 346
    private var mBeadsGap = 25

    private lateinit var mDetector: GestureDetector
    private lateinit var mValueAnimator: ValueAnimator
    private var isPlayingAnm: Boolean = false
    private var mTranslateY = 0f
    private lateinit var iIOnFlingListener: IOnFlingListener
    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
//        LogUtils.e("onDraw")
        canvas.drawBitmap(
                mRopeBitmap,
                Rect(0,0,mRopeBitmap.width,mRopeBitmap.height),
                Rect(
                        width / 2 - mRopeBitmap.width / 2,
                        0,
                        width / 2 + mRopeBitmap.width / 2,
                        mRopeBitmap.height
                ),
                mBitPaint
        )
        for (i in 0 until mBeadNumber + 1) {
            canvas.drawBitmap(
                    mBeadBitmaps[i],
                    Rect(0,0,mBeadBitmapSize,mBeadBitmapSize),
                    Rect(
                            width / 2 - mBeadBitmapSize / 2 ,
                            (mBeadsGap * (i+1) + mBeadBitmapSize * i - mTranslateY).toInt(),
                            width / 2 + mBeadBitmapSize / 2 ,
                            (mBeadsGap * (i+1) + mBeadBitmapSize * i - mTranslateY
                                    + mBeadBitmapSize).toInt()
                    ),
                    mBitPaint
            )
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        LogUtils.e("onSizeChanged",w,h,oldw,oldh,height,measuredHeight)
        mDetector = GestureDetector(context,this)
        mRopeBitmap = BitmapFactory.decodeResource(resources,R.mipmap.img_zhulian)
                .copy(Bitmap.Config.ARGB_8888, true)
        mRopeBitmap.height = h
        initBitmapSize()
        initBeadBitmaps()
        initValueAnimator()
        initPaint()
    }

    /**
     * 初始化佛珠图
     */
    private fun initBeadBitmaps(){
        mBeadBitmaps.clear()
        var j = 0
        // 需增加屏幕底部外一颗珠子,方便动画移动显示 所以 加 1
        for (i in 0 until mBeadNumber + 1) {
            if (j == mBeadStyle.size) {
                j = 0
            }

            val bitmap = BitmapFactory.decodeResource(resources, mBeadStyle[j])
            mBeadBitmaps.add(imageScale(bitmap, mBeadBitmapSize,mBeadBitmapSize))
            j ++
        }
    }

    /**
     * (高度 - 每个珠子的间距) / 珠子数量 = 每个珠子的大小
     */
    private fun initBitmapSize(){
        // 25代表10dp
        mBeadBitmapSize = (height - mBeadsGap * (mBeadNumber + 1)) / mBeadNumber
        LogUtils.e("mBitmapSize",mBeadBitmapSize)
    }

    /**
     * 初始化值动画
     */
    private fun initValueAnimator(){
        mValueAnimator = ValueAnimator.ofFloat(0f, mBeadBitmapSize + mBeadsGap.toFloat())
        mValueAnimator.duration = 500
        mValueAnimator.addUpdateListener {
            mTranslateY = it.animatedValue as Float
            invalidate()
        }
        mValueAnimator.addListener(object :AnimatorListener{
            override fun onAnimationStart(p0: Animator) {
                isPlayingAnm = true
            }

            override fun onAnimationEnd(p0: Animator) {
                mTranslateY = 0f
                changeBeadBitmapsPosition()
                invalidate()
                isPlayingAnm = false
            }

            override fun onAnimationCancel(p0: Animator) {

            }

            override fun onAnimationRepeat(p0: Animator) {

            }

        })
    }

    /**
     * 改变珠子位置
     */
    private fun changeBeadBitmapsPosition() {
        var newBeadBitmaps: MutableList<Bitmap> = arrayListOf()
        for (i in 1 until mBeadBitmaps.size) {
            newBeadBitmaps.add(mBeadBitmaps[i])
        }
        newBeadBitmaps.add(mBeadBitmaps[1])
        mBeadBitmaps.clear()
        mBeadBitmaps.addAll(newBeadBitmaps)
    }

    private fun updateValueAnimator(){
        if (this::mValueAnimator.isInitialized){
            mValueAnimator.cancel()
        }
        initValueAnimator()
    }

    private fun initPaint(){
        mBitPaint = Paint(Paint.ANTI_ALIAS_FLAG)
        mBitPaint.isFilterBitmap = true
        mBitPaint.isDither = true
    }

    private fun imageScale(oldBitmap: Bitmap, newWidth: Int, newHeight: Int): Bitmap {
        //原始bitmap的宽高
        val oldWidth = oldBitmap.width
        val oldHeight = oldBitmap.height

        // 设置的宽高和原来的宽高的比例
        val scaleW = newWidth / oldWidth.toFloat()
        val scaleH = newHeight / oldHeight.toFloat()
        //比例
        val matrix = Matrix()
        matrix.postScale(scaleW, scaleH)
        return Bitmap.createBitmap(oldBitmap, 0, 0, oldWidth, oldHeight, matrix, true)
    }

    fun setBeadNumber(number: Int) {
        mBeadNumber = number
        initBitmapSize()
        initBeadBitmaps()
        invalidate()
        updateValueAnimator()
    }

    fun setBeadStyle(beadStyle: Array<Int>) {
        mBeadStyle = beadStyle
        initBeadBitmaps()
        invalidate()
    }

    fun setIOnFlingListener(listener: IOnFlingListener){
        iIOnFlingListener = listener
    }


    override fun onTouchEvent(event: MotionEvent): Boolean {
        mDetector.onTouchEvent(event)
        return true
    }

    override fun onDown(p0: MotionEvent): Boolean {
        return true
    }

    override fun onShowPress(p0: MotionEvent) {

    }

    override fun onSingleTapUp(p0: MotionEvent): Boolean {
        return false
    }

    override fun onScroll(p0: MotionEvent, p1: MotionEvent, p2: Float, p3: Float): Boolean {
        return false
    }

    override fun onLongPress(p0: MotionEvent) {

    }

    override fun onFling(e1: MotionEvent, e2: MotionEvent, p2: Float, p3: Float): Boolean {
        return if(e1.y - e2.y > 0 && abs((e1.y - e2.y)) > 30 && !isPlayingAnm){
            //向上滑动的判断,如果手指上滑动,就走这个方法
            mValueAnimator.start()
            iIOnFlingListener.slideUp()
            true
        }else {
            false
        }
    }

    interface IOnFlingListener{
        fun slideUp()
    }
}

需提供佛珠图片mBeadStyle

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值