私のように自分の属性を実装する必要があります com.android.R.attr
公式ドキュメントには何も見つからなかったため、これらの属性の定義方法とコードからの使用方法に関する情報が必要です。
私のように自分の属性を実装する必要があります com.android.R.attr
公式ドキュメントには何も見つからなかったため、これらの属性の定義方法とコードからの使用方法に関する情報が必要です。
回答:
現在、最良のドキュメントはソースです。ここ(attrs.xml)で確認できます。
属性は、最上位の<resources>
要素または要素の内部で定義できます<declare-styleable>
。attrを複数の場所で使用する場合は、ルート要素に配置します。すべての属性が同じグローバル名前空間を共有していることに注意してください。つまり、<declare-styleable>
要素内に新しい属性を作成した場合でも、それをその要素の外で使用でき、同じタイプの別の属性を別のタイプで作成することはできません。
<attr>
要素は2つのXML属性を持ってname
してformat
。name
あなたはそれを何かと呼ぶことができます、そしてこれはあなたがコードでそれを参照することになる方法です、例えばR.attr.my_attribute
。format
属性には、あなたがしたい属性の「タイプ」に応じて、異なる値を持つことができます。
などを使用して|
、フォーマットを複数のタイプに設定できます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();
}
終わり。:)
xmlns:my="http://schemas.android.com/apk/lib/my.namespace"
-attrs.xmlをコピーしないでください。ネームスペースURIパスは/ apk / resではなく/ apk / * lib *である必要があります。
apk/lib
トリックは、ライブラリプロジェクトの参照形式のカスタム属性では機能しませんでした。何やった仕事は、利用したことapk/res-auto
で提案されているように、stackoverflow.com/a/13420366/22904でもすぐ下とstackoverflow.com/a/10217752
enum
とflag
:前者は私たちが唯一つの値を選ぶことができます、後者は、私たちはいくつか組み合わせることができます。私はここで同様の質問に長い回答を書いており、この質問を見つけたので、それにリンクすると思いました。
a.recycle()
ここでメモリを解放することが非常に重要です
Qberticusの答えは良いですが、1つの有用な詳細がありません。これらをライブラリに実装する場合は、以下を置き換えます。
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が複数リソースに設定されている場合、それは結果のフォーマットに使用されます。
// 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アノテーションが追加されたので、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リポジトリをご覧ください。
android:title="@{"Hello, world!"}"
。
要素format
から属性を省略した場合attr
、それを使用して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()
}
}
}