如何在 Switch/SwitchCompat 按钮中设置宽度和跟踪文本并实现此结果? (附图片和GIF)

Posted

技术标签:

【中文标题】如何在 Switch/SwitchCompat 按钮中设置宽度和跟踪文本并实现此结果? (附图片和GIF)【英文标题】:How to set width and track text in a Switch/SwitchCompat button and achieve this result? (Image and GIF attached) 【发布时间】:2019-02-03 11:04:13 【问题描述】:

我需要像这样在我的应用中实现一个按钮

我使用了 SwitchCompat 按钮,但我到达的最近的位置是,

有两个主要问题:

1 - 当屏幕尺寸改变(drawable 被截断,变得太小等)时,按钮的宽度不能正确调整,宽度正确占据父视图很重要(a包围它的小线性布局)

2 -我无法理解如何获得 Switch Track 中的字母

是否可以通过开关按钮实现此结果?如何? 我应该使用另一个视图而不是切换按钮吗?哪一个?

我偶然发现了这个项目,但它似乎有点过时了

https://github.com/pellucide/android-Switch-Demo-pre-4.0/tree/master/

【问题讨论】:

我不认为你可以使用默认的Switch 组件轻松实现这一点。您可能应该使用自定义视图方法 @MatPag 我应该怎么做?我不知道 创建自定义视图并不容易。至少需要一些框架技能。您可以尝试从以下内容开始:github.com/GwonHyeok/StickySwitch 作为基本视图,然后根据您的需要对其进行更改 【参考方案1】:

例如:

class SwitchCompatEx : SwitchCompat 

    companion object 

        val TRACK_COLOR = 0xFFFFFFFF.toInt()
        val TRACK_STROKE_WIDTH = 2f.dp2Px.toInt()
        val TRACK_STROKE_COLOR = 0xFF00A1FF.toInt()
        val TRACK_LABEL_COLOR = 0xFF00A1FF.toInt()
        val TRACK_LABEL_SIZE = 14f.sp2Px

        val THUMB_COLOR = 0xFF00A1FF.toInt()
        val THUMB_LABEL_COLOR = 0xFFFFFFFF.toInt()
        val THUMB_LABEL_SIZE = 14f.sp2Px

        fun drawLabel(canvas: Canvas,
                      bounds: Rect,
                      paint: Paint,
                      text: CharSequence?) 
            text ?: return

            val tb = RectF();
            tb.right = paint.measureText(text, 0, text.length)
            tb.bottom = paint.descent() - paint.ascent()
            tb.left += bounds.centerX() - tb.centerX()
            tb.top += bounds.centerY() - tb.centerY() - paint.ascent()

            canvas.drawText(text.toString(), tb.left, tb.top, paint)
        

        private inline val Float.sp2Px
            get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                    this,
                    Resources.getSystem().displayMetrics)

        private inline val Float.dp2Px
            get() = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                    this,
                    Resources.getSystem().displayMetrics)
    

    private val trackLabelPaint = Paint().apply 
        isAntiAlias = true
        textSize = TRACK_LABEL_SIZE
        color = TRACK_LABEL_COLOR
    

    private val thumbLabelPaint = Paint().apply 
        isAntiAlias = true
        textSize = THUMB_LABEL_SIZE
        color = THUMB_LABEL_COLOR
    

    private val thumbLabel
        get () = if (isChecked) textOn else textOff

    constructor(context: Context?) : super(context)
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr)

    init 
        background = null
        trackDrawable = TrackDrawable()
        thumbDrawable = ThumbDrawable()
    

    override fun onSizeChanged(w: Int,
                               h: Int,
                               oldw: Int,
                               oldh: Int) 
        super.onSizeChanged(w, h, oldw, oldh)

        (trackDrawable as GradientDrawable).setSize(w, h)
        (thumbDrawable as GradientDrawable).setSize(w / 2, h)
    

    inner class TrackDrawable : GradientDrawable() 

        private val textOffBounds = Rect()
        private val textOnBounds = Rect()

        init 
            setColor(TRACK_COLOR)
            setStroke(TRACK_STROKE_WIDTH, TRACK_STROKE_COLOR)
        

        override fun onBoundsChange(r: Rect) 
            super.onBoundsChange(r)

            cornerRadius = r.height() / 2f

            textOffBounds.set(r)
            textOffBounds.right /= 2

            textOnBounds.set(textOffBounds)
            textOnBounds.offset(textOffBounds.right, 0)
        

        override fun draw(canvas: Canvas) 
            super.draw(canvas)

            drawLabel(canvas, textOffBounds, trackLabelPaint, textOff)
            drawLabel(canvas, textOnBounds, trackLabelPaint, textOn)
        
    

    inner class ThumbDrawable : GradientDrawable() 

        private val thumbLabelBounds = Rect()

        init 
            setColor(THUMB_COLOR)
        

        override fun onBoundsChange(r: Rect) 
            super.onBoundsChange(r)

            cornerRadius = r.height() / 2f

            thumbLabelBounds.set(r)
        

        override fun draw(canvas: Canvas) 
            super.draw(canvas)

            drawLabel(canvas, thumbLabelBounds, thumbLabelPaint, thumbLabel)
        
    

...

<demo.sodemos.SwitchCompatEx
    android:layout_
    android:layout_
    android:layout_gravity="center"
    android:minHeight="40dp"
    android:textOff="M"
    android:textOn="F"
    app:switchMinWidth="100dp" />

...

还可以查看此Custom view components 教程。

希望对你有帮助

【讨论】:

非常感谢您的详细回答!将尝试并提供一些反馈! :) 我试过了,它在带有 api 17 的模拟器中完美运行!问题是,至少对于 api 24 和 27,它不能正确显示,拇指和轨道根本不显示(唯一显示的是设置的 texton 和 textoff)。有什么建议?谢谢:) 很有趣和奇怪的事实,自定义视图类在所有 api 版本中正确显示后我在同一个活动中按随机编辑文本视图并打开软键盘' 嗯,有趣,听起来可绘制边界有问题。我稍后会检查它。谢谢 是的,这似乎是问题所在,我尝试调试它但无法弄清楚...如果您以编程方式设置新高度(在创建所有视图之后)它也可以工作。我试图改变 onStart 调用中的高度,但它没有改变任何东西......再次感谢您的帮助,如果您找出问题所在,我很乐意看到解释!【参考方案2】:

在搞砸之后,我通过在每个可绘制对象的 onDraw 函数末尾添加以下两行来修复渲染问题

    invalidate()
    requestLayout()

【讨论】:

以上是关于如何在 Switch/SwitchCompat 按钮中设置宽度和跟踪文本并实现此结果? (附图片和GIF)的主要内容,如果未能解决你的问题,请参考以下文章

如何在Postgres中按年月按最大(日期)组获取行?

如何按时间顺序列出文件?

如何在asp.net的GridView中按列名而不是按索引获取单元格值

如何在 django 中按日期范围过滤记录?

如何实现对文件夹的按大小排序

如何在 solr 查询中排序之前按分数限制