سؤال

أحتاج إلى تنفيذ سماتي الخاصة مثل في com.android.R.attr

لم أجد شيئًا في الوثائق الرسمية ، لذا أحتاج إلى معلومات حول كيفية تحديد هذه الأبطال وكيفية استخدامها من الكود الخاص بي.

هل كانت مفيدة؟

المحلول

حاليا أفضل الوثائق هو المصدر. يمكنك إلقاء نظرة عليه هنا (attrs.xml).

يمكنك تحديد السمات في الأعلى <resources> عنصر أو داخل أ <declare-styleable> عنصر. إذا كنت سأستخدم ATTR في أكثر من مكان أضعه في عنصر الجذر. ملاحظة ، تشترك جميع السمات في نفس مساحة الاسم العالمية. هذا يعني أنه حتى لو قمت بإنشاء سمة جديدة داخل أ <declare-styleable> العنصر يمكن استخدامه خارجه ولا يمكنك إنشاء سمة أخرى بنفس الاسم لنوع مختلف.

و <attr> يحتوي العنصر على سمتين XML name و format. name يتيح لك أن نسميها شيئًا وهذه هي الطريقة التي ينتهي بها الأمر إلى الإشارة إليها في الكود ، على سبيل المثال ، R.attr.my_attribute. ال format يمكن أن تحتوي السمة على قيم مختلفة اعتمادًا على "نوع" السمة التي تريدها.

  • المرجع - إذا كان يشير إلى معرف مورد آخر (على سبيل المثال ، "@color/my_color" ، "@layout/my_layout")
  • اللون
  • منطقية
  • البعد
  • يطفو
  • عدد صحيح
  • سلسلة
  • جزء
  • التعداد - محدد ضمنيًا عادة
  • العلم - محدد بشكل ضمني عادة

يمكنك ضبط التنسيق على أنواع متعددة باستخدام |, ، على سبيل المثال ، format="reference|color".

enum يمكن تعريف السمات على النحو التالي:

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

flag السمات متشابهة باستثناء القيم التي يجب تحديدها حتى يمكن أن تتم تجميعها معًا:

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

بالإضافة إلى السمات هناك <declare-styleable> عنصر. يتيح لك ذلك تحديد السمات التي يمكن استخدام طريقة عرض مخصصة. يمكنك القيام بذلك عن طريق تحديد <attr> العنصر ، إذا تم تعريفه مسبقًا ، فأنت لا تحدد format. إذا كنت ترغب في إعادة استخدام atrroid attr ، على سبيل المثال ، Android: Gravity ، فيمكنك القيام بذلك في name, ، كالآتي.

مثال على عرض مخصص <declare-styleable>:

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

عند تحديد سماتك المخصصة في XML على عرضك المخصص ، تحتاج إلى القيام ببعض الأشياء. أولاً ، يجب أن تعلن مساحة الاسم للعثور على سماتك. يمكنك القيام بذلك على عنصر تخطيط الجذر. عادة لا يوجد سوى xmlns:android="http://schemas.android.com/apk/res/android". يجب عليك الآن إضافة xmlns:whatever="http://schemas.android.com/apk/res-auto".

مثال:

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

أخيرًا ، للوصول إلى تلك السمة المخصصة ، عادة ما تقوم بذلك في مُنشئ العرض المخصص الخاص بك على النحو التالي.

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

النهاية. قون

نصائح أخرى

إجابة Qberticus جيدة ، لكن التفاصيل المفيدة واحدة مفقودة. إذا كنت تقوم بتنفيذ هذه في مكتبة استبدال:

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

مع:

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

وإلا فإن التطبيق الذي يستخدم المكتبة سيكون له أخطاء في وقت التشغيل.

يغطي الجواب أعلاه كل شيء بتفصيل كبير ، بصرف النظر عن بضعة أشياء.

أولاً ، إذا لم تكن هناك أنماط ، فإن ذلك (Context context, AttributeSet attrs) سيتم استخدام توقيع الطريقة لإنشاء إنشاء التفضيل. في هذه الحالة فقط استخدم context.obtainStyledAttributes(attrs, R.styleable.MyCustomView) للحصول على typedarray.

ثانياً ، لا يغطي كيفية التعامل مع موارد Plaurals (سلاسل الكمية). لا يمكن التعامل معها باستخدام typedarray. فيما يلي مقتطف رمز من مؤتمر SeekBarPreference الذي يحدد ملخص تفضيل تنسيق قيمته وفقًا لقيمة التفضيل. إذا قام XML بتفضيل التفضيلات بتعيين Android: ملخص لسلسلة نصية أو سلسلة ، فسيتم إعادة تنسيق قيمة التفضيل في السلسلة (يجب أن يكون لها ٪ d فيها ، لالتقاط القيمة). إذا تم ضبط Android: تم تعيين ملخص على مورد Plaurals ، فسيتم استخدامه لتنسيق النتيجة.

// 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() في التفضيل onDialogClosed طريقة.

النهج التقليدي مليء برمز الغلاية والتعامل مع الموارد الخرقاء. لهذا السبب صنعت إطار spyglass. لتوضيح كيفية عمله ، إليك مثال يوضح كيفية تقديم طريقة عرض مخصصة تعرض عنوان السلسلة.

الخطوة 1: إنشاء فئة عرض مخصصة.

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

الخطوة 2: تحديد سمة سلسلة في values/attrs.xml ملف الموارد:

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

الخطوة 3: تطبيق @StringHandler التعليقات التوضيحية ل setTitle طريقة لإخبار إطار spyglass لتوجيه قيمة السمة إلى هذه الطريقة عند تضخيم العرض.

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

الآن بعد أن حصل فصلك على تعليق توضيحي ، سيقوم إطار عمل Spyglass باكتشافه في وقت الترجمة وإنشاء تلقائيًا CustomView_SpyglassCompanion صف دراسي.

الخطوة 4: استخدم الفئة التي تم إنشاؤها في العرض المخصص init طريقة:

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

هذا هو. الآن عندما تقوم بتسهيل الفصل من XML ، يفسر رفيق Spyglass السمات ويقوم بإجراء استدعاء الطريقة المطلوبة. على سبيل المثال ، إذا قمنا بتضخيم التصميم التالي بعد ذلك setTitle سيتم استدعاؤه مع "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>

لا يقتصر الإطار على موارد السلسلة على الكثير من التعليقات التوضيحية المختلفة للتعامل مع أنواع الموارد الأخرى. كما أن لديها تعليقات توضيحية لتحديد القيم الافتراضية ولتمرير قيم العناصر النائبة إذا كانت أساليبك تحتوي على معلمات متعددة.

ألقِ نظرة على Github Repo لمزيد من المعلومات والأمثلة.

إذا حذفت format ميزة من attr العنصر ، يمكنك استخدامه للإشارة إلى فئة من تخطيطات XML.

  • مثال من attrs.xml.
  • يدرك Android Studio أن الفصل يجري الرجوع إليه من XML
    • بمعنى آخر
      • Refactor > Rename يعمل
      • Find Usages يعمل
      • وهلم جرا...

لا تحدد أ format ميزة في .../src/main/res/attrs.xml

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

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

</resources>

استخدمه في بعض ملفات التخطيط .../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>

تحليل الفصل في رمز تهيئة العرض الخاص بك .../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()
            }
        }
    }
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top