Définition de attrs personnalisés
-
27-09-2019 - |
Question
Je dois mettre en œuvre mes propres attributs comme dans com.android.R.attr
rien trouvé dans la documentation officielle donc j'ai besoin d'informations sur la façon de définir ces attrs et comment les utiliser dans mon code.
La solution
À l'heure actuelle, la meilleure documentation est la source. Vous pouvez jeter un coup d'oeil ici (attrs. xml) .
Vous pouvez définir des attributs dans l'élément <resources>
supérieur ou à l'intérieur d'un élément de <declare-styleable>
. Si je vais utiliser un attr dans plus d'un endroit où je l'ai mis dans l'élément racine. Notez tous les attributs partagent le même espace de noms global. Cela signifie que même si vous créez un nouvel attribut à l'intérieur d'un élément de <declare-styleable>
il peut être utilisé en dehors de celui-ci et vous ne pouvez pas créer un autre attribut avec le même nom d'un type différent.
Un élément de <attr>
a deux attributs xml name
et format
. name
vous permet de l'appeler quelque chose et voilà comment vous finissez par y faire référence dans le code, par exemple, R.attr.my_attribute
. L'attribut format
peut avoir des valeurs différentes selon le « type » de l'attribut que vous voulez.
- référence - si elle fait référence à un autre identifiant de ressource (par exemple, "@ couleur / my_color", "@ layout / my_layout")
- couleur
- boolean
- dimension
- float
- entier chaîne
- fraction
- ENUM - normalement défini implicitement
- flag - normalement défini implicitement
Vous pouvez définir le format à plusieurs types en utilisant |
, par exemple, format="reference|color"
.
attributs de enum
peuvent être définis comme suit:
<attr name="my_enum_attr">
<enum name="value1" value="1" />
<enum name="value2" value="2" />
</attr>
attributs de flag
sont similaires, sauf les valeurs doivent être définies afin qu'ils puissent être peu ORED ensemble:
<attr name="my_flag_attr">
<flag name="fuzzy" value="0x01" />
<flag name="cold" value="0x02" />
</attr>
En plus des attributs, il est l'élément <declare-styleable>
. Cela vous permet de définir des attributs d'une vue personnalisée peut utiliser. Vous faites cela en spécifiant un élément <attr>
, si elle a déjà été défini, vous ne spécifiez pas le format
. Si vous souhaitez réutiliser un androïde attr, par exemple, android:. Gravité, alors vous pouvez le faire dans le name
, comme suit
Un exemple d'une vue personnalisée <declare-styleable>
:
<declare-styleable name="MyCustomView">
<attr name="my_custom_attribute" />
<attr name="android:gravity" />
</declare-styleable>
Lorsque vous définissez vos attributs personnalisés en XML sur votre vue personnalisée que vous devez faire quelques choses. D'abord, vous devez déclarer un espace de noms pour trouver vos attributs. Pour ce faire, sur l'élément de mise en page de racine. Normalement, il n'y a que xmlns:android="http://schemas.android.com/apk/res/android"
. Vous devez maintenant ajouter également xmlns:whatever="http://schemas.android.com/apk/res-auto"
.
Exemple:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:whatever="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<org.example.mypackage.MyCustomView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
whatever:my_custom_attribute="Hello, world!" />
</LinearLayout>
Enfin, l'accès à ce point de vue attribut personnalisé vous font normalement dans le constructeur de votre commande comme suit.
public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);
String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);
//do something with str
a.recycle();
}
La fin. :)
Autres conseils
La réponse de Qberticus est bonne, mais un détail utile manque. Si vous implémentez ces remplacer dans une bibliothèque:
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
avec:
xmlns:whatever="http://schemas.android.com/apk/res-auto"
Sinon, l'application qui utilise la bibliothèque aura des erreurs d'exécution.
La réponse ci-dessus couvre tout en détail, à part deux ou trois choses.
Tout d'abord, s'il n'y a pas de styles, la signature de la méthode de (Context context, AttributeSet attrs)
sera utilisé pour instancier la préférence. Dans ce cas, il suffit d'utiliser context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)
pour obtenir le TypedArray.
En second lieu, il ne couvre pas comment traiter les ressources plaurals (chaînes de quantité). Ceux-ci ne peuvent pas être traitées en utilisant TypedArray. Voici un extrait de code de mon SeekBarPreference qui définit le résumé de la préférence mise en forme de sa valeur en fonction de la valeur de la préférence. Si le fichier XML pour les jeux de préférence Android: résumé à une chaîne de texte ou une chaîne resouce la valeur de la préférence est formatée dans la chaîne (il devrait avoir% d en elle, pour ramasser la valeur). Si android. Résumé est réglé sur une ressource de plaurals, puis qui est utilisé pour formater le résultat
// Use your own name space if not using an android resource.
final static private String ANDROID_NS =
"http://schemas.android.com/apk/res/android";
private int pluralResource;
private Resources resources;
private String summary;
public SeekBarPreference(Context context, AttributeSet attrs) {
// ...
TypedArray attributes = context.obtainStyledAttributes(
attrs, R.styleable.SeekBarPreference);
pluralResource = attrs.getAttributeResourceValue(ANDROID_NS, "summary", 0);
if (pluralResource != 0) {
if (! resources.getResourceTypeName(pluralResource).equals("plurals")) {
pluralResource = 0;
}
}
if (pluralResource == 0) {
summary = attributes.getString(
R.styleable.SeekBarPreference_android_summary);
}
attributes.recycle();
}
@Override
public CharSequence getSummary() {
int value = getPersistedInt(defaultValue);
if (pluralResource != 0) {
return resources.getQuantityString(pluralResource, value, value);
}
return (summary == null) ? null : String.format(summary, value);
}
- Ceci est simplement donné à titre d'exemple, cependant, si vous voulez sont tentés de régler le résumé sur l'écran de préférence, vous devez appeler
notifyChanged()
dans la méthode deonDialogClosed
de la préférence.
L'approche traditionnelle est pleine de code réutilisable et la gestion des ressources maladroite. Voilà pourquoi j'ai fait le cadre Spyglass . Pour montrer comment cela fonctionne, voici un exemple montrant comment faire une vue personnalisée qui affiche un titre de chaîne.
Étape 1:. Créer une classe de vue personnalisée
public class CustomView extends FrameLayout {
private TextView titleView;
public CustomView(Context context) {
super(context);
init(null, 0, 0);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr, 0);
}
@RequiresApi(21)
public CustomView(
Context context,
AttributeSet attrs,
int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs, defStyleAttr, defStyleRes);
}
public void setTitle(String title) {
titleView.setText(title);
}
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
inflate(getContext(), R.layout.custom_view, this);
titleView = findViewById(R.id.title_view);
}
}
Étape 2: Définir un attribut de chaîne dans le fichier de ressources values/attrs.xml
:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Étape 3:. Appliquer l'annotation @StringHandler
à la méthode setTitle
pour dire le cadre Spyglass pour acheminer la valeur d'attribut à cette méthode lorsque la vue est gonflé
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Maintenant que votre classe a une annotation Spyglass, le cadre Spyglass détectera lors de la compilation et de générer automatiquement la classe CustomView_SpyglassCompanion
.
Étape 4: Utilisez la classe générée dans la méthode de init
de la vue personnalisée:
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
inflate(getContext(), R.layout.custom_view, this);
titleView = findViewById(R.id.title_view);
CustomView_SpyglassCompanion
.builder()
.withTarget(this)
.withContext(getContext())
.withAttributeSet(attrs)
.withDefaultStyleAttribute(defStyleAttr)
.withDefaultStyleResource(defStyleRes)
.build()
.callTargetMethodsNow();
}
Voilà. Maintenant, lorsque vous instancier la classe de XML, le compagnon Spyglass interprète les attributs et fait l'appel de méthode nécessaire. Par exemple, si on gonfle la mise en page suivante alors setTitle
sera appelée avec "Hello, World!"
comme argument.
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:width="match_parent"
android:height="match_parent">
<com.example.CustomView
android:width="match_parent"
android:height="match_parent"
app:title="Hello, World!"/>
</FrameLayout>
Le cadre ne se limite pas aux ressources de chaîne a beaucoup de différentes annotations pour le traitement d'autres types de ressources. Il a également des annotations pour définir des valeurs par défaut et pour passer des valeurs d'espace réservé si vos méthodes ont plusieurs paramètres.
Jetez un oeil à la prise en pension Github pour plus d'informations et des exemples.
si vous ne spécifiez pas l'attribut format
de l'élément attr
, vous pouvez l'utiliser pour référencer une classe de mises en page XML.
- exemple attrs.xml .
- Android comprend que studio la classe est référencée à partir de XML
- i.e..
-
Refactor > Rename
fonctionne -
Find Usages
fonctionne - et ainsi de suite ...
-
- i.e..
ne spécifiez pas d'attribut format
... / src / main / res / valeurs / attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
l'utiliser dans un certain fichier de mise en page ... / src / main / res / layout / activity__main_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<SomeLayout
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- make sure to use $ dollar signs for nested classes -->
<MyCustomView
app:give_me_a_class="class.type.name.Outer$Nested/>
<MyCustomView
app:give_me_a_class="class.type.name.AnotherClass/>
</SomeLayout>
parse la classe dans votre code d'initialisation de vue ... / src / main / java /.../ MyCustomView.kt
class MyCustomView(
context:Context,
attrs:AttributeSet)
:View(context,attrs)
{
// parse XML attributes
....
private val giveMeAClass:SomeCustomInterface
init
{
context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply()
{
try
{
// very important to use the class loader from the passed-in context
giveMeAClass = context::class.java.classLoader!!
.loadClass(getString(R.styleable.MyCustomView_give_me_a_class))
.newInstance() // instantiate using 0-args constructor
.let {it as SomeCustomInterface}
}
finally
{
recycle()
}
}
}