Özel izinleri tanımlama


472

Benim gibi kendi niteliklerimi uygulamak gerekir com.android.R.attr

Bu belgelerin nasıl tanımlanacağı ve kodumdan nasıl kullanılacağı hakkında bilgiye ihtiyacım olduğu için resmi belgelerde hiçbir şey bulunamadı.


20
Bu dokümanlar yayınınızdan daha yeni olabilir, ancak bunu güncel tutmak için, özellikler için iyi, resmi belgeler bulabilirsiniz: developer.android.com/training/custom-views/…
OYRM

Özel özellikler hakkında bir örnek içeren güzel bir makale öneriyorum: amcmobileware.org/android/blog/2016/09/11/custom-attributes
Arkadiusz Cieśliński

küçük bir çalışma örneği yardımcı olabilir: github.com/yujiaao/MergeLayout1
Yu Jiaao

Yanıtlar:


971

Şu anda en iyi belgeler kaynaktır. Buradan bakabilirsiniz (attrs.xml) .

Nitelikleri, üst <resources>öğede veya öğenin içinde tanımlayabilirsiniz <declare-styleable>. Birden fazla yerde bir attr kullanacaksam, onu kök öğeye koyarım. Tüm özelliklerin aynı global ad alanını paylaştığını unutmayın. Bu, bir <declare-styleable>öğenin içinde yeni bir özellik oluştursanız bile, bunun dışında kullanılabilir ve farklı türde aynı adla başka bir özellik oluşturamayacağınız anlamına gelir.

Bir <attr>öğenin iki xml özelliği vardır nameve format. namebuna bir şey demenize izin verir ve bu şekilde kodda atıfta bulunma şekliniz budur, örn R.attr.my_attribute. formatNitelik istediğiniz özelliğin 'türü' bağlı olarak farklı değerlere sahip olabilir.

  • başvuru - başka bir kaynak kimliğine başvuruyorsa (ör. "@ color / my_color", "@ layout / my_layout")
  • renk
  • boole
  • boyut
  • şamandıra
  • tamsayı
  • sicim
  • kesir
  • enum - normalde dolaylı olarak tanımlanır
  • bayrak - normalde dolaylı olarak tanımlanır

Kullanarak pek çok türde biçimini ayarlayabilirsiniz |, örn format="reference|color".

enum nitelikler aşağıdaki gibi tanımlanabilir:

<attr name="my_enum_attr">
  <enum name="value1" value="1" />
  <enum name="value2" value="2" />
</attr>

flag öznitelikler benzerdir, ancak değerlerin birlikte bitlenmesi veya birleştirilmesi gerekir:

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

Özelliklere ek olarak <declare-styleable>öğe var. Bu, özel bir görünümün kullanabileceği nitelikleri tanımlamanıza olanak tanır. Bunu bir <attr>öğeyi belirterek yaparsınız , daha önce tanımlanmışsa format. Bir android attr, örneğin android: gravity'yi yeniden kullanmak isterseniz, bunu nameaşağıdaki gibi yapabilirsiniz.

Özel görünüm örneği <declare-styleable>:

<declare-styleable name="MyCustomView">
  <attr name="my_custom_attribute" />
  <attr name="android:gravity" />
</declare-styleable>

Özel görünümünüzde XML'deki özel niteliklerinizi tanımlarken birkaç şey yapmanız gerekir. Öncelikle niteliklerinizi bulmak için bir ad alanı bildirmelisiniz. Bunu kök düzen öğesinde yaparsınız. Normalde sadece vardır xmlns:android="http://schemas.android.com/apk/res/android". Şimdi de eklemelisiniz xmlns:whatever="http://schemas.android.com/apk/res-auto".

Misal:

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

Son olarak, bu özel özelliğe erişmek için normalde bunu özel görünümünüzün yapıcısında aşağıdaki gibi yaparsınız.

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

Son. :)


14
Bir özel ile kullanım için özel nitelikleri gösteren örnek bir proje View: github.com/commonsguy/cw-advandroid/tree/master/Views/…
CommonsWare

7
Bir kütüphane projesinden özel attr'ler kullanıyorsanız: şu soruya bakın: stackoverflow.com/questions/5819369/… - xmlns:my="http://schemas.android.com/apk/lib/my.namespace"Kullandığınız gibi görünüyor - hiçbir kopyalama attrs.xml yok. Ad alanı URI yolunun / apk / res değil / apk / * lib * olması gerektiğini unutmayın.
thom_nic

2
@ThomNichols, apk/libbir kütüphane projesinden referans formatı olan özel özellikler üzerinde benim için çalışmadı. Ne işe yaradıapk/res-auto , hemen aşağıda stackoverflow.com/a/13420366/22904'te ve ayrıca stackoverflow.com/a/10217752
Giulio Piancastelli

1
Quoting @Qberticus: "bayrak öznitelikleri benzerdir, ancak değerlerin tanımlanması gerekir, bu yüzden birlikte bit veya ored olabilirler". Benim düşünceme göre bu, enumve arasındaki temel farkı bir çeşit göstermekte flag. Burada benzer bir soruya daha uzun bir cevap yazdım ve şimdi bu soruyu bulduğumda bununla bağlantı kuracağımı düşündüm.
Rad Haring

