Ich muss meine eigenen Attribute wie in implementieren com.android.R.attr
In der offiziellen Dokumentation wurde nichts gefunden, daher benötige ich Informationen zum Definieren dieser Attribute und zur Verwendung in meinem Code.
Ich muss meine eigenen Attribute wie in implementieren com.android.R.attr
In der offiziellen Dokumentation wurde nichts gefunden, daher benötige ich Informationen zum Definieren dieser Attribute und zur Verwendung in meinem Code.
Antworten:
Derzeit ist die beste Dokumentation die Quelle. Sie können es hier ansehen (attrs.xml) .
Sie können Attribute im obersten <resources>
Element oder innerhalb eines <declare-styleable>
Elements definieren. Wenn ich ein attr an mehr als einer Stelle verwenden möchte, füge ich es in das Stammelement ein. Beachten Sie, dass alle Attribute denselben globalen Namespace verwenden. Das heißt, selbst wenn Sie ein neues Attribut innerhalb eines <declare-styleable>
Elements erstellen , kann es außerhalb des Elements verwendet werden, und Sie können kein anderes Attribut mit demselben Namen eines anderen Typs erstellen.
Ein <attr>
Element hat zwei XML-Attribute name
und format
. name
Sie können es etwas nennen, und so verweisen Sie im Code darauf, z R.attr.my_attribute
. Das format
Attribut kann je nach gewünschtem Attributtyp unterschiedliche Werte haben.
Sie können das Format auf mehrere Typen einstellen, indem Sie |
z format="reference|color"
.
enum
Attribute können wie folgt definiert werden:
<attr name="my_enum_attr">
<enum name="value1" value="1" />
<enum name="value2" value="2" />
</attr>
flag
Attribute sind ähnlich, außer dass die Werte definiert werden müssen, damit sie zusammengefügt werden können:
<attr name="my_flag_attr">
<flag name="fuzzy" value="0x01" />
<flag name="cold" value="0x02" />
</attr>
Neben Attributen gibt es das <declare-styleable>
Element. Auf diese Weise können Sie Attribute definieren, die eine benutzerdefinierte Ansicht verwenden kann. Sie tun dies, indem Sie ein <attr>
Element angeben format
. Wenn es zuvor definiert wurde, geben Sie das nicht an . Wenn Sie ein Android-Attribut, beispielsweise Android: Gravity, wiederverwenden möchten, können Sie dies name
wie folgt tun .
Ein Beispiel für eine benutzerdefinierte Ansicht <declare-styleable>
:
<declare-styleable name="MyCustomView">
<attr name="my_custom_attribute" />
<attr name="android:gravity" />
</declare-styleable>
Wenn Sie Ihre benutzerdefinierten Attribute in XML in Ihrer benutzerdefinierten Ansicht definieren, müssen Sie einige Dinge tun. Zuerst müssen Sie einen Namespace deklarieren, um Ihre Attribute zu finden. Sie tun dies für das Root-Layout-Element. Normalerweise gibt es nur xmlns:android="http://schemas.android.com/apk/res/android"
. Sie müssen jetzt auch hinzufügen xmlns:whatever="http://schemas.android.com/apk/res-auto"
.
Beispiel:
<?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>
Um auf dieses benutzerdefinierte Attribut zuzugreifen, tun Sie dies normalerweise im Konstruktor Ihrer benutzerdefinierten Ansicht wie folgt.
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();
}
Das Ende. :) :)
View
: github.com/commonsguy/cw-advandroid/tree/master/Views/…
xmlns:my="http://schemas.android.com/apk/lib/my.namespace"
- kein Kopieren von attrs.xml. Beachten Sie, dass der Namespace-URI-Pfad / apk / * lib * not / apk / res sein muss.
apk/lib
Trick hat bei benutzerdefinierten Attributen mit Referenzformat aus einem Bibliotheksprojekt bei mir nicht funktioniert. Was tat der Arbeit Gebrauch war apk/res-auto
, wie in vorgeschlagen stackoverflow.com/a/13420366/22904 knapp unter und auch in stackoverflow.com/a/10217752
enum
und flag
: Ersteres lässt uns einen und nur einen Wert auswählen, letzteres lässt uns mehrere kombinieren. Ich schrieb eine längere Antwort auf eine ähnliche Frage auf , hier und jetzt diese Frage gefunden zu haben , ich dachte ich , dass verknüpfen würde.
a.recycle()
ist hier sehr wichtig, um Speicher
Qberticus 'Antwort ist gut, aber ein nützliches Detail fehlt. Wenn Sie diese in einer Bibliothek implementieren, ersetzen Sie:
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
mit:
xmlns:whatever="http://schemas.android.com/apk/res-auto"
Andernfalls weist die Anwendung, die die Bibliothek verwendet, Laufzeitfehler auf.
Die obige Antwort deckt alles sehr detailliert ab, abgesehen von ein paar Dingen.
Wenn keine Stile vorhanden sind, wird zunächst die (Context context, AttributeSet attrs)
Methodensignatur verwendet, um die Voreinstellung zu instanziieren. In diesem Fall verwenden Sie einfach context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)
, um das TypedArray abzurufen.
Zweitens wird nicht behandelt, wie mit plauralen Ressourcen (Mengenzeichenfolgen) umgegangen wird. Diese können mit TypedArray nicht behandelt werden. Hier ist ein Codeausschnitt aus meiner SeekBarPreference, der die Zusammenfassung der Präferenz festlegt und ihren Wert entsprechend dem Wert der Präferenz formatiert. Wenn die XML-Datei für die Voreinstellung android: summary auf eine Textzeichenfolge oder eine Zeichenfolge resouce setzt, wird der Wert der Voreinstellung in die Zeichenfolge formatiert (sie sollte% d enthalten, um den Wert abzurufen). Wenn android: summary auf eine plaurale Ressource festgelegt ist, wird dies zum Formatieren des Ergebnisses verwendet.
// 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()
die onDialogClosed
Methode der Voreinstellung aufrufen .Der traditionelle Ansatz ist voll von Boilerplate-Code und ungeschicktem Umgang mit Ressourcen. Deshalb habe ich das Spyglass-Framework erstellt . In diesem Beispiel wird gezeigt, wie eine benutzerdefinierte Ansicht erstellt wird, in der ein Zeichenfolgentitel angezeigt wird.
Schritt 1: Erstellen Sie eine benutzerdefinierte Ansichtsklasse.
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);
}
}
Schritt 2: Definieren Sie ein Zeichenfolgenattribut in der values/attrs.xml
Ressourcendatei:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Schritt 3: Wenden Sie die @StringHandler
Anmerkung auf die setTitle
Methode an, um das Spyglass-Framework anzuweisen, den Attributwert an diese Methode weiterzuleiten, wenn die Ansicht aufgeblasen wird.
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Nachdem Ihre Klasse über eine Spyglass-Annotation verfügt, erkennt das Spyglass-Framework diese zur Kompilierungszeit und generiert die CustomView_SpyglassCompanion
Klasse automatisch .
Schritt 4: Verwenden Sie die generierte Klasse in der init
Methode der benutzerdefinierten Ansicht :
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();
}
Das ist es. Wenn Sie nun die Klasse aus XML instanziieren, interpretiert der Spyglass-Begleiter die Attribute und führt den erforderlichen Methodenaufruf aus. Wenn wir beispielsweise das folgende Layout aufblasen, setTitle
wird dies "Hello, World!"
als Argument aufgerufen .
<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>
Das Framework ist nicht auf Zeichenfolgenressourcen beschränkt und verfügt über viele verschiedene Anmerkungen für die Verarbeitung anderer Ressourcentypen. Es enthält auch Anmerkungen zum Definieren von Standardwerten und zum Übergeben von Platzhalterwerten, wenn Ihre Methoden mehrere Parameter haben.
Weitere Informationen und Beispiele finden Sie im Github-Repo.
android:title="@{"Hello, world!"}"
.
Wenn Sie das format
Attribut im attr
Element weglassen , können Sie damit auf eine Klasse aus XML-Layouts verweisen.
Refactor > Rename
funktioniertFind Usages
funktioniertGeben Sie kein format
Attribut in ... / src / main / res / values / attrs.xml an
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
Verwenden Sie es in einer Layoutdatei ... / 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>
Analysieren Sie die Klasse in Ihrem Ansichtsinitialisierungscode ... / 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()
}
}
}