如何在 Android 中动态合并两个 Drawable?

How to merge two Drawables dynamically in Android?

所以我有两个不同的 Drawables,我需要合并它们并在运行时得到一个 Drawable。我希望第一个 Drawable 位于顶部,另一个位于底部。我遇到了 LayerDrawable,它看起来正是我所需要的,但我在尝试安排 Drawables.

时遇到了问题

所以我有一个 ImageButton,它是 48x48 dp,这就是最终 Drawable 的位置。第一个 Drawable 是加号按钮 (20x20 dp),第二个是加号按钮下方的小点 (4x4 dp)。

加号按钮 Drawable 是使用字体字形加载的。我正在使用此 xml 片段创建点按钮 Drawable

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" 
       android:shape="oval">
    <solid
        android:color="@color/white_40"/>
    <size
        android:width="4dp"
        android:height="4dp"/>
</shape>

我的第一个方法是将 Drawables 添加到 LayerDrawable,但是当我这样做时,xml 中指定的点的 width/height 属性被忽略,它会延伸到覆盖加号图标。

LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] {plusIcon, dotIcon});

以上结果为:



我尝试的第二种方法是使用 setLayerInset 尝试定位两个 Drawables

    LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] {plusIcon, dotIcon});
    finalDrawable.setLayerInset(0, 0, 0, 0, 0);
    finalDrawable.setLayerInset(1, dp(22), dp(44), dp(22), 0);

上面的代码片段最终将点放在了正确的位置,但它也开始影响加号按钮的位置和大小,最终看起来像这样:

但我真正想要的是在 ImageButton 的中央加上加号按钮,加号图标就在它的正下方。有谁知道我哪里出错了以及如何正确放置两个可绘制对象?

PS:我的应用程序支持 API 15+,所以我不能使用 LayerDrawable 的 API 中的一堆方法,例如 setLayerGravity、`setPaddingMode等

编辑

此代码适用于 23 以下的 API 个级别:

ImageButton button = (ImageButton) findViewById(R.id.button);

Drawable plusIcon = ContextCompat.getDrawable(this, R.drawable.plus);
Drawable dotIcon = ContextCompat.getDrawable(this, R.drawable.oval);

int horizontalInset = (plusIcon.getIntrinsicWidth() - dotIcon.getIntrinsicWidth()) / 2;

LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] {plusIcon, dotIcon});
finalDrawable.setLayerInset(0, 0, 0, 0, dotIcon.getIntrinsicHeight());
finalDrawable.setLayerInset(1, horizontalInset, plusIcon.getIntrinsicHeight(), horizontalInset, 0);

button.setImageDrawable(finalDrawable);

原创

以下代码适用于我:

ImageButton button = (ImageButton) findViewById(R.id.button);

Drawable plusIcon = ContextCompat.getDrawable(this, R.drawable.plus);
Drawable dotIcon = ContextCompat.getDrawable(this, R.drawable.oval);

LayerDrawable finalDrawable = new LayerDrawable(new Drawable[] {plusIcon, dotIcon});
finalDrawable.setLayerInsetBottom(0, dotIcon.getIntrinsicHeight());
finalDrawable.setLayerGravity(1, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL);

button.setImageDrawable(finalDrawable);

这会产生以下结果 ui:

我就是这样解决的:

final View view = findViewById(R.id.view_in_layout);

Drawable bottom = ContextCompat.getDrawable(context, R.drawable.ic_bottom);
Drawable top = ContextCompat.getDrawable(context, R.drawable.ic_top);

//bottom = index 0, top = index 1
final LayerDrawable layer = new LayerDrawable(new Drawable[]{bottom, top});

view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
    @Override
    public void onLayoutChange(View v, int left, int top, int right, int bottom,
                  int oldLeft, int oldTop, int oldRight, int oldBottom) {
        //get the real height after it is calculated
        int height = view.getHeight();
        //set the height half of the available space for example purposes,
        //the drawables will have half of the available height
        int halfHeight = height / 2;
        //the bottom drawable need to start when the top drawable ends
        layer.setLayerInset(0, 0, halfHeight, 0, 0);
        //the top drawable need to end before the bottom drawable starts
        layer.setLayerInset(1, 0, 0, 0, halfHeight);

        view.setBackground(layer);
        view.removeOnLayoutChangeListener(this);
    }
});

您可以为可绘制对象选择不同的尺寸或使用索引移动它们。例如,我使用 View.OnLayoutChangeListener(...) 来等待视图的当前可用高度