江门网站建设电话,深圳网站建设认准乐云践新,手机模板网站,如何创立网站前言关于自定义View#xff0c;相信大家都已经很熟悉了。今天#xff0c;我想分享一下关于自定义View中的一部分#xff0c;就是自定义Drawable。Drawable 是可绘制对象的一个抽象类#xff0c;相对比View来说#xff0c;它更加的纯粹#xff0c;只用来处理绘制的相关工作…前言关于自定义View相信大家都已经很熟悉了。今天我想分享一下关于自定义View中的一部分就是自定义Drawable。Drawable 是可绘制对象的一个抽象类相对比View来说它更加的纯粹只用来处理绘制的相关工作而不处理与用户的交互事件所以适合用来处理背景的绘制。在介绍自定义Drawable前我们先来学习一下几种常见的Drawable。可绘制对象资源介绍可绘制对象是指可在屏幕上绘制的图形可以通过getDrawable(int)等方法来获取然后应用到 android:drawable 和 android:icon 等属性方法中。下面介绍几种常见的可绘制对象我会分三个步骤来介绍1. 介绍一下在XML中的使用方法会举例说明。2. 然后介绍一下其属性方法。3. 再以代码的形式来动态实现XML中的同样效果会举例说明。BitmapDrawable位图图像。Android支持三种格式的位图文件.png(首选)、.jpg(可接受)、.gif(不建议)。我们可以直接使用文件名作为资源 ID 来引用位图文件也可以在 XML 文件中创建别名资源 ID这就叫做 XML位图。XML位图通过XML文件来定义指向位图文件文件位于res/drawable/filename.xml其文件名就是作为引用的资源 ID如R.drawable.filename。关于 bitmap 属性1. android:src引用可绘制对象资源必备。2. android:tileMode定义平铺模式。当平铺模式启用时位图会重复且注意一旦平铺模式启用 android:gravity 属性就将会被忽略。 定义平铺属性的值必须是以下值之一 • disabled不平铺位图默认值。 • clamp当着色器绘制范围超出其原边界时复制边缘颜色。 • repeat水平和垂直重复着色器的图像。 • mirror水平和垂直重复着色器的图像交替镜像图像以使相邻图像始终相接。注意在平铺模式启用时 android:gravity 属性将被忽略。android:gravity定义位图的重力属性当位图小于容器时可绘制对象在其容器中放置的位置。• top将对象放在其容器顶部不改变其大小。• bottom将对象放在其容器底部不改变其大小。• left将对象放在其容器左边缘不改变其大小。• right将对象放在其容器右边缘不改变其大小。• center_vertical将对象放在其容器的垂直中心不改变其大小。• fill_vertical按需要扩展对象的垂直大小使其完全适应其容器。• center_horizontal将对象放在其容器的水平中心不改变其大小。• fill_horizontal按需要扩展对象的水平大小使其完全适应其容器。• center将对象放在其容器的水平和垂直轴中心不改变其大小。• fill按需要扩展对象的垂直大小使其完全适应其容器。这是默认值。• clip_vertical可设置为让子元素的上边缘和/或下边缘裁剪至其容器边界的附加选项。裁剪基于垂直重力顶部重力裁剪上边缘底部重力裁剪下边缘任一重力不会同时裁剪两边。• clip_horizontal可设置为让子元素的左边和/或右边裁剪至其容器边界的附加选项。裁剪基于水平重力左边重力裁剪右边缘右边重力裁剪左边缘任一重力不会同时裁剪两边。除了在 XML 文件中定义位图我们也可以直接通过代码来实现即BitmapDrawable。val bitmap BitmapFactory.decodeResource(resources, R.drawable.nick)
val bitmapShape BitmapDrawable(resources, bitmap)
binding.tv2.background bitmapShape效果图如下所示LayerDrawable图层列表LayerDrawable是可绘制对象列表组成的可绘制对象。列表中的每个可绘制对象均按照列表顺序绘制列表中的最后一个可绘制对象绘于顶部。每个可绘制对象由单一 layer-list 元素内的 item 元素表示。介绍一下其中的属性1. layer-list必备的根元素。包含一个或多个 item 元素。2. item是 layer-list 元素的子项其属性支持定义在图层中所处的位置。 • android:drawable必备。引用可绘制对象资源。 • android:top整型。顶部偏移像素。 • android:right整型。右边偏移像素。 • android:bottom整型。底部偏移像素。 • android:left整型。左边偏移像素。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。val itemLeft GradientDrawable().apply {setColor(ContextCompat.getColor(requireContext(), R.color.royal_blue))setSize(50.px, 50.px)shape GradientDrawable.OVAL
}
val itemCenter GradientDrawable().apply {setColor(ContextCompat.getColor(requireContext(), R.color.indian_red))shape GradientDrawable.OVAL
}
val itemRight GradientDrawable().apply {setColor(ContextCompat.getColor(requireContext(), R.color.yellow))shape GradientDrawable.OVAL
}
val arr arrayOf(ContextCompat.getDrawable(requireContext(), R.drawable.nick)!!,itemLeft,itemCenter,itemRight
)
val ld LayerDrawable(arr).apply {setLayerInset(1, 0.px, 0.px, 250.px, 150.px)setLayerInset(2, 125.px, 75.px, 125.px, 75.px)setLayerInset(3, 250.px, 150.px, 0.px, 0.px)
}
binding.tv2.background ld效果图如下所示StateListDrawable状态列表StateListDrawable会根据对象状态使用多个不同的图像来表示同一个图形。介绍一下其中的属性• selector必备的根元素。包含一个或多个 item 元素。• item定义在某些状态期间使用的可绘制对象必须是 selector 元素的子项。其属性android:drawable引用可绘制对象资源必备。android:state_pressed布尔值。是否按下对象例如触摸/点按某按钮。android:state_checked布尔值。是否选中对象。android:state_enabled布尔值。是否能够接收触摸或点击事件。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。val sld StateListDrawable().apply {addState(intArrayOf(android.R.attr.state_pressed),ContextCompat.getDrawable(requireContext(), R.drawable.basketball))addState(StateSet.WILD_CARD, ContextCompat.getDrawable(requireContext(), R.drawable.nick))
}
binding.stateListDrawableTv2.apply {background sldsetOnClickListener {Log.e(TAG, stateListDrawableTv2: isPressed $isPressed)}
}LevelListDrawable级别列表LevelListDrawable管理可绘制对象列表每个可绘制对象都有设置Level等级限制当使用setLevel()时会加载级别列表中 android:maxLevel 值大于或等于传递至方法的值的可绘制对象资源。介绍一下其中的属性1. level-list必备的根元素。包含一个或多个 item 元素。2. item在特定级别下使用的可绘制对象。 • android:drawable必备。引用可绘制对象资源。 • android:maxLevel整型。表示该Item允许的最高级别。 • android:minLevel整型。表示该Item允许的最低级别。在将该 Drawable 应用到 View 后就可以通过 setLevel() 或 setImageLevel() 更改级别。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。class LevelListDrawableFragment : BaseFragmentFragmentLevelListDrawableBinding() {private val lld by lazy {LevelListDrawable().apply {addLevel(0, 1, getDrawable(R.drawable.nick))addLevel(0, 2, getDrawable(R.drawable.tom1))addLevel(0, 3, getDrawable(R.drawable.tom2))addLevel(0, 4, getDrawable(R.drawable.tom3))addLevel(0, 5, getDrawable(R.drawable.tom4))addLevel(0, 6, getDrawable(R.drawable.tom5))addLevel(0, 7, getDrawable(R.drawable.tom6))addLevel(0, 8, getDrawable(R.drawable.tom7))addLevel(0, 9, getDrawable(R.drawable.tom8))addLevel(0, 10, getDrawable(R.drawable.tom9))}}private fun getDrawable(id: Int): Drawable {return (ContextCompat.getDrawable(requireContext(), id)?: ContextCompat.getDrawable(requireContext(), R.drawable.nick)) as Drawable}private val levelListDrawable by lazy {ContextCompat.getDrawable(requireContext(), R.drawable.level_list_drawable)}override fun initView() {binding.levelListDrawableInclude.apply {tv1.setText(R.string.level_list_drawable)tv1.background levelListDrawabletv2.setText(R.string.level_list_drawable)tv2.background lld}binding.seekBar.apply {//init levellevelListDrawable?.level progresslld.level progress//add listenersetOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {override fun onProgressChanged(seekBar: SeekBar?,progress: Int,fromUser: Boolean) {levelListDrawable?.level progresslld.level progressLog.e(TAG, onProgressChanged: progreess $progress)}override fun onStartTrackingTouch(seekBar: SeekBar?) {}override fun onStopTrackingTouch(seekBar: SeekBar?) {}})}}}效果图如下所示TransitionDrawable转换可绘制对象TransitionDrawable可在两种可绘制对象资源之间交错淡出。介绍一下其中的属性1. transition必备的根元素。包含一个或多个 item 元素。2. item转换部分的可绘制对象。 • android:drawable必备。引用可绘制对象资源。 • android:top、android:bottom、android:left、android:right整型。偏移量像素。注意不能超过两个Item调用 startTransition() 向前转换调用 reverseTransition() 向后转换。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。class TransitionDrawableFragment : BaseFragmentFragmentTransitionDrawableBinding() {private var isShow falseprivate lateinit var manualDrawable: TransitionDrawableoverride fun initView() {binding.transitionDrawableInclude.apply {val drawableArray arrayOf(ContextCompat.getDrawable(requireContext(), R.drawable.nick),ContextCompat.getDrawable(requireContext(), R.drawable.basketball))manualDrawable TransitionDrawable(drawableArray)tv2.background manualDrawable}}private fun setTransition() {if (isShow) {manualDrawable.reverseTransition(3000)} else {manualDrawable.startTransition(3000)}}override fun onResume() {super.onResume()setTransition()isShow !isShow}}效果图如下所示InsetDrawable插入可绘制对象InsetDrawable以指定距离插入其他可绘制对象当视图需要小于视图实际边界的背景时此类可绘制对象很有用。介绍一下其属性• inset必备。根元素。• android:drawable必备。引用可绘制对象资源。• android:insetTop、android:insetBottom、android:insetLeft、android:insetRight尺寸。插入的表示为尺寸除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。val insetDrawable InsetDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.nick),0f, 0f, 0.5f, 0.25f
)
binding.tv2.background insetDrawable效果图如下所示ClipDrawable裁剪可绘制对象ClipDrawable根据level等级对可绘制对象进行裁剪可以根据level与gravity来控制子可绘制对象的宽度与高度。?xml version1.0 encodingutf-8?
clip xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:drawabledrawable/nickandroid:clipOrientationhorizontalandroid:gravitycenter/clip介绍一下其属性clip必备。根元素。android:drawable必备。引用可绘制对象资源。android:clipOrientation裁剪方向。 • horizontal水平裁剪。 • vertical垂直裁剪。android:gravity重力属性。最后通过设置level等级来实现裁剪level 默认级别为 0即完全裁剪使图像不可见。当级别为 10,000 时图像不会裁剪而是完全可见。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。class ClipDrawableFragment : BaseFragmentFragmentClipDrawableBinding() {private val clipDrawable by lazy {ContextCompat.getDrawable(requireContext(), R.drawable.clip_drawable)}private val manualClipDrawable by lazy {ClipDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.nick),Gravity.CENTER,ClipDrawable.VERTICAL)}override fun initView() {binding.clipDrawableInclude.apply {tv1.setText(R.string.clip_drawable)tv1.background clipDrawabletv2.setText(R.string.clip_drawable)tv2.background manualClipDrawable}//level 默认级别为 0即完全裁剪使图像不可见。当级别为 10,000 时图像不会裁剪而是完全可见。binding.seekBar.apply {//init levelclipDrawable?.level progressmanualClipDrawable.level progress//add listenersetOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {override fun onProgressChanged(seekBar: SeekBar?,progress: Int,fromUser: Boolean) {clipDrawable?.level progressmanualClipDrawable.level progress}override fun onStartTrackingTouch(seekBar: SeekBar?) {}override fun onStopTrackingTouch(seekBar: SeekBar?) {}})}}}效果图如下所示ScaleDrawable缩放可绘制对象ScaleDrawable根据level等级来更改其可绘制对象大小。?xml version1.0 encodingutf-8?
scale xmlns:androidhttp://schemas.android.com/apk/res/androidandroid:drawabledrawable/nickandroid:scaleWidth100%android:scaleHeight100%android:scaleGravitycenter/scale介绍一下其属性• scale必备。根元素。• android:drawable必备。引用可绘制对象资源。• android:scaleGravity指定缩放后的重力位置。• android:scaleHeight百分比。缩放高度表示为可绘制对象边界的百分比。值的格式为 XX%。例如100%、12.5% 等。• android:scaleWidth百分比。缩放宽度表示为可绘制对象边界的百分比。值的格式为 XX%。例如100%、12.5% 等。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。val scaleDrawable ScaleDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.nick),Gravity.CENTER,1f,1f
)
binding.tv2.background scaleDrawablebinding.seekBar.apply {//init leveltv1.background.level progressscaleDrawable.level progress//add listenersetOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {override fun onProgressChanged(seekBar: SeekBar?,progress: Int,fromUser: Boolean) {tv1.background.level progressscaleDrawable.level progressLog.e(TAG, onProgressChanged: progreess $progress)}override fun onStartTrackingTouch(seekBar: SeekBar?) {}override fun onStopTrackingTouch(seekBar: SeekBar?) {}})
}效果图如下所示ShapeDrawable形状可绘制对象ShapeDrawable通过XML来定义各种形状的可绘制对象。介绍一下其属性1. shape必备。根元素。2. android:shape定义形状的类型。 • rectangle默认形状填充包含视图的矩形。 • oval适应包含视图尺寸的椭圆形状。 • line跨越包含视图宽度的水平线。此形状需要 元素定义线宽。 ring环形。 • android:innerRadius尺寸。环内部中间的孔的半径。 • android:thickness尺寸。环的厚度。3. corners圆角仅当形状为矩形时适用。 • android:radius尺寸。所有角的半径。如果想要设置单独某个角可以使用android:topLeftRadius、android:topRightRadius、android:bottomLeftRadius、android:bottomRightRadius。4. padding设置内边距。 • android:left尺寸。设置左内边距。同样还有android:right、android:top、android:bottom供选择。5. size形状的大小。 • android:height尺寸。形状的高度。 • android:width尺寸。形状的宽度。6. solid填充形状的纯色。 • android:color颜色。7. stroke形状的笔画 • android:width尺寸。线宽。 • android:color颜色。线的颜色。 • android:dashGap尺寸。短划线的间距。虚线效果。 • android:dashWidth尺寸。每个短划线的大小。虚线效果。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。class ShapeDrawableFragment : BaseFragmentFragmentShapeDrawableBinding() {override fun initView() {val roundRectShape RoundRectShape(floatArrayOf(20f.px, 20f.px, 20f.px, 20f.px, 0f, 0f, 0f, 0f),null,null)binding.tv2.background MyShapeDrawable(roundRectShape)}/*** TODO: 使用 GradientDrawable 效果更好*/class MyShapeDrawable(shape: Shape) : ShapeDrawable(shape) {private val fillPaint Paint().apply {style Paint.Style.FILLcolor Color.parseColor(#4169E1)}private val strokePaint Paint().apply {style Paint.Style.STROKEcolor Color.parseColor(#FFBB86FC)strokeMiter 10fstrokeWidth 5f.pxpathEffect DashPathEffect(floatArrayOf(10f.px, 5f.px), 0f)}override fun onDraw(shape: Shape?, canvas: Canvas?, paint: Paint?) {super.onDraw(shape, canvas, paint)shape?.draw(canvas, fillPaint)shape?.draw(canvas, strokePaint)}}}效果图如下所示GradientDrawable渐变可绘制对象GradientDrawable如其名实现渐变颜色效果。其实也是属于ShapeDrawable。介绍一下其属性1. shape必备。根元素。2. gradient表示渐变的颜色。 • android:angle整型。表示渐变的角度。0 表示为从左到右90 表示为从上到上。注意必须是 45 的倍数。默认值为 0。 • android:centerX浮点型。表示渐变中心相对 X 轴位置 (0 - 1.0)。android:centerY同理。 • android:startColor颜色。起始颜色。android:endColor、android:centerColor分别表示结束颜色与中间颜色。 • android:gradientRadius浮点型。渐变的半径。仅在 android:typeradial 时适用。 • android:type渐变的类型。 • linear线性渐变。默认为该类型。 • radial径向渐变也就是雷达式渐变起始颜色为中心颜色。 • sweep流线型渐变。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。val gradientDrawable GradientDrawable().apply {shape GradientDrawable.OVALgradientType GradientDrawable.RADIAL_GRADIENTcolors intArrayOf(Color.parseColor(#00F5FF), Color.parseColor(#BBFFFF))gradientRadius 100f.px
}
binding.tv2.background gradientDrawable效果图如下所示AnimationDrawable动画可绘制对象AnimationDrawable用于创建逐帧动画的可绘制对象。?xml version1.0 encodingutf-8?
animation-list xmlns:androidhttp://schemas.android.com/apk/res/androiditemandroid:drawabledrawable/nickandroid:duration1000 /itemandroid:drawabledrawable/basketballandroid:duration1000 //animation-list介绍一下其属性1. animation-list必备。根元素。2. item每一帧的可绘制对象。 • android:drawable必备。引用可绘制对象资源。 • android:duration该帧的持续时间单位为毫秒。 • android:oneshot布尔值。代表是否只单次展示该动画默认为false。除了通过在XML中实现我们同样可以通过代码来实现上面同样的效果。val animationDrawable AnimationDrawable().apply {ContextCompat.getDrawable(requireContext(), R.drawable.nick)?.let { addFrame(it, 1000) }ContextCompat.getDrawable(requireContext(), R.drawable.basketball)?.let { addFrame(it, 1000) }
}
binding.tv2.background animationDrawable
animationDrawable.start()效果图如下所示自定义 Drawable介绍完了几种常见的可绘制对象资源接下来我们进一步学习一下如果进行自定义Drawable。class JcTestDrawable : Drawable() {override fun draw(p0: Canvas) {TODO(Not yet implemented)}override fun setAlpha(p0: Int) {TODO(Not yet implemented)}override fun setColorFilter(p0: ColorFilter?) {TODO(Not yet implemented)}override fun getOpacity(): Int {TODO(Not yet implemented)}}从上述代码可以看出我们需要继承Drawable()然后实现4个方法分别是1. setAlpha为Drawable指定一个alpha值0 表示完全透明255 表示完全不透明。2. setColorFilter为Drawable指定可选的颜色过滤器。Drawable的draw绘图内容的每个输出像素在混合到 Canvas 的渲染目标之前将被颜色过滤器修改。传递 null 会删除任何现有的颜色过滤器。3. getOpacity返回Drawable的透明度如下所示 • PixelFormat.TRANSLUCENT半透明的。 • PixelFormat.TRANSPARENT透明的。 • PixelFormat.OPAQUE不透明的。 • PixelFormat.UNKNOWN未知。4. draw在边界内进行绘制通过setBounds()受alpha与colorFilter所影响。接下来为大家举个例子。举例滚动篮球功能介绍当我们点击屏幕篮球会滚向该坐标。如下图所示实现步骤可以简单分为两步1. 绘制一个篮球。2.获取到用户点击坐标使用属性动画让篮球滚动到该位置。绘制篮球首先说绘制篮球这一步这一步不需要与用户进行交互所以我们采用自定义Drawable来进行绘制。如下所示class BallDrawable : Drawable() {private val paint Paint(Paint.ANTI_ALIAS_FLAG).apply {style Paint.Style.FILLcolor Color.parseColor(#D2691E)}private val linePaint Paint(Paint.ANTI_ALIAS_FLAG).apply {style Paint.Style.STROKEstrokeWidth 1f.pxcolor Color.BLACK}override fun draw(canvas: Canvas) {val radius bounds.width().toFloat() / 2canvas.drawCircle(bounds.width().toFloat() / 2,bounds.height().toFloat() / 2,radius,paint)//the vertical line of the ballcanvas.drawLine(bounds.width().toFloat() / 2,0f,bounds.width().toFloat() / 2,bounds.height().toFloat(),linePaint)//the transverse line of the ballcanvas.drawLine(0f,bounds.height().toFloat() / 2,bounds.width().toFloat(),bounds.height().toFloat() / 2,linePaint)val path Path()val sinValue kotlin.math.sin(Math.toRadians(45.0)).toFloat()//left curvepath.moveTo(radius - sinValue * radius,radius - sinValue * radius)path.cubicTo(radius - sinValue * radius,radius - sinValue * radius,radius,radius,radius - sinValue * radius,radius sinValue * radius)//right curvepath.moveTo(radius sinValue * radius,radius - sinValue * radius)path.cubicTo(radius sinValue * radius,radius - sinValue * radius,radius,radius,radius sinValue * radius,radius sinValue * radius)canvas.drawPath(path, linePaint)}override fun setAlpha(alpha: Int) {paint.alpha alpha}override fun getOpacity(): Int {return when (paint.alpha) {0xff - PixelFormat.OPAQUE0x00 - PixelFormat.TRANSPARENTelse - PixelFormat.TRANSLUCENT}}override fun setColorFilter(colorFilter: ColorFilter?) {paint.colorFilter colorFilter}
}滚动绘制好篮球后接着就是获取到用户的点击坐标为了更好的举例这里我放在自定义View中进行完成。如下所示class CustomBallMovingSiteView(context: Context, attributeSet: AttributeSet?, defStyleAttr: Int) :FrameLayout(context, attributeSet, defStyleAttr) {constructor(context: Context) : this(context, null, 0)constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)private lateinit var ballContainerIv: ImageViewprivate val ballDrawable BallDrawable()private val radius 50private var rippleAlpha 0private var rippleRadius 10fprivate var rawTouchEventX 0fprivate var rawTouchEventY 0fprivate var touchEventX 0fprivate var touchEventY 0fprivate var lastTouchEventX 0fprivate var lastTouchEventY 0fprivate val ripplePaint Paint(Paint.ANTI_ALIAS_FLAG).apply {isDither truecolor Color.REDstyle Paint.Style.STROKEstrokeWidth 2f.pxalpha rippleAlpha}init {initView(context, attributeSet)}private fun initView(context: Context, attributeSet: AttributeSet?) {//generate a ball by dynamicballContainerIv ImageView(context).apply {layoutParams LayoutParams(radius * 2, radius * 2).apply {gravity Gravity.CENTER}setImageDrawable(ballDrawable)//setBackgroundColor(Color.BLUE)}addView(ballContainerIv)setWillNotDraw(false)}override fun onTouchEvent(event: MotionEvent?): Boolean {lastTouchEventX touchEventXlastTouchEventY touchEventYevent?.let {rawTouchEventX it.xrawTouchEventY it.ytouchEventX it.x - radiustouchEventY it.y - radius}ObjectAnimator.ofFloat(this, rippleValue, 0f, 1f).apply {duration 1000start()}val path Path().apply {moveTo(lastTouchEventX, lastTouchEventY)quadTo(lastTouchEventX,lastTouchEventY,touchEventX,touchEventY)}val oaMoving ObjectAnimator.ofFloat(ballContainerIv, x, y, path)val oaRotating ObjectAnimator.ofFloat(ballContainerIv, rotation, 0f, 360f)AnimatorSet().apply {duration 1000playTogether(oaMoving, oaRotating)start()}return super.onTouchEvent(event)}fun setRippleValue(currentValue: Float) {rippleRadius currentValue * radiusrippleAlpha ((1 - currentValue) * 255).toInt()invalidate()}override fun onDraw(canvas: Canvas?) {super.onDraw(canvas)ripplePaint.alpha rippleAlpha//draw ripple for click eventcanvas?.drawCircle(rawTouchEventX, rawTouchEventY, rippleRadius, ripplePaint)}
}简单概括一下首先我们会动态的生成一个View将其背景设置为我们刚刚绘制的BallDrawable()来构成一个篮球。然后通过onTouchEvent()方法来获取到用户的点击坐标再通过属性动画让球滚动到该坐标。更多额外代码请查看 Github Drawable_Leaning 之篮球滚动。https://github.com/JereChen11/Drawable_Learning/tree/main/app/src/main/java/com/drawable/learning/fragment/custom/ball总结通过这篇文章我们学习了几种常见的Drawable也学习了自定义Drawable我们知道Drawable只用来处理绘制的相关工作而不处理与用户的交互事件。所以在我们复杂的自定义View中我们可以将其进行拆分像一些背景、装饰等完全就可以采取自定义Drawable来进行绘制。这样就能让我们复杂的自定义View变得图层更加层次清晰代码可读性大大提升。如果你想参考文章中所有源码可以点击 Github Drawable_Learning 进行查看欢迎你给我点个小星星。https://github.com/JereChen11/Drawable_Learning