用基本形状画一根香烟

Drawing a cigarette with basic shapes

我正在尝试用 "ash" 视图占 "smokeable" 视图的百分比绘制香烟。它必须是一个百分比,因为我将使用一个滑块来改变 "ash" 视图的百分比。

这是我想要的:

这是我的代码:

<LinearLayout
    android:id="@+id/cigarette"
    android:layout_width="match_parent"
    android:layout_height="20dp"
    android:layout_marginBottom="20dp"
    android:orientation="horizontal">

    <View
        android:id="@+id/butt"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".25"
        android:background="#ff7700" />

    <LinearLayout
        android:id="@+id/smokeable"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".75"
        android:background="#cccccc"
        android:orientation="horizontal">

        <View
            android:id="@+id/ash"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight=".33"
            android:background="#777777"
            android:gravity="right" />

    </LinearLayout>

</LinearLayout>

在这个例子中,我希望烟灰占可吸烟视图的 33%。我不明白为什么这不起作用。任何帮助将不胜感激。

鉴于所有三个元素都在一条直线上,您只需要一个 LinearLayout 父元素:

<LinearLayout
    android:id="@+id/cigarette"
    android:layout_width="match_parent"
    android:layout_height="20dp"
    android:orientation="horizontal">

    <View
        android:id="@+id/butt"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="25"
        android:background="#ff7700" />

    <View
        android:id="@+id/smokeable"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="65"
        android:background="#cccccc" />

    <View
        android:id="@+id/ash"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="10"
        android:background="#777777" />

</LinearLayout>

我认为 最简单 最干净的方法是为您定制 View "cigarette",它将由 filter/smokeable/ash 组成, 每个部分都有不同的颜色。

因此声明一些样式属性,比如 attrs.xml

<resources>
    <declare-styleable name="CigaretteView">
        <attr name="filterColor" format="color" />
        <attr name="smokeableColor" format="color" />
        <attr name="ashColor" format="color" />
        <attr name="filterWidth" format="dimension" />
    </declare-styleable>
</resources>

Viewclass本身:

class CigaretteView(context: Context, attrs: AttributeSet?) : View(context, attrs) {

    companion object {
        private val DEFAULT_FILTER_COLOR = 0xffbbaa00.toInt()
        private val DEFAULT_SMOKABLE_COLOR = 0xffffffff.toInt()
        private val DEFAULT_ASH_COLOR = 0xff888888.toInt()
        private val DEFAUALT_FILTER_WIDTH_PX = 120
    }

    private val filterPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val smokeablePaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val ashPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var filterWidthPx = DEFAUALT_FILTER_WIDTH_PX

    private val rect = Rect()

    var smokedPercent = 0f
    set(value) {
        field = value
        invalidate()
    }

    init {
        val a = context.theme.obtainStyledAttributes(attrs, R.styleable.CigaretteView, 0, 0)

        var filterColor = DEFAULT_FILTER_COLOR
        var smokeableColor = DEFAULT_SMOKABLE_COLOR
        var ashColor = DEFAULT_ASH_COLOR

        try {
            filterColor = a.getColor(R.styleable.CigaretteView_filterColor, filterColor)
            smokeableColor = a.getColor(R.styleable.CigaretteView_smokeableColor, smokeableColor)
            ashColor = a.getColor(R.styleable.CigaretteView_ashColor, ashColor)
            filterWidthPx = a.getDimensionPixelSize(R.styleable.CigaretteView_filterWidth, filterWidthPx)
        } finally {
            a.recycle()
        }

        filterPaint.color = filterColor
        smokeablePaint.color = smokeableColor
        ashPaint.color = ashColor
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)

        rect.set(0, 0, filterWidthPx, height)
        canvas.drawRect(rect, filterPaint)

        val leftOver = (width - filterWidthPx).toFloat()
        val ashStartPoint = (width - leftOver * smokedPercent).toInt()

        rect.set(filterWidthPx, 0, ashStartPoint, height)
        canvas.drawRect(rect, smokeablePaint)

        rect.set(ashStartPoint, 0, width, height)
        canvas.drawRect(rect, ashPaint)
    }
}

您的 xml 布局将简化为

<com.example.CigaretteView
    android:id="@+id/cigaretteView"
    android:layout_width="match_parent"
    android:layout_height="48dp"
    app:filterWidth="48dp"
    app:filterColor="#ffbbaa00"
    app:smokeableColor="#ffffffff"
    app:ashColor="#ff888888"/>

然后您可以简单地指定灰分百分比值

myCigaretteView.smokedPercent = percent

其中 percent 从 0(香烟完好无损)到 1(香烟完全吸完)。

里面的LinearLayout需要另一个childView作为香烟的not-yet-consumed部分。在您的示例中,它的 layout_weight 必须为 1 - 0.33 = 0.67(两个兄弟姐妹的权重总和必须始终为 1)。

<LinearLayout
    android:id="@+id/cigarette"
    android:layout_width="match_parent"
    android:layout_height="20dp"
    android:layout_below="@+id/titleText"
    android:orientation="horizontal">

    <View
        android:id="@+id/butt"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".25"
        android:background="#ff7700" />

    <LinearLayout
        android:id="@+id/smokeable"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight=".75"
        android:orientation="horizontal">

        <View
            android:id="@+id/rest"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:background="#cccccc"
            android:layout_weight=".67"
            />
        <View
            android:id="@+id/ash"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight=".33"
            android:background="#777777"
            android:gravity="right" />

    </LinearLayout>

</LinearLayout>

请注意,虽然这会修复您的代码,但 "nested weighted LinearLayout" 方法并不是一个好的方法,即使现代设备不应该 运行 出现性能问题。

涉及自定义 View 要好得多,因为您只需要一个 View,而不是两个 ViewGroup 加上三个 View。您有一种方法可以轻松更改百分比,而且所有计算和绘图部分都被很好地封装了。

这是我能想到的最简单的东西,它可以满足您的需要:

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="20dp"
    android:background="#777777"
    android:orientation="horizontal">

    <View
        android:id="@+id/butt"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="0.25"
        android:background="#ff7700"/>

    <View
        android:id="@+id/smokeable"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="0.75"
        android:background="#cccccc"
        android:transformPivotX="0px"/>

</LinearLayout>

现在您可以使用任意百分比调用 smokeable.setScaleX() 以获得所需的结果。