[android] 맞춤 속성 정의
에서처럼 자신의 속성을 구현해야합니다. com.android.R.attr
공식 문서에서 아무것도 발견하지 못했기 때문에이 attr을 정의하는 방법과 내 코드에서 사용하는 방법에 대한 정보가 필요합니다.
답변
현재 가장 좋은 문서는 소스입니다. 여기서 볼 수 있습니다 (attrs.xml) .
맨 위 <resources>
요소 또는 요소 내부 에서 속성을 정의 할 수 있습니다 <declare-styleable>
. 여러 곳에서 attr을 사용하려면 루트 요소에 넣습니다. 모든 속성은 동일한 전역 네임 스페이스를 공유합니다. 즉, <declare-styleable>
요소 내부에 새 속성을 작성하더라도 외부에서 사용할 수 있으며 동일한 유형의 다른 이름을 가진 다른 속성을 작성할 수 없습니다.
<attr>
요소는 두 개의 XML 속성을 가지고 name
와 format
. 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();
}
끝. 🙂
답변
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 : 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
방법 을 호출해야합니다 .
답변
전통적인 접근 방식은 상용구 코드와 서투른 리소스 처리로 가득합니다. 그래서 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 리포지토리를 참조하십시오.
답변
요소 에서 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()
}
}
}