A solução indicada por @ (Ted Hopp) funciona, mas precisa de uma pequena correção: no seletor, os estados do item precisam de um prefixo "app:"; caso contrário, o inflador não reconhecerá corretamente o espaço para nome e falhará silenciosamente; pelo menos é o que acontece comigo.
Permita-me relatar aqui toda a solução, com mais alguns detalhes:
Primeiro, crie o arquivo "res / values / attrs.xml":
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="food">
<attr name="state_fried" format="boolean" />
<attr name="state_baked" format="boolean" />
</declare-styleable>
</resources>
Em seguida, defina sua classe personalizada. Por exemplo, pode ser uma classe "FoodButton", derivada da classe "Button". Você terá que implementar um construtor; implemente este, que parece ser o usado pelo inflador:
public FoodButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
No topo da classe derivada:
private static final int[] STATE_FRIED = {R.attr.state_fried};
private static final int[] STATE_BAKED = {R.attr.state_baked};
Além disso, suas variáveis de estado:
private boolean mIsFried = false;
private boolean mIsBaked = false;
E alguns levantadores:
public void setFried(boolean isFried) {mIsFried = isFried;}
public void setBaked(boolean isBaked) {mIsBaked = isBaked;}
Em seguida, substitua a função "onCreateDrawableState":
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 2);
if (mIsFried) {
mergeDrawableStates(drawableState, STATE_FRIED);
}
if (mIsBaked) {
mergeDrawableStates(drawableState, STATE_BAKED);
}
return drawableState;
}
Finalmente, a peça mais delicada deste quebra-cabeça; o seletor que define o StateListDrawable que você usará como plano de fundo para seu widget. Este é o arquivo "res / drawable / food_button.xml":
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.mydomain.mypackage">
<item
app:state_baked="true"
app:state_fried="false"
android:drawable="@drawable/item_baked" />
<item
app:state_baked="false"
app:state_fried="true"
android:drawable="@drawable/item_fried" />
<item
app:state_baked="true"
app:state_fried="true"
android:drawable="@drawable/item_overcooked" />
<item
app:state_baked="false"
app:state_fried="false"
android:drawable="@drawable/item_raw" />
</selector>
Observe o prefixo "app:", enquanto que com os estados padrão do android você usaria o prefixo "android:". O espaço para nome XML é crucial para uma interpretação correta do inflador e depende do tipo de projeto no qual você está adicionando atributos. Se for um aplicativo, substitua com.mydomain.mypackage pelo nome do pacote real do seu aplicativo (nome do aplicativo excluído). Se for uma biblioteca, você deverá usar "http://schemas.android.com/apk/res-auto" (e usar o Tools R17 ou posterior) ou obterá erros de tempo de execução.
Algumas notas:
Parece que você não precisa chamar a função "refreshDrawableState", pelo menos a solução funciona bem como está, no meu caso
Para usar sua classe personalizada em um arquivo xml de layout, você precisará especificar o nome completo (por exemplo, com.mydomain.mypackage.FoodButton)
Você pode misturar estados padrão (por exemplo, android: pressionado, android: ativado, android: selecionado) com estados personalizados, a fim de representar combinações de estado mais complicadas