务实求正 發表於 2026-4-23 17:11:00

扫光动效

<p><img src="https://img2024.cnblogs.com/blog/583064/202604/583064-20260423165343426-1382920484.gif"></p>
<p>使用&nbsp;LinearGradient 绘制渐变区域,然后旋转角度,可以根据情况跳转扫光区域的大小</p>
<p>针对大量扫光动效同时进行时,需要对绘制进行优化,否则过渡消耗CPU性能</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">drawRectF.set(
    max(0f, diffX </span>-<span style="color: rgba(0, 0, 0, 1)"> extraWidth), 0f,
    min(width.toFloat(), lightRectF.right </span>+<span style="color: rgba(0, 0, 0, 1)"> extraWidth), mHeight
)<br></span></pre>
<div>
<pre>override fun onDraw(canvas: Canvas) {<br>    super.onDraw(canvas)<br>    if (!isStart) return<br>    canvas.drawRect(drawRectF, paint)<br>}</pre>
</div>
<pre><span style="color: rgba(0, 0, 0, 1)">&nbsp;</span></pre>
</div>
<p>只绘制扫光区域,并且对不可见区域不做绘制,暂停时不做绘制</p>
<div class="cnblogs_code"><img src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_67f65ea9-8ae0-4e81-9797-a88faff314f5" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_67f65ea9-8ae0-4e81-9797-a88faff314f5" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> com.example.screentest

</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.animation.ValueAnimator
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.content.Context
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.Canvas
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.Color
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.LinearGradient
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.Matrix
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.Outline
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.Paint
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.RectF
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.graphics.Shader
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.util.AttributeSet
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.util.Log
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.view.View
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.view.ViewOutlineProvider
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> android.view.animation.AnticipateOvershootInterpolator
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> androidx.core.animation.doOnEnd
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> androidx.core.content.withStyledAttributes
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> kotlin.math.abs
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> kotlin.math.cos
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> kotlin.math.max
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> kotlin.math.min
</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> kotlin.math.sin

