맞춤 속성 정의


472

에서처럼 자신의 속성을 구현해야합니다. com.android.R.attr

공식 문서에서 아무것도 발견하지 못했기 때문에이 attr을 정의하는 방법과 내 코드에서 사용하는 방법에 대한 정보가 필요합니다.


20
이 문서는 귀하의 게시물이 있지만,이 전류를 유지하기 위해, 당신은 여기에 속성에 대한 좋은, 공식 문서를 찾을 수있는 새로운 일 수 있습니다 developer.android.com/training/custom-views/...
OYRM

나는 사용자 정의 속성에 대한 예를 들어 좋은 기사를 추천 : amcmobileware.org/android/blog/2016/09/11/custom-attributes
ARKADIUSZ Cieśliński

작은 작업 예는 도움이 될 수 있습니다 : github.com/yujiaao/MergeLayout1
유 Jiaao

답변:


971

현재 가장 좋은 문서는 소스입니다. 여기서 볼 수 있습니다 (attrs.xml) .

맨 위 <resources>요소 또는 요소 내부 에서 속성을 정의 할 수 있습니다 <declare-styleable>. 여러 곳에서 attr을 사용하려면 루트 요소에 넣습니다. 모든 속성은 동일한 전역 네임 스페이스를 공유합니다. 즉, <declare-styleable>요소 내부에 새 속성을 작성하더라도 외부에서 사용할 수 있으며 동일한 유형의 다른 이름을 가진 다른 속성을 작성할 수 없습니다.

<attr>요소는 두 개의 XML 속성을 가지고 nameformat. name그것을 무언가라고 부를 수 있으며 이것이 코드에서 그것을 참조하는 방법입니다 (예 :) R.attr.my_attribute. format속성은 당신이 원하는 속성의 '유형'에 따라 다른 값을 가질 수 있습니다.

  • 참조-다른 자원 ID를 참조하는 경우 (예 : "@ 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됩니다. android : gravity와 같은 Android attr을 재사용하려면 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/…
CommonsWare

7
라이브러리 프로젝트에서 사용자 정의 속성을 사용하는 경우 :이 질문을 참조하십시오 : stackoverflow.com/questions/5819369/…-xmlns:my="http://schemas.android.com/apk/lib/my.namespace" 복사하지 않으면 attrs.xml 을 사용하면 작동하는 것 같습니다 . 네임 스페이스 URI 경로는 / apk / res가 아닌 / apk / * lib * 여야합니다.
thom_nic

2
@ThomNichols apk/lib트릭은 라이브러리 프로젝트의 참조 형식이있는 사용자 정의 속성에서 작동하지 않았습니다. 어떤 일이 사용되었다 apk/res-auto에 제안, stackoverflow.com/a/13420366/22904 바로 아래도에 stackoverflow.com/a/10217752
줄리오 Piancastelli

1
@Qberticus 인용 : "플래그 속성은 서로 비트 정렬 될 수 있도록 값을 정의해야한다는 점을 제외하면 유사합니다." 내 의견으로는 이것은 enum과 의 주요 차이점을 과소 평가하는 것입니다 flag. 전자는 하나의 값만 선택할 수 있고 후자는 여러 값을 결합 할 수 있습니다. 나는 비슷한 질문에 더 긴 대답을 여기 에 썼다. 이 질문을 발견하면 그에 연결할 것이라고 생각했다.
Rad Haring

5
a.recycle()메모리를 확보하기 위해 여기에서 매우 중요합니다
Tash Pemhiwa

87

Qberticus의 답변은 좋지만 유용한 정보가 하나 없습니다. 라이브러리에서이를 구현하는 경우 다음을 바꾸십시오.

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

와:

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

그렇지 않으면 라이브러리를 사용하는 응용 프로그램에 런타임 오류가 발생합니다.


3
이것은 최근에 추가되었습니다 ... 몇 주 전에 생각합니다. 확실히 그것은 Qberticus가 그의 답변을 쓴 후 오랫동안 추가되었습니다.
ArtOfWarfare

12
나는 그것이 그것보다 오래되었다고 생각하지만, Qberticus가 그의 대답을 쓴 후 오랫동안 추가되었습니다. 그를 잘못하지 않고 유용한 세부 사항을 추가하십시오.
닐 밀러

11
혼란을 피하기 위해 apk / res-auto를 사용하도록 Qbericus의 답변을 업데이트했습니다.
Intrications

15

위의 답변은 몇 가지 사항을 제외하고 모든 것을 자세히 설명합니다.

먼저 스타일이 없으면 (Context context, AttributeSet attrs)메소드 서명을 사용하여 환경 설정을 인스턴스화합니다. 이 경우 context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)에는 TypedArray를 얻는 데 사용 하십시오.

둘째, plaurals 리소스 (수량 문자열)를 다루는 방법은 다루지 않습니다. 이들은 TypedArray 사용을 처리 할 수 ​​없습니다. 다음은 내 SeekBarPreference의 코드 스 니펫으로 환경 설정의 요약을 환경 설정 값에 따라 값으로 설정합니다. 환경 설정의 xml이 android : summary를 텍스트 문자열 또는 문자열 자원으로 설정하면 환경 설정의 값이 문자열로 형식화됩니다 (값을 가져 오기 위해 % d가 있어야 함). android : summary가 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방법 을 호출해야합니다 .

5

전통적인 접근 방식은 상용구 코드와 서투른 리소스 처리로 가득합니다. 그래서 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 단계 : 뷰가 팽창 할 때 Spyglass 프레임 워크에 속성 값을이 메소드로 라우팅 하도록 @StringHandler주석을 setTitle메소드에 적용하십시오 .

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

클래스에 Spyglass 어노테이션이 있으므로 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 리포지토리를 참조하십시오.


Google Data Binding을 사용하여 동일한 결과를 얻을 수 있습니다. 특정 속성에 대한 속성 바인딩이 없으면 GDB는 set * 메소드를 찾으려고 대신 사용합니다. 이 경우에는 다음과 같이 작성해야합니다 android:title="@{&quot;Hello, world!&quot;}".
Spook

0

요소 에서 format특성 을 생략 attr하면이를 사용하여 XML 레이아웃에서 클래스를 참조 할 수 있습니다.

  • attrs.xml의 예 .
  • Android Studio는 클래스가 XML에서 참조되고 있음을 이해합니다.
      • Refactor > Rename 공장
      • Find Usages 공장
      • 등등...

... / src / main / res / values ​​/ attrs.xml에format 속성을 지정하지 마십시오

<?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()
            }
        }
    }
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.