如何在Android drawable中传递参数

4

如何在Android drawable中传递参数。

如何实现下面提到的代码:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="rectangle">
            <solid android:color="{colorcode}" />
            <stroke android:width="1sp" android:color="{colorcode}" />
            <corners android:radius="15dp" />
        </shape>
    </item>
</selector>

然后从XML文件中获取这个Drawable,并传递参数的值。类似于:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="50dp"
    android:layout_marginRight="5dp"
    android:layout_weight="1"
    android:background="@drawable/rounded,{colorCode}"
    android:gravity="center"/>

据我所知,你不能像上述的方式从XML中传递值 - 如果有人能回答这个问题,我会很期待。但是,你可以通过编程的方式将值/参数传递到Android drawable中,例如在问题中要求更改形状内部的实心颜色。那么这样做能解决或帮助你想要完成的目标吗? - SaadAAkash
我们如何在Android中以编程方式传递参数到可绘制对象?我想要实现两个功能:1. 更改半径和2. 更改填充颜色。 - Anupam Somani
已发布答案。请检查并告诉我是否有帮助。 - SaadAAkash
有没有办法可以通过样式标签来改变圆角?例如: rectangle 15dp ``` - Anupam Somani
请检查我的答案,如果您将可绘制对象显式转换为GradientDrawable,则会获得大量的半径属性(cornerRadius、cornerRadii等)。我已编辑答案并添加了一些内容。 - SaadAAkash
2个回答

4
你可以使用数据绑定和BindingAdapter来完成。我将使用Kotlin展示一个例子。
  1. 为了启用数据绑定,请在Gradle中添加以下内容:
apply plugin: 'kotlin-kapt' // only need when You use Kotlin

...
android {  
    ...
    buildFeatures {
        dataBinding = true
    }
    ...
}
  1. 创建BindingAdapter(我将其添加到MainActivity.kt中以简化代码):
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.databinding.BindingAdapter
import androidx.databinding.DataBindingUtil
import com.myniprojects.teststackjava.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity()
{
    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?)
    {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(
                this, R.layout.activity_main)
    }
}

// Here is binding adapter. It takes 2 colors, background and stroke. If You want more customization add more parameters 
@BindingAdapter(value = ["colorStoke", "colorBack"], requireAll = true)
fun setBackground(textView: TextView, @ColorRes colorStoke: Int, @ColorRes colorBack: Int)
{
    val stroke = ContextCompat.getColor(textView.context, colorStoke)
    val back = ContextCompat.getColor(textView.context, colorBack)

    val gd = GradientDrawable()

    // setting background
    gd.colors = intArrayOf(
            back,
            back
    )

    // here change parameters as You want
    gd.gradientType = GradientDrawable.LINEAR_GRADIENT

    gd.shape = GradientDrawable.RECTANGLE
    gd.cornerRadius = 15f;
    
    // setting stroke width and color 
    gd.setStroke(5, stroke)

    textView.background = gd
}

要自定义GradientDrawable,请查看此文档

  1. 在您的MainActivity.xml文件中执行以下操作:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <import type="com.myniprojects.teststackjava.R" />
    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <TextView
            android:id="@+id/txtVTest"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="20dp"
            android:gravity="center"
            android:text="TextView Text"
            android:textSize="40sp"
            app:colorBack="@{R.color.color_back}"
            app:colorStoke="@{R.color.color_stroke}"/>

    </FrameLayout>
</layout>

我正在导入R,以便能够传递资源。 (也许有其他方法可以不导入,但我无法像@{@color/name}一样传递颜色,所以我只好导入它)。在您的TextView中,您必须调用app:colorBackapp:colorStoke,并使用您要设置为描边和背景的颜色。
当然,在res/values/colors中:
<color name="color_stroke">#B71C1C</color>
<color name="color_back">#33691E</color>
  1. 最终效果展示,以证明一切正常运作:

enter image description here


要生成ActivityMainBinding,您首先需要创建一个数据绑定布局。如果您已正确添加依赖项以快速将布局转换为数据绑定布局,请使用:Alt + EnterConvert to data binding layout*。

enter image description here

所以你的布局应该长这样:

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

    </data>

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

    </FrameLayout>
</layout>

现在,重新构建您的项目后,您应该得到ActivityMainBinding类。


如果我想在所有片段中使用,我必须在每个片段中重复第2步和第3步吗? - Anupam Somani
不需要。你只需要创建一个BindingAdapter,然后就可以在任何地方使用它。所以在第二个片段中,你只需要创建一个TextView并添加app:colorBack="@{R.color.some_color}"app:colorStoke="@{R.color.another_color}"。就这些了。 - iknow
databinding.ActivityMainBinding类不会自动生成,我需要做些什么。 - Anupam Somani
@AnupamSomani 第一种情况 - 您没有正确添加依赖项。在您完成后,请重新构建您的项目。第二种情况 - 您的主活动根元素不是布局。要做到这一点,只需单击alt + enter,您将看到“转换为数据绑定布局”选项。在您完成后,您应该会看到绑定类。 - iknow
@AnupamSomani 我已经编辑了我的答案,更好地解释了如何生成 ActivityMainBinding 类。 - iknow

1

ShapeDrawable, GradientDrawableColorDrawableDrawable的直接子类。您可以将可绘制对象显式转换为上述任何子类,以调用setColor(int).paint等方法/属性访问,如下所示:

val drawable: Drawable = (yourDrawable as StateListDrawable).getStateDrawable(0)
when (drawable) {
    is ShapeDrawable -> {
        (drawable as ShapeDrawable).paint.color = ContextCompat.getColor(mContext, R.color.colorToSet)
    }
    is GradientDrawable -> {
        (drawable as GradientDrawable).setColor(ContextCompat.getColor(mContext, R.color.colorToSet))
        (drawable as GradientDrawable).cornerRadius = 2.0F
    }
    is ColorDrawable -> {
        (drawable as ColorDrawable).color = ContextCompat.getColor(mContext, R.color.colorToSet)
    }
}

需要注意的事项:

你可以在此可绘制对象上使用 mutate() 方法,因为它返回具有已复制常量可绘制状态的相同实例。mutate() 方法不与任何其他可绘制对象共享其状态,因此如果您想将可绘制对象保持原样,然后在许多地方使用不同的状态,它非常有帮助。这里是一个关于如何使用 mutate() 方法并保存状态的好答案。(链接)

另外,正如您在评论中提到的那样,要同时更改可绘制对象半径,请使用 cornerRadii。根据给定嵌入链接中的文档,该方法返回每个角落的半径。对于每个角落,数组包含 2 个值:[X_radius,Y_radius]。角落按顺序排列为 top-lefttop-rightbottom-rightbottom-left


我正在获取一个StateListDrawable作为drawable。那么现在的解决方案是什么? - Anupam Somani
使用getStateDrawable()方法获取形状可绘制对象,然后应用我的答案。请参考以下链接查看其文档:https://developer.android.com/reference/android/graphics/drawable/StateListDrawable#getStateDrawable(int) - SaadAAkash
此外,如果需要支持至 API 级别 21,请使用 applyTheme()。链接:https://developer.android.com/reference/android/graphics/drawable/StateListDrawable#applyTheme(android.content.res.Resources.Theme) - SaadAAkash
1
@AnupamSomani编辑了我的答案,展示了如何获取StateListDrawable中的drawable。请检查。 - SaadAAkash

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接