Android kotlin实现自定义水波纹效果

Android kotlin实现自定义水波纹效果

最近做项目需要用kotlin实现一个水波纹效果,所以自己写了一个,怕以后忘记所以上来总结和分享一下,希望对大家也有帮助!不说废话了,直接上代码,在代码里面都加了注释,相信大家一看就懂了!下面是水波纹自定义view的全部代码:

class WaterRipplesView(context: Context, attrs: AttributeSet) : View(context, attrs) {

    /**
     * 默认的周期
     */
    private var mCycleFactorW = 0f

    /**
     * 周期倍数
     */
    private var mCycleMultiple = 1f

    /**
     * 控件总宽度,dp,转化为dp为了减少资源消耗
     */
    private var mTotalWidth = 0
    /**
     * 控件总高度,像素点
     */
    private var mTotalHeight = 0f
    /**
     * 用于保存初始时波纹的Y值
     */
    private lateinit var mYPositions: FloatArray

    /**
     * 保存移动波纹的Y值
     */
    private var mResetYPositions: ArrayList<FloatArray> = ArrayList()
    /**
     * 波纹移动速度
     */
    private var mXOffsetSpeeds: ArrayList<Int> = ArrayList()
    /**
     * 波纹移动距离
     */
    private var mXOffsets: ArrayList<Int> = ArrayList()
    /**
     * 波纹的振幅,默认20
     */
    private var mAmplitude = 30f
    /**
     * 波纹数量,默认3
     */
    private var mRippleNum = 3
    /**
     * 波纹的基础速度
     */
    private var mRippleBaseSpeed = 5f
    /**
     * 波纹的间隔速度
     */
    private var mRippleInterval = 2f

    private var mRippleYPosition = 0.5f

    /**
     * 当前水波纹的y值占比
     */
    private var mCurrentRippleYPosition = 0.5f

    /**
     * 是否要刷新水波纹
     */
    private var mIsStartRefresh = true

    private var paintColor: IntArray

    private var callback: OnPercentageCallback? = null

    private var time = 20L


    private val runnable: Runnable = Runnable {
        postInvalidate()
    }

    companion object {
        private val OFFSET_Y = 0

        private val GREEN_COLOR = intArrayOf(0x6685f53f, 0x8085f53f.toInt(), 0xcc85f53f.toInt())

        private val YELLOW_COLOR = intArrayOf(0x66ffee59, 0x80ffee59.toInt(), 0xccffee59.toInt())

        private val RED_COLOR = intArrayOf(0x66d94a35, 0x80d94a35.toInt(), 0xccd94a35.toInt())
    }

    /**
     * 水波纹画笔
     */
    private val mWavePaint: Paint
    private val mDrawFilter: DrawFilter

    init {
        val ta: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.app_WaterRipplesView)
        mAmplitude = ScreenUtils.dip2px(context, ta.getFloat(R.styleable.app_WaterRipplesView_app_ripple_amplitude, 20f))
        mCycleMultiple = ta.getFloat(R.styleable.app_WaterRipplesView_app_ripple_cycle, 1f)
        mRippleYPosition = ta.getFloat(R.styleable.app_WaterRipplesView_app_ripple_position, 0f)
        mCurrentRippleYPosition = mRippleYPosition
        for (i in 0..mRippleNum - 1) {
            mXOffsetSpeeds.add((mRippleBaseSpeed + i * mRippleInterval).toInt())
            mXOffsets.add(0)
        }