</span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
* </span><span style="color: rgba(128, 128, 128, 1)">@author</span><span style="color: rgba(0, 128, 0, 1)"> liuzhen
* 流光动效
* loading drawable
* see
* 圆角
</span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
open </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ShimmerView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet</span>? = <span style="color: rgba(0, 0, 255, 1)">null</span>, defStyleAttr: Int = 0<span style="color: rgba(0, 0, 0, 1)">
) : View(context, attrs, defStyleAttr) {

    companion object {
      </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">const</span> val TAG = "ShimmerView"
      <span style="color: rgba(0, 0, 255, 1)">const</span> val SHIMMER_DURATION = 2000L<span style="color: rgba(0, 0, 0, 1)">
    }

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val shimmerColors =<span style="color: rgba(0, 0, 0, 1)"> intArrayOf(
      Color.TRANSPARENT,
      resources.getColor(R.color.color_shimmer2, context.theme),
      resources.getColor(R.color.color_shimmer1, context.theme),
      resources.getColor(R.color.color_shimmer2, context.theme),
      Color.TRANSPARENT,
    )
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val positions = floatArrayOf(0f, 0.35f, 0.5f, 0.65f<span style="color: rgba(0, 0, 0, 1)">, 1f)
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style =<span style="color: rgba(0, 0, 0, 1)"> Paint.Style.FILL }
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val lightRectF =<span style="color: rgba(0, 0, 0, 1)"> RectF()
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val drawRectF =<span style="color: rgba(0, 0, 0, 1)"> RectF()
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val mShaderMatrix =<span style="color: rgba(0, 0, 0, 1)"> Matrix()
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> var diffX =<span style="color: rgba(0, 0, 0, 1)"> 0f
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> var isStart = <span style="color: rgba(0, 0, 255, 1)">false</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> var isInit = <span style="color: rgba(0, 0, 255, 1)">false</span>
    <span style="color: rgba(0, 0, 255, 1)">private</span> var lightWidth =<span style="color: rgba(0, 0, 0, 1)"> 0f
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> var extraWidth =<span style="color: rgba(0, 0, 0, 1)"> 0f
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> var mHeight =<span style="color: rgba(0, 0, 0, 1)"> 0f
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> var mRotate =<span style="color: rgba(0, 0, 0, 1)"> 30f
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> var shimmerType =<span style="color: rgba(0, 0, 0, 1)"> ShimmerType.BANNER.type

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val shimmerAnimator =<span style="color: rgba(0, 0, 0, 1)"> ValueAnimator().apply {
      duration </span>=<span style="color: rgba(0, 0, 0, 1)"> SHIMMER_DURATION
      repeatCount </span>=<span style="color: rgba(0, 0, 0, 1)"> ValueAnimator.INFINITE
      interpolator </span>= AnticipateOvershootInterpolator(0.3f<span style="color: rgba(0, 0, 0, 1)">)
      addUpdateListener { anim </span>-&gt;<span style="color: rgba(0, 0, 0, 1)">
            val num </span>=<span style="color: rgba(0, 0, 0, 1)"> anim.animatedValue as Float
            diffX </span>=<span style="color: rgba(0, 0, 0, 1)"> num
            updateGradient()
      }
      doOnEnd {
            isStart </span>= <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">
            diffX </span>=<span style="color: rgba(0, 0, 0, 1)"> lightWidth
            updateGradient()
      }
    }

    init {
      context.withStyledAttributes(attrs, R.styleable.ShimmerView) {
            val shimmerType </span>=<span style="color: rgba(0, 0, 0, 1)"> getInt(R.styleable.ShimmerView_shimmerType, ShimmerType.BANNER.type)
            val radius </span>=<span style="color: rgba(0, 0, 0, 1)"> getDimension(R.styleable.ShimmerView_radius, 0f)
            init(shimmerType, radius)
      }
    }

    fun init(type: Int, radius: Float) {
      shimmerType </span>=<span style="color: rgba(0, 0, 0, 1)"> type
      clipToOutline </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
      outlineProvider </span>=<span style="color: rgba(0, 0, 0, 1)"> object : ViewOutlineProvider() {
            override fun getOutline(view: View, outline: Outline) {
                outline.setRoundRect(</span>0, 0<span style="color: rgba(0, 0, 0, 1)">, measuredWidth, measuredHeight, radius)
            }
      }
    }

    fun startShimmer() {
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (shimmerAnimator.isStarted || shimmerAnimator.isRunning || isStart) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
      Log.i(TAG, </span>"startShimmer isInit=$isInit"<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (isInit) {
            isStart </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
            shimmerAnimator.setFloatValues(</span>-(lightWidth + extraWidth), width +<span style="color: rgba(0, 0, 0, 1)"> lightWidth)
            shimmerAnimator.start()
      }
    }

    fun stopShimmer() {
      Log.i(TAG, </span>"stopShimmer"<span style="color: rgba(0, 0, 0, 1)">)
      shimmerAnimator.cancel()
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onSizeChanged(w, h, oldw, oldh)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 根据旋转角度计算合适的绘制区域宽度</span>
      val rotationRadians =<span style="color: rgba(0, 0, 0, 1)"> Math.toRadians(mRotate.toDouble())
      val cosValue </span>=<span style="color: rgba(0, 0, 0, 1)"> abs(cos(rotationRadians))
      val sinValue </span>=<span style="color: rgba(0, 0, 0, 1)"> abs(sin(rotationRadians))

      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 计算旋转后需要的额外宽度,确保覆盖整个可见区域</span>
      extraWidth = (h * sinValue /<span style="color: rgba(0, 0, 0, 1)"> cosValue).toFloat()
      lightWidth </span>= w / <span style="color: rgba(0, 0, 255, 1)">if</span> (shimmerType == ShimmerType.ITEM.type) 1.2f <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> 4f
      mHeight </span>=<span style="color: rgba(0, 0, 0, 1)"> h.toFloat()
      isInit </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
      log(</span>"onSizeChanged type=$shimmerType,width=$w,height=$h,lightWidth=$lightWidth,extraWidth=$extraWidth"<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">isStart) startShimmer()
    }

    </span><span style="color: rgba(0, 0, 255, 1)">private</span><span style="color: rgba(0, 0, 0, 1)"> fun updateGradient() {
      mShaderMatrix.reset()
      lightRectF.set(diffX, 0f, diffX </span>+<span style="color: rgba(0, 0, 0, 1)"> lightWidth, mHeight)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置可见区域,避免多余消耗</span>
<span style="color: rgba(0, 0, 0, 1)">      drawRectF.set(
            max(0f, diffX </span>-<span style="color: rgba(0, 0, 0, 1)"> extraWidth), 0f,
            min(width.toFloat(), lightRectF.right </span>+<span style="color: rgba(0, 0, 0, 1)"> extraWidth), mHeight
      )
      paint.shader </span>=<span style="color: rgba(0, 0, 0, 1)"> LinearGradient(
            lightRectF.left, lightRectF.top,
            lightRectF.right, lightRectF.top,
            shimmerColors, positions,
            Shader.TileMode.CLAMP
      ).apply {
            mShaderMatrix.setRotate(mRotate, lightRectF.centerX(), lightRectF.centerY())
            setLocalMatrix(mShaderMatrix)
      }
      invalidate()
    }

    override fun onDraw(canvas: Canvas) {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onDraw(canvas)
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!isStart) <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
      canvas.drawRect(drawRectF, paint)
    }

    override fun onAttachedToWindow() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onAttachedToWindow()
      startShimmer()
    }

    override fun onDetachedFromWindow() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onDetachedFromWindow()
      Log.i(TAG, </span>"onDetachedFromWindow type=$shimmerType"<span style="color: rgba(0, 0, 0, 1)">)
      stopShimmer()
    }

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> fun log(str: String) =<span style="color: rgba(0, 0, 0, 1)"> Log.i(TAG, str)

}

