कस्टम अटार्नी को परिभाषित करना


472

मुझे अपनी खुद की विशेषताओं को लागू करने की आवश्यकता है जैसे कि com.android.R.attr

आधिकारिक दस्तावेज़ीकरण में कुछ भी नहीं मिला है, इसलिए मुझे इन कोड को परिभाषित करने और उन्हें अपने कोड से कैसे उपयोग करना है, इसके बारे में जानकारी चाहिए।


20
ये डॉक्स नए हो सकते हैं कि आपकी पोस्ट, लेकिन इस करंट को बनाए रखने के लिए, आप यहाँ विशेषताओं के लिए अच्छे, आधिकारिक दस्तावेज पा सकते हैं: developer.android.com/training/custom-views/…
OYRM

मैं कस्टम विशेषताओं के बारे में एक उदाहरण के साथ अच्छा लेख सुझाता हूं
Arkadiusz Cieśliński

एक छोटा सा काम करने वाला उदाहरण सहायक हो सकता है: github.com/yujiaao/MergeLayout1
Yu Jiaao

जवाबों:


971

वर्तमान में सबसे अच्छा प्रलेखन स्रोत है। आप इसे यहाँ देख सकते हैं (attrs.xml)

आप शीर्ष <resources>तत्व में या किसी <declare-styleable>तत्व के अंदर विशेषताओं को परिभाषित कर सकते हैं । यदि मैं एक से अधिक जगह पर एक attr का उपयोग करने जा रहा हूँ, तो मैं इसे मूल तत्व में डाल देता हूँ। ध्यान दें, सभी विशेषताएँ समान वैश्विक नाम स्थान साझा करती हैं। इसका मतलब है कि भले ही आप एक <declare-styleable>तत्व के अंदर एक नई विशेषता का निर्माण करते हैं, इसका उपयोग इसके बाहर किया जा सकता है और आप एक अलग प्रकार के नाम के साथ एक और विशेषता नहीं बना सकते हैं।

एक <attr>तत्व में दो xml विशेषताएँ हैं nameऔर formatnameआपको इसे कुछ कॉल करने देता है और इस तरह से आप कोड, उदाहरण के लिए इसे समाप्त करते हैं R.attr.my_attribute। जिस formatविशेषता को आप चाहते हैं उसके आधार पर विशेषता के भिन्न मूल्य हो सकते हैं।

  • संदर्भ - यदि यह किसी अन्य संसाधन आईडी का संदर्भ देता है (जैसे, "@ रंग / my_color", "@ लेआउट / my_layout")
  • रंग
  • बूलियन
  • आयाम
  • नाव
  • पूर्णांक
  • तार
  • अंश
  • enum - सामान्य रूप से स्पष्ट रूप से परिभाषित
  • झंडा - सामान्य रूप से स्पष्ट रूप से परिभाषित

आप |उदाहरण के लिए, कई प्रकारों का उपयोग करके प्रारूप सेट कर सकते हैं , जैसे 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। यदि आप एक एंड्रॉइड एट्री का पुन: उपयोग करना चाहते हैं, उदाहरण के लिए, एंड्रॉइड: गुरुत्वाकर्षण, तो आप 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();
}

समाप्त। :)


14
यहाँ एक नमूना परियोजना है जिसमें कस्टम के साथ उपयोग के लिए कस्टम विशेषताओं का प्रदर्शन किया गया है View: github.com/commonsguy/cw-advandroid/tree/master/Views/…
कॉमन्सवेयर

7
यदि आप किसी लायब्रेरी प्रोजेक्ट से कस्टम अटैचर्स का उपयोग कर रहे हैं: इस प्रश्न को देखें: stackoverflow.com/questions/5819369/… - यदि आप उपयोग करते हैं तो यह काम करने लगता है xmlns:my="http://schemas.android.com/apk/lib/my.namespace"- कोई कॉपीिंग attrs.xml नहीं। ध्यान दें नामस्थान URI पथ / apk / * lib * नहीं / apk / res होना चाहिए।
thom_nic

2
@ThomNichols apk/libट्रिक ने लाइब्रेरी प्रोजेक्ट से संदर्भ प्रारूप के साथ कस्टम विशेषताओं पर मेरे लिए काम नहीं किया। क्या किया था काम उपयोग के लिए गया था apk/res-autoके रूप में सुझाव दिया, stackoverflow.com/a/13420366/22904 और बस से नीचे भी stackoverflow.com/a/10217752
गिउलिओ Piancastelli

1
@Qberticus का हवाला देते हुए: "ध्वज गुण समान हैं सिवाय मूल्यों को परिभाषित किए जाने की आवश्यकता है ताकि उन्हें एक साथ बिट किया जा सके"। मेरी राय में इस बीच मुख्य अंतर यह समझ की तरह है enumऔर flagपूर्व हमें एक और केवल एक मूल्य लेने की सुविधा देता है, बाद हमें कई गठबंधन की सुविधा देता है। मैंने यहाँ एक ऐसे ही प्रश्न का लंबा उत्तर लिखा है , और अब मुझे यह प्रश्न मिला है कि मैं उससे जुड़ा था।
राड हरिंग