        // 初始绘制波纹的画笔
        mWavePaint = Paint()
        // 去除画笔锯齿
        mWavePaint.isAntiAlias = true
        mWavePaint.strokeWidth = ScreenUtils.dip2px(context, 1f)
        // 设置风格为实线
        mWavePaint.style = Style.FILL
        mDrawFilter = PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG or Paint.FILTER_BITMAP_FLAG)
        paintColor = GREEN_COLOR
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        // 从canvas层面去除绘制时锯齿
        canvas.drawFilter = mDrawFilter
        //水波纹的y值小于当前值时的处理
        if (mCurrentRippleYPosition > mRippleYPosition) {
            mRippleYPosition += 0.02f
            //防止不被2整除的时候出现的问题
            if (mRippleYPosition > mCurrentRippleYPosition) {
                mRippleYPosition = mCurrentRippleYPosition
            }
            //判断波纹的颜色
            if (mRippleYPosition <= 0.5f) {
                paintColor = GREEN_COLOR
            } else if (mRippleYPosition <= 0.75) {
                paintColor = YELLOW_COLOR
            } else {
                paintColor = RED_COLOR
            }
            //回调当前水波纹y轴的占比,用于界面动态显示
            callback!!.onPercentage(mRippleYPosition)
        }
        val startY = mTotalHeight - mRippleYPosition * mTotalHeight + mAmplitude
        // 绘制水波纹
        for (j in 0..mRippleNum - 1) {
            // 使用System.arraycopy方式重新填充第一条波纹的数据
            System.arraycopy(mYPositions, mXOffsets[j], mResetYPositions[j], 0, mYPositions.size - mXOffsets[j])
            System.arraycopy(mYPositions, 0, mResetYPositions[j], mYPositions.size - mXOffsets[j], mXOffsets[j])
            mWavePaint.color = paintColor[j]
            for (i in 0..mTotalWidth - 1) {
                val X = ScreenUtils.dip2px(context, i.toFloat())
                canvas.drawLine(X, startY - mResetYPositions[j][i], X, mTotalHeight, mWavePaint)
            }
        }
        // 改变波纹的移动点
        for (i in 0..mRippleNum - 1) {
            mXOffsets[i] += mXOffsetSpeeds[i]
            // 如果已经移动到结尾处,则重头记录
            if (mXOffsets[i] >= mTotalWidth)
                mXOffsets[i] = 0
        }

        // 延时后刷新水波纹,如果想水波纹更加的流畅,可以不延时,但是会消耗更多的资源
        if (mIsStartRefresh) {
            handler.removeCallbacks(runnable)
            handler.postDelayed(runnable, time)
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        // 记录下view的宽高
        mTotalWidth = ScreenUtils.px2dip(context, w.toFloat()).toInt()
        mTotalHeight = h.toFloat()

        // 用于保存原始波纹的y值
        mYPositions = FloatArray(mTotalWidth)

        for (i in 0..mRippleNum - 1) {
            mResetYPositions.add(kotlin.FloatArray(mTotalWidth))
        }
        // 将周期定为view总宽度
        mCycleFactorW = (2 * Math.PI / mTotalWidth).toFloat()
        setYPositions()
        //保持时间一直,从而保持不同手机的水波纹移动速度基本一样
        time = (100 * (185f / mTotalWidth)).toLong()
    }

    private fun setYPositions() {
        // 根据view总宽度得出所有对应的y值
        for (i in 0..mTotalWidth - 1) {
            mYPositions[i] = (mAmplitude * Math.sin((mCycleMultiple * mCycleFactorW * i).toDouble()) + OFFSET_Y).toFloat()
        }
    }

    /**
     * 设置波纹占整个控件的多少
     * @param position 值为0到1,该值小于0和大于1时无效
     */
    fun setRipplePosition(position: Float) {
        if (position in 0.0..1.0) {
            mRippleYPosition = 0f
            mCurrentRippleYPosition = position
        }
    }

    /**
     * 设置百分比的监听
     */
    fun setOnPercentageCallback(callback: OnPercentageCallback) {
        this.callback = callback
    }

    /**
     * 开始刷新水波纹
     */
    fun startRefreshRipple() {
        mIsStartRefresh = true
        postInvalidate()
    }

    /**
     * 停止刷新水波纹
     */
    fun stopRefreshRipple() {
        mIsStartRefresh = false
    }

    interface OnPercentageCallback {
        fun onPercentage(percentage: Float)
    }
}

以上就是自定义水波纹的代码了,有不懂的地方欢迎留言,有时间肯定及时回复,还有可以优化的地方欢迎大家一起讨论!

下面是我自己写的demo,需要的可以去下载: 源码下载地址

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现 Android Kotlin 中按钮展示 Spinner 的自定义样式,可以按照以下步骤进行: 1. 在布局文件中添加一个 Button 控件和一个隐藏的 Spinner 控件: ```xml <Button android:id="@+id/btn_spinner" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Select Item"/> <Spinner android:id="@+id/spinner" android:layout_width="0dp" android:layout_height="0dp" android:visibility="gone"/> ``` 2. 在 Kotlin 代码中设置 Button 的点击事件: ```kotlin btn_spinner.setOnClickListener { spinner.performClick() } ``` 3. 创建一个自定义的 Spinner 样式,例如: ```xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="16dp"> <ImageView android:id="@+id/img_icon" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/ic_launcher"/> <TextView android:id="@+id/tv_item_name" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginStart="16dp" android:text="Item Name" android:textSize="16sp"/> </LinearLayout> ``` 4. 创建一个自定义的 Spinner 适配器: ```kotlin class CustomSpinnerAdapter(private val context: Context, private val items: List<String>) : ArrayAdapter<String>(context, 0, items) { override fun getView(position: Int, convertView: View?, parent: ViewGroup): View { var view = convertView if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.item_spinner, parent, false) } val tvItemName = view?.findViewById<TextView>(R.id.tv_item_name) tvItemName?.text = items[position] return view!! } override fun getDropDownView(position: Int, convertView: View?, parent: ViewGroup): View { var view = convertView if (view == null) { view = LayoutInflater.from(context).inflate(R.layout.item_spinner_dropdown, parent, false) } val tvItemName = view?.findViewById<TextView>(R.id.tv_item_name) tvItemName?.text = items[position] return view!! } } ``` 5. 在 Kotlin 代码中设置 Spinner 的适配器和选择事件: ```kotlin val items = listOf("Item 1", "Item 2", "Item 3") val adapter = CustomSpinnerAdapter(this, items) spinner.adapter = adapter spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) { btn_spinner.text = items[position] } override fun onNothingSelected(parent: AdapterView<*>?) { // Do nothing } } ``` 这样就可以实现一个自定义样式的 Spinner,展示在一个 Button 控件上。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值