</span><span style="color: rgba(0, 0, 255, 1)">enum</span> <span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ShimmerType(val type: Int) {

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> 流光宽度较小 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    BANNER(</span>0<span style="color: rgba(0, 0, 0, 1)">),

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)"> 流光宽度较大 </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    ITEM(</span>1<span style="color: rgba(0, 0, 0, 1)">),
}</span></pre>
</div>
<span class="cnblogs_code_collapse">View Code</span></div>
<div class="cnblogs_code"><img id="code_img_closed_9b6177cc-4b84-49e4-a136-dcd594651080" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_9b6177cc-4b84-49e4-a136-dcd594651080" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_9b6177cc-4b84-49e4-a136-dcd594651080" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">FrameLayout </span><span style="color: rgba(255, 0, 0, 1)">xmlns:android</span><span style="color: rgba(0, 0, 255, 1)">="http://schemas.android.com/apk/res/android"</span><span style="color: rgba(255, 0, 0, 1)">
    xmlns:app</span><span style="color: rgba(0, 0, 255, 1)">="http://schemas.android.com/apk/res-auto"</span><span style="color: rgba(255, 0, 0, 1)">
    android:id</span><span style="color: rgba(0, 0, 255, 1)">="@+id/main"</span><span style="color: rgba(255, 0, 0, 1)">
    android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
    android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">com.example.screentest.ShimmerView
      </span><span style="color: rgba(255, 0, 0, 1)">android:id</span><span style="color: rgba(0, 0, 255, 1)">="@+id/shimmer_view1"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="350dp"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_marginHorizontal</span><span style="color: rgba(0, 0, 255, 1)">="50dp"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_marginTop</span><span style="color: rgba(0, 0, 255, 1)">="20dp"</span><span style="color: rgba(255, 0, 0, 1)">
      android:background</span><span style="color: rgba(0, 0, 255, 1)">="@android:color/background_dark"</span><span style="color: rgba(255, 0, 0, 1)">
      app:radius</span><span style="color: rgba(0, 0, 255, 1)">="24dp"</span><span style="color: rgba(255, 0, 0, 1)">
      app:shimmerType</span><span style="color: rgba(0, 0, 255, 1)">="banner"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>

    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">com.example.screentest.ShimmerView
      </span><span style="color: rgba(255, 0, 0, 1)">android:id</span><span style="color: rgba(0, 0, 255, 1)">="@+id/shimmer_view2"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="530dp"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="300dp"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_marginHorizontal</span><span style="color: rgba(0, 0, 255, 1)">="50dp"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_marginTop</span><span style="color: rgba(0, 0, 255, 1)">="400dp"</span><span style="color: rgba(255, 0, 0, 1)">
      android:background</span><span style="color: rgba(0, 0, 255, 1)">="@android:color/background_dark"</span><span style="color: rgba(255, 0, 0, 1)">
      app:radius</span><span style="color: rgba(0, 0, 255, 1)">="24dp"</span><span style="color: rgba(255, 0, 0, 1)">
      app:shimmerType</span><span style="color: rgba(0, 0, 255, 1)">="item"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">FrameLayout</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<span class="cnblogs_code_collapse">activity_test</span></div>
