J'ai besoin d'implémenter mes propres attributs comme dans com.android.R.attr
Je n'ai rien trouvé dans la documentation officielle, j'ai donc besoin d'informations sur la façon de définir ces attr et comment les utiliser à partir de mon code.
J'ai besoin d'implémenter mes propres attributs comme dans com.android.R.attr
Je n'ai rien trouvé dans la documentation officielle, j'ai donc besoin d'informations sur la façon de définir ces attr et comment les utiliser à partir de mon code.
Réponses:
Actuellement, la meilleure documentation est la source. Vous pouvez y jeter un œil ici (attrs.xml) .
Vous pouvez définir des attributs dans l' <resources>
élément supérieur ou à l'intérieur d'un <declare-styleable>
élément. Si je vais utiliser un attr à plus d'un endroit, je le mets dans l'élément racine. Notez que 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 <declare-styleable>
élément, il peut être utilisé à l'extérieur de celui-ci et vous ne pouvez pas créer un autre attribut avec le même nom d'un type différent.
Un <attr>
élément a deux attributs xml name
et format
. name
vous permet d' appeler quelque chose et c'est la façon dont vous finissez par y faire référence dans le code, par exemple R.attr.my_attribute
. L' format
attribut peut avoir différentes valeurs selon le «type» d'attribut souhaité.
Vous pouvez définir le format à plusieurs types en utilisant |
, par exemple, format="reference|color"
.
enum
les attributs peuvent être définis comme suit:
<attr name="my_enum_attr">
<enum name="value1" value="1" />
<enum name="value2" value="2" />
</attr>
flag
les attributs sont similaires, sauf que les valeurs doivent être définies pour pouvoir être bitées ensemble:
<attr name="my_flag_attr">
<flag name="fuzzy" value="0x01" />
<flag name="cold" value="0x02" />
</attr>
En plus des attributs, il y a l' <declare-styleable>
élément. Cela vous permet de définir les attributs qu'une vue personnalisée peut utiliser. Pour ce faire, spécifiez un <attr>
élément, s'il a été défini précédemment, vous ne spécifiez pas le format
. Si vous souhaitez réutiliser un attr android, par exemple, android: gravity, vous pouvez le faire dans le name
, comme suit.
Un exemple de vue personnalisée <declare-styleable>
:
<declare-styleable name="MyCustomView">
<attr name="my_custom_attribute" />
<attr name="android:gravity" />
</declare-styleable>
Lors de la définition de vos attributs personnalisés en XML sur votre vue personnalisée, vous devez effectuer quelques opérations. Vous devez d'abord déclarer un espace de noms pour trouver vos attributs. Vous faites cela sur l'élément de disposition racine. Normalement, il y en a seulement 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, pour accéder à cet attribut personnalisé, vous le faites normalement dans le constructeur de votre vue personnalisée 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. :)
View
: github.com/commonsguy/cw-advandroid/tree/master/Views/...
xmlns:my="http://schemas.android.com/apk/lib/my.namespace"
- pas de copie d'attrs.xml. Notez que le chemin URI de l'espace de noms doit être / apk / * lib * et non / apk / res.
apk/lib
astuce n'a pas fonctionné pour moi sur les attributs personnalisés avec le format de référence d'un projet de bibliothèque. Ce qui a fonctionné était d'utiliser apk/res-auto
, comme suggéré dans stackoverflow.com/a/13420366/22904 juste en dessous et également dans stackoverflow.com/a/10217752
enum
et flag
: la première nous permet de choisir une et une seule valeur, la seconde nous permet d'en combiner plusieurs. J'ai écrit une réponse plus longue dans une question similaire ici , et maintenant que j'ai trouvé cette question, je me suis dit que j'y lierais un lien.
a.recycle()
est très important ici pour libérer de la mémoire
La réponse de Qberticus est bonne, mais il manque un détail utile. Si vous les implémentez dans une bibliothèque, remplacez:
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, à l'exception de quelques éléments.
Tout d'abord, s'il n'y a pas de styles, la (Context context, AttributeSet attrs)
signature de la méthode sera utilisée pour instancier la préférence. Dans ce cas, utilisez simplement context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)
pour obtenir le TypedArray.
Deuxièmement, il ne couvre pas comment gérer les ressources plaurales (chaînes de quantité). Ceux-ci ne peuvent pas être traités à l'aide de TypedArray. Voici un extrait de code de mon SeekBarPreference qui définit le résumé de la préférence en formatant sa valeur en fonction de la valeur de la préférence. Si le xml de la préférence définit android: summary dans une chaîne de texte ou une chaîne de caractères, la valeur de la préférence est formatée dans la chaîne (elle doit contenir% d, pour récupérer la valeur). Si android: summary est défini sur une ressource plaurals, il 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);
}
notifyChanged()
la onDialogClosed
méthode des préférences .L'approche traditionnelle est pleine de code standard et de gestion des ressources maladroite. C'est pourquoi j'ai créé le cadre Spyglass . Pour montrer comment cela fonctionne, voici un exemple montrant comment créer une vue personnalisée qui affiche un titre de chaîne.
Étape 1: créez 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éfinissez un attribut de chaîne dans le values/attrs.xml
fichier de ressources:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Étape 3: appliquez l' @StringHandler
annotation à la setTitle
méthode pour indiquer au framework Spyglass de router la valeur d'attribut vers cette méthode lorsque la vue est gonflée.
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Maintenant que votre classe a une annotation Spyglass, le framework Spyglass la détectera au moment de la compilation et générera automatiquement la CustomView_SpyglassCompanion
classe.
Étape 4: utilisez la classe générée dans la init
méthode 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();
}
C'est ça. Désormais, lorsque vous instanciez la classe à partir de XML, le compagnon Spyglass interprète les attributs et effectue l'appel de méthode requis. Par exemple, si nous gonflons la disposition suivante, setTitle
nous serons appelés 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 n'est pas limité aux ressources de chaîne a beaucoup d'annotations différentes pour gérer d'autres types de ressources. Il comporte également des annotations pour définir des valeurs par défaut et pour transmettre des valeurs d'espace réservé si vos méthodes ont plusieurs paramètres.
Jetez un œil au dépôt Github pour plus d'informations et d'exemples.
android:title="@{"Hello, world!"}"
.
si vous omettez l' format
attribut de l' attr
élément, vous pouvez l'utiliser pour référencer une classe à partir de dispositions XML.
Refactor > Rename
travauxFind Usages
travauxne spécifiez pas d' format
attribut dans ... / src / main / res / values / attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
utilisez-le dans un 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>
analyser la classe dans le code d'initialisation de votre 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()
}
}
}