从状态列表中获取特定的可绘制对象

14

我有一个状态列表Drawable,我想从状态列表Drawable中获取特定的Drawable:

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


    <item kplus:key_type_space_alt="true" android:state_pressed="true" android:drawable="@drawable/space_1_pressed" />
    <item kplus:key_type_space_alt="true" android:drawable="@drawable/space_1_normal" />

    <!-- TopNav keys. -->

    <item kplus:key_type_topnav="true" android:state_pressed="true" android:drawable="@drawable/tab_down" />
    <item kplus:key_type_topnav="true" android:state_selected="true" android:drawable="@drawable/tab_down" />
    <item kplus:key_type_topnav="true" android:drawable="@drawable/tab_normal" />

    <!-- TopRow keys. -->

    <item kplus:key_type_toprow="true" android:state_pressed="true" android:drawable="@drawable/numeric_presseed" />
    <item kplus:key_type_toprow="true" android:drawable="@drawable/numeric_normal" />
</selector>

我为每个键选择正确的可绘制状态,类似于这样:

if (keyIsNumbers) {
    if (KPlusInputMethodService.sNumbersState == 2) {
        drawableState = mDrawableStatesProvider.KEY_STATE_TOPNAV_CHECKED;
    }
}

现在状态是这样定义的:

KEY_STATE_TOPNAV_NORMAL = new int[] {keyTypeTopNavAttrId};
KEY_STATE_TOPNAV_PRESSED = new int[] {keyTypeTopNavAttrId, android.R.attr.state_pressed};
KEY_STATE_TOPNAV_CHECKED = new int[] {keyTypeTopNavAttrId, android.R.attr.state_selected};

现在我的问题是如何提取每个状态的正确可绘制对象?我需要获取可绘制对象的9patch填充,因为如果状态在9patch上有不同的填充,则它将仅获得顶部可绘制对象的填充,并且我希望手动为每个键设置填充(drawable.getPadding(rect))。


无法获取组成StateListDrawable的Drawables。 - pskink
我在想我是否可以使用stateDrawable[0]的id属性来获取它们,你确定不可能吗?:( - Tazz
你需要ID干什么?而且我不明白为什么你需要访问特定的Drawable... - pskink
我使用ben75的解决方案解决了它,现在运行良好,但我会继续监视以查看是否有任何变化。 - Tazz
这个答案可以帮助你,并且不使用任何公共 API:https://dev59.com/questions/_mIj5IYBdhLWcg3wzIFo#25038966 - Radost
2个回答

23

过时的答案。 请查看VinceStyling的答案,使用SDK 29+的更好解决方案。


没有公共API可以从状态中获取可绘制对象。

StateListDrawable中有一些方法,但它们被@hide修饰,并带有注释"pending API council"

您可以通过反射调用它们...但这是自担风险!!!(它可能会在将来的版本中发生变化)

这些方法包括:

以下是操作步骤(省略了异常情况):

int[] currentState = view.getDrawableState();
StateListDrawable stateListDrawable = (StateListDrawable)view.getBackground();
Method getStateDrawableIndex = StateListDrawable.class.getMethod("getStateDrawableIndex", int[].class);
Method getStateDrawable = StateListDrawable.class.getMethod("getStateDrawable", int.class);
int index = (int) getStateDrawableIndex.invoke(stateListDrawable,currentState);
Drawable drawable = (Drawable) getStateDrawable.invoke(stateListDrawable,index);

目前运行得非常好。我会继续监督,看看未来是否会有变化。 - Tazz
谢谢,你真是个天才!如果可以的话,我可以给你100分作为这些答案的奖励。 - Sushant Gosavi
4
提醒一下,这个在Android P中不起作用,参考 https://developer.android.com/preview/restrictions-non-sdk-interfaces.html。 - behelit
2
API 终于在 Android Q 中公开。 - DrBreakalot
工作得非常好!不过使用非公共API还是有点令人担忧。对于反射调用的一个小建议是使用getDeclaredMethod(...)而不仅仅是getMethod(...)。这可能会抛出NoSuchMethodException异常(不确定这是否是@hide的副作用)。 - Ani Fichadia
显示剩余3条评论

1

现在获取特定的drawable的方法不需要使用反射了:

StateListDrawable pressedDrawable = (StateListDrawable) this.mPressed;
final int state = android.R.attr.state_pressed;
final int index = pressedDrawable.findStateDrawableIndex(new int[]{state});
if (index >= 0) {
    Drawable drawable = pressedDrawable.getStateDrawable(index);
    if (drawable instanceof GradientDrawable) {
        ((GradientDrawable) drawable).setCornerRadius(mRoundConerRadius);
    }
}

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