<div class="cnblogs_code"><img id="code_img_closed_7b64aa0b-57da-4062-b693-76b61b2de905" class="code_img_closed lazyload" data-src="http://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif"><img id="code_img_opened_7b64aa0b-57da-4062-b693-76b61b2de905" class="code_img_opened lazyload" style="display: none" data-src="http://images.cnblogs.com/OutliningIndicators/ExpandedBlockStart.gif">
<div id="cnblogs_code_open_7b64aa0b-57da-4062-b693-76b61b2de905" class="cnblogs_code_hide">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">declare-styleable </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="ShimmerView"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> Sets a drawable as the content of this ImageView. </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">attr </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="src"</span><span style="color: rgba(255, 0, 0, 1)"> format</span><span style="color: rgba(0, 0, 255, 1)">="reference|color"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
      <span style="color: rgba(0, 128, 0, 1)">&lt;!--</span><span style="color: rgba(0, 128, 0, 1)"> com.example.screentest.ShimmerType </span><span style="color: rgba(0, 128, 0, 1)">--&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">attr </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="shimmerType"</span><span style="color: rgba(255, 0, 0, 1)"> format</span><span style="color: rgba(0, 0, 255, 1)">="enum"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">enum </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="banner"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="0"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
            <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">enum </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="item"</span><span style="color: rgba(255, 0, 0, 1)"> value</span><span style="color: rgba(0, 0, 255, 1)">="1"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">attr</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>
      <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">attr </span><span style="color: rgba(255, 0, 0, 1)">name</span><span style="color: rgba(0, 0, 255, 1)">="radius"</span><span style="color: rgba(255, 0, 0, 1)"> format</span><span style="color: rgba(0, 0, 255, 1)">="dimension"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>
<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">declare-styleable</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<span class="cnblogs_code_collapse">attrs</span></div>
<p><img alt="image" width="784" height="274" loading="lazy" src="https://img2024.cnblogs.com/blog/583064/202604/583064-20260423170915891-2073693300.png"></p>
<p>非旋转状态下起始位置绘制</p>
<p><img src="https://img2024.cnblogs.com/blog/583064/202604/583064-20260423170807026-889078893.png"></p>
<p>旋转30度后,在起始位置绘制</p>
<p>&nbsp;</p><br><br>
来源:https://www.cnblogs.com/LiuZhen/p/19917206
頁: [1]
查看完整版本: 扫光动效