5
a.recycle()belleği boşaltmak için çok önemlidir
Tash Pemhiwa

87

Qberticus'un cevabı iyidir, ancak yararlı bir ayrıntı eksik. Bunları bir kitaplıkta uyguluyorsanız:

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

ile:

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

Aksi takdirde kitaplığı kullanan uygulamada çalışma zamanı hataları olur.


3
Bu sadece yakın zamanda eklendi ... Bence birkaç hafta önce. Kesinlikle Qberticus'un cevabını yazmasından çok sonra eklendi.
ArtOfWarfare

12
Bence bundan daha eski, ama Qberticus'un cevabını yazmasından çok sonra eklendi. Onu hiç kusurlamamak, sadece yararlı bir detay eklemek.
Neil Miller

11
Qbericus'un karışıklığı kaydetmek için apk / res-auto'u kullanma cevabını güncelledim.
Karmaşıklar

15

Yukarıdaki cevap, birkaç şey dışında her şeyi ayrıntılı olarak ele almaktadır.

İlk olarak, stil yoksa (Context context, AttributeSet attrs), tercihi örneklemek için yöntem imzası kullanılacaktır. Bu durumda sadece context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)TypedArray almak için kullanın .

İkincisi, plaurals kaynakları (miktar dizeleri) ile nasıl başa çıkılacağını kapsamaz. Bunlar TypedArray kullanılarak ele alınamaz. İşte SeekBarPreference'imden, tercihin değerini tercih değerine göre biçimlendiren tercihin özetini ayarlayan bir kod snippet'i. Tercih için xml, bir metin dizesine veya dizeye özetlenir: tercihin değeri dizeye biçimlendirilir (değeri almak için içinde% d olması gerekir). Android: özeti bir plaurals kaynağına ayarlanmışsa, sonucu biçimlendirmek için kullanılır.

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

  • Bu sadece bir örnek olarak verilmiştir, ancak tercih ekranındaki özeti ayarlamak isterseniz notifyChanged(), tercihin onDialogClosedyöntemini çağırmanız gerekir .

5

Geleneksel yaklaşım, kaynak plakası kodu ve beceriksiz kaynak kullanımı ile doludur. Bu yüzden Spyglass çerçevesini yaptım . Nasıl çalıştığını göstermek için, bir Dize başlığı görüntüleyen özel bir görünümün nasıl oluşturulacağını gösteren bir örnek.

1. Adım: Özel bir görünüm sınıfı oluşturun.

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

Adım 2: values/attrs.xmlKaynak dosyada bir dize niteliği tanımlayın :

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

Adım 3: Görünüm açıklandığında Spyglass çerçevesine öznitelik değerini bu yönteme yönlendirmesini bildirmek için @StringHandlerek açıklamayı setTitleyönteme uygulayın .

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

Artık sınıfınızda bir Spyglass ek açıklaması olduğu için, Spyglass çerçevesi bunu derleme zamanında algılar ve otomatik olarak CustomView_SpyglassCompanionsınıfı oluşturur .

4. Adım: Oluşturulan sınıfı özel görünüm inityönteminde kullanın:

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

Bu kadar. Şimdi sınıfı XML'den başlattığınızda, Spyglass arkadaşı öznitelikleri yorumlar ve gerekli yöntem çağrısını yapar. Örneğin, aşağıdaki düzeni şişirirsek argüman setTitleolarak çağrılır "Hello, World!".

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

Çerçeve, dize kaynaklarıyla sınırlı değildir, diğer kaynak türlerini işlemek için birçok farklı ek açıklama içerir. Ayrıca, varsayılan değerlerin tanımlanması ve yöntemlerinizde birden çok parametre varsa yer tutucu değerlerin iletilmesi için ek açıklamalar bulunur.

Daha fazla bilgi ve örnek için Github repo'ya bakınız.


Aynı şeyi Google Veri Bağlama ile elde edebilirsiniz - belirli bir özellik için özellik bağlama yoksa, GDB set * yöntemini bulmaya çalışır ve bunun yerine kullanır. Bu durumda şunu yazmanız gerekir android:title="@{&quot;Hello, world!&quot;}".
Spook

0

formatözniteliği attröğeden atlarsanız, XML düzenlerinden bir sınıfa başvurmak için bu özniteliği kullanabilirsiniz.

  • attrs.xml dosyasından bir örnek .
  • Android Studio, sınıfa XML'den başvurulduğunu anlar
    • yani
      • Refactor > Rename İşler
      • Find Usages İşler
      • ve bunun gibi...

... / src / main / res / değerleri / attrs.xmlformat içinde bir özellik belirtmeyin

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="MyCustomView">
        ....
        <attr name="give_me_a_class"/>
        ....
    </declare-styleable>

</resources>

bazı düzen dosyalarında kullanın ... / 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>

görünümü başlatma kodunuzda sınıfı ayrıştırın ... / 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()
            }
        }
    }
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.