5
a.recycle()स्मृति को मुक्त करने के लिए यहां बहुत महत्वपूर्ण है
ताश पेमहिवा

87

Qberticus का उत्तर अच्छा है, लेकिन एक उपयोगी विवरण गायब है। यदि आप इन्हें पुस्तकालय में प्रतिस्थापित कर रहे हैं:

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

साथ में:

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

अन्यथा लाइब्रेरी का उपयोग करने वाले एप्लिकेशन में रनटाइम त्रुटियां होंगी।


3
यह केवल हाल ही में जोड़ा गया था ... मुझे लगता है कि कुछ हफ्तों पहले। निश्चित रूप से यह लंबे समय के बाद जोड़ा गया था जब कुरबिकस ने अपना उत्तर लिखा था।
ArtOfWarfare

12
मुझे लगता है कि यह उससे भी पुराना है, लेकिन यह निश्चित रूप से लंबे समय के बाद जोड़ा गया था जब क़तरिकस ने अपना जवाब लिखा था। उसे गलती से नहीं, सिर्फ एक उपयोगी विवरण जोड़कर।
नील मिलर

11
मैंने भ्रम को बचाने के लिए apk / res-auto का उपयोग करने के लिए Qbericus के उत्तर को अपडेट किया है।
17

15

ऊपर दिया गया उत्तर एक-दो चीजों के अलावा हर चीज को बहुत विस्तार से कवर करता है।

सबसे पहले, यदि कोई शैली नहीं है, तो (Context context, AttributeSet attrs)वरीयता को तत्काल करने के लिए विधि हस्ताक्षर का उपयोग किया जाएगा। इस मामले में सिर्फ context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)टाइप करने के लिए उपयोग करेंआधार।

दूसरी बात यह नहीं है कि प्लाउरल संसाधनों (मात्रा के तार) से कैसे निपटा जाए। इन्हें TypedArray का उपयोग करके निपटाया नहीं जा सकता। यहाँ मेरा SeekBarPreference से एक कोड स्निपेट है जो वरीयता के सारांश को वरीयता के मूल्य के अनुसार इसके मूल्य को सेट करता है। यदि वरीयता के लिए xml Android सेट करता है: एक पाठ स्ट्रिंग या एक स्ट्रिंग का सारांश, वरीयता के मूल्य को स्ट्रिंग में स्वरूपित किया जाता है (यह मान लेने के लिए इसमें% d होना चाहिए)। यदि एंड्रॉइड: सारांश को प्लोरल्स संसाधन पर सेट किया जाता है, तो इसका उपयोग परिणाम को प्रारूपित करने के लिए किया जाता है।

// 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विधि में कॉल करने की आवश्यकता है ।

5

पारंपरिक दृष्टिकोण बॉयलरप्लेट कोड और अनाड़ी संसाधन हैंडलिंग से भरा है। इसलिए मैंने स्पाईग्लास फ्रेमवर्क बनाया । यह कैसे काम करता है यह प्रदर्शित करने के लिए, यहां एक उदाहरण दिखाया गया है कि एक कस्टम दृश्य कैसे बनाया जाए जो स्ट्रिंग शीर्षक प्रदर्शित करता है।

चरण 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तरीके को विशेषता मान को देखने के लिए स्पेलग्लास फ्रेमवर्क को बताने के लिए एनोटेशन को लागू करें जब दृश्य फुलाया जाता है।

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

अब जब आपकी कक्षा में स्पाईग्लास एनोटेशन है, तो स्पाईग्लास फ्रेमवर्क इसे संकलन-समय पर पता लगाएगा और स्वचालित रूप से 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();
}

बस। अब जब आप एक्सएमएल से क्लास को इंस्टेंट करते हैं, तो स्पाईग्लास साथी विशेषताओं की व्याख्या करता है और आवश्यक विधि कॉल करता है। उदाहरण के लिए, यदि हम निम्नलिखित लेआउट को बढ़ाते हैं, तो 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 पर एक नज़र डालें।


आप Google डेटा बाइंडिंग के साथ इसे प्राप्त कर सकते हैं - यदि विशिष्ट विशेषता के लिए कोई विशेषता बाइंडिंग नहीं है, तो GDB सेट * विधि खोजने की कोशिश करता है और इसके बजाय इसका उपयोग करता है। इस मामले में आपको कहना होगा android:title="@{&quot;Hello, world!&quot;}"
भूत

0

यदि आप तत्व formatसे विशेषता को छोड़ देते हैं attr, तो आप इसका उपयोग XML लेआउट से एक क्लास को संदर्भित करने के लिए कर सकते हैं।

  • attrs.xml से उदाहरण ।
  • एंड्रॉइड स्टूडियो समझता है कि वर्ग को एक्सएमएल से संदर्भित किया जा रहा है
    • अर्थात
      • Refactor > Rename काम करता है
      • Find Usages काम करता है
      • और इसी तरह...

... / src / main / res / values ​​/ attrs.xmlformat में एक विशेषता निर्दिष्ट न करें

<?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()
            }
        }
    }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.