Frage

Ich brauche meine eigenen Attribute zu implementieren, wie in com.android.R.attr

Nichts gefunden in der offiziellen Dokumentation, so brauche ich Informationen darüber, wie diese attrs zu definieren und wie sie von meinem Code verwenden.

War es hilfreich?

Lösung

Derzeit ist die beste Dokumentation ist die Quelle. Sie können einen Blick auf sie hier href="https://github.com/android/platform_frameworks_base/blob/master/core/res/res/values/attrs.xml" rel="noreferrer"> .

Sie können Attribute im oberen <resources> Elemente oder innerhalb eines <declare-styleable> Elements definieren. Wenn ich ein attr in mehr als einem Ort benutzen werde habe ich es in das Root-Element. Beachten Sie, alle Attribute teilen die gleiche globale Namespace. Das bedeutet, dass selbst wenn Sie ein neues Attribut innerhalb eines <declare-styleable> Elements erstellen kann es verwendet außerhalb davon sein, und Sie können nicht ein weiteres Attribut mit demselben Namen eines anderen Typs erstellen.

Ein <attr> Element hat zwei XML-Attribute name und format. name können Sie es nennen etwas und das ist, wie Sie im Code darauf zu verweisen am Ende, zum Beispiel R.attr.my_attribute. Das format Attribut kann verschiedene Werte haben je nach ‚Typ‘ des Attributs, das Sie möchten.

  • Referenz - wenn es verweist auf eine andere Ressource-ID (beispielsweise "@ color / my_color", "@ Layout / my_layout")
  • Farbe
  • boolean
  • Dimension
  • float
  • integer
  • string
  • Fraktion
  • Enum - normalerweise implizit definiert
  • Flagge - normalerweise implizit definiert

Sie können das Format auf mehrere Arten festgelegt durch | beispielsweise unter Verwendung 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 die Werte definiert werden müssen, damit sie etwas zusammen ored werden:

<attr name="my_flag_attr">
  <flag name="fuzzy" value="0x01" />
  <flag name="cold" value="0x02" />
</attr>

Neben Attribute gibt es das <declare-styleable> Element. Auf diese Weise können Sie Attribute eine benutzerdefinierte Ansicht verwenden können, definieren. Sie tun dies, indem sie ein <attr> Element spezifiziert, wenn es vorher Sie die format nicht angeben, definiert wurde. Wenn Sie ein Android-attr, zum Beispiel zur Wiederverwendung, android. Schwerkraft, dann können Sie das in der name tun, wie folgt

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 benutzerdefinierte Attribute in XML auf Ihrer benutzerdefinierten Ansicht definieren, müssen Sie ein paar Dinge zu tun. Zuerst müssen Sie einen Namespace deklarieren Ihre Attribute zu finden. Sie tun dies auf dem Root-Layoutelement. Normalerweise gibt es nur xmlns:android="http://schemas.android.com/apk/res/android". Sie müssen jetzt auch xmlns:whatever="http://schemas.android.com/apk/res-auto" hinzufügen.

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>

Schließlich, um den Zugang, dass benutzerdefiniertes Attribut, das Sie normalerweise tun dies im Konstruktor der 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. :)

Andere Tipps

Qberticus Antwort ist gut, aber ein nützliches Detail fehlt. Wenn Sie diese in einer Bibliothek implementieren ersetzen:

xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"

mit:

xmlns:whatever="http://schemas.android.com/apk/res-auto"

Ansonsten ist die Anwendung, die die Bibliothek verwendet werden Laufzeitfehler haben.

Die Antwort oben deckt alles im Detail, abgesehen von ein paar Dinge.

Erstens, wenn es keine Stile sind, dann wird die (Context context, AttributeSet attrs) Methodensignatur verwendet werden, um die Präferenz zu instanziieren. In diesem Fall nur context.obtainStyledAttributes(attrs, R.styleable.MyCustomView) verwenden, um die TypedArray zu erhalten.

Zweitens ist es nicht behandelt, wie mit plaurals Ressourcen (Anzahl Strings) beschäftigen. Diese können nicht mit der Verwendung von TypedArray behandelt werden. Hier ist ein Code-Schnipsel aus meinem SeekBarPreference, das die Zusammenfassung der Präferenz Formatierung seinen Wert legt entsprechend dem Wert der Präferenz. Wenn die XML für die Vorzugs Sätze android: Zusammenfassung in einer Textzeichenfolge oder eine Zeichenfolge resouce der Wert der Präferenz in den String formatiert ist (es sollte daran haben% d, um den Wert zu holen). Wenn Android. Zusammenfassung zu einer plaurals Ressource festgelegt ist, die verwendet wird, um das Ergebnis zu formatieren

// 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);
}

  • Dies ist nur als ein Beispiel gegeben, jedoch, wenn Sie versucht sind, wollen die Zusammenfassung auf dem Einstellungsbildschirm zu setzen, dann müssen Sie notifyChanged() in der Präferenz des onDialogClosed Methode aufzurufen.

Der traditionelle Ansatz ist voll von Standardcode und ungeschickten Umgang mit Ressourcen. Deshalb habe ich die Spyglass Rahmen . Um zu zeigen, wie es funktioniert, hier ist ein Beispiel dafür, wie eine benutzerdefinierte Ansicht zu machen, dass zeigt einen String-Titel.

Schritt 1:. Erstellen Sie eine benutzerdefinierte Ansicht Klasse

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 Zeichenfolge-Attribut in der values/attrs.xml Ressource-Datei:

<resources>
    <declare-styleable name="CustomView">
        <attr name="title" format="string"/>
    </declare-styleable>
</resources>

Schritt 3:. Wenden Sie die @StringHandler Anmerkung zur setTitle Methode des Attributwert dieser Methode der Spyglass Rahmen Weg zu sagen, wenn die Ansicht aufgeblasen wird

@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
    titleView.setText(title);
}

Nun, da Ihre Klasse eine Spyglass Anmerkung hat, erkennt die Spyglass Rahmen es zum Zeitpunkt der Kompilierung und automatisch die CustomView_SpyglassCompanion Klasse erzeugen.

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. Nun, wenn Sie die Klasse von XML-instanziiert, interpretieren die Spyglass Begleiter die Attribute und machen den erforderlichen Methodenaufruf. Zum Beispiel, wenn wir das folgende Layout dann aufblasen setTitle mit "Hello, World!" als Argument aufgerufen werden.

<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>

Der Rahmen ist nicht auf String-Ressourcen beschränkt hat viele verschiedene Anmerkungen zum Umgang mit anderen Ressourcentypen. Es hat auch Anmerkungen zur Definition von Standardwerten und für in Platzhalter Werte geben, wenn Ihre Methoden mehrere Parameter haben.

Haben Sie einen Blick auf die Github Repo für weitere Informationen und Beispiele.

Wenn Sie das format Attribut aus dem attr Elemente weglassen, können Sie es verwenden, um eine Klasse von XML-Layout zu verweisen.

  • Beispiel von attrs.xml .
  • Android Studio versteht, dass die Klasse von XML verwiesen wird
    • d.
      • Refactor > Rename funktioniert
      • Find Usages funktioniert
      • und so weiter ...

nicht angeben, ein format Attribut in ... / src / main / res / Werte / attrs.xml

<?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 einigen Layout-Datei ... / 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>

analysiert, um die Klasse in Ihrer Ansicht Initialisierungscode ... / 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()
            }
        }
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top