-
27-09-2019 - |
質問
次のように独自の属性を実装する必要があります com.android.R.attr
公式ドキュメントには何も見つからなかったので、これらの属性を定義する方法とコードからそれらを使用する方法についての情報が必要です。
解決
現在、最良のドキュメントはソースです。見てみることができます ここ (attrs.xml).
上部で属性を定義できます <resources>
要素または内部 <declare-styleable>
要素。属性を複数の場所で使用する場合は、それをルート要素に置きます。すべての属性が同じグローバル名前空間を共有することに注意してください。つまり、内部に新しい属性を作成したとしても、 <declare-styleable>
要素の外部で使用でき、同じ名前で異なるタイプの別の属性を作成することはできません。
アン <attr>
要素には 2 つの XML 属性があります name
そして format
. name
これを何かと呼ぶと、コード内でそれを参照することになります。 R.attr.my_attribute
. 。の format
属性には、必要な属性の「タイプ」に応じて異なる値を指定できます。
- Reference - 別のリソース ID を参照する場合 (例: "@color/my_color"、"@layout/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
. 。Android 属性 (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 を取得します。
第二に、複数形リソース (数量文字列) の処理方法については説明されていません。これらは 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
方法。
伝統的なアプローチは、定型的なコードと不器用なリソースハンドリングがいっぱいです。私はスパイグラスフレームワークを作った理由です。それがどのように動作するか示すために、ここで表示する文字列のタイトルというカスタムビューを作成する方法を示す例です。
ステップ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();
}
それはそれです。あなたがXMLからのクラスをインスタンス化するとき今、スパイグラスのコンパニオンは、属性を解釈して、必要なメソッドの呼び出しを行います。私たちは、次のレイアウトを膨らませる場合たとえば、その後、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
作品- 等々...
- つまり
を指定しないでください format
の属性 .../src/main/res/values/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()
}
}
}