Muszę zaimplementować własne atrybuty jak w com.android.R.attr
Nie znalazłem nic w oficjalnej dokumentacji, więc potrzebuję informacji o tym, jak zdefiniować te attry i jak ich używać z mojego kodu.
Muszę zaimplementować własne atrybuty jak w com.android.R.attr
Nie znalazłem nic w oficjalnej dokumentacji, więc potrzebuję informacji o tym, jak zdefiniować te attry i jak ich używać z mojego kodu.
Odpowiedzi:
Obecnie najlepszą dokumentacją jest źródło. Możesz na to spojrzeć tutaj (attrs.xml) .
Możesz zdefiniować atrybuty w górnym <resources>
elemencie lub wewnątrz <declare-styleable>
elementu. Jeśli zamierzam użyć attr w więcej niż jednym miejscu, umieszczam go w elemencie głównym. Uwaga: wszystkie atrybuty mają tę samą globalną przestrzeń nazw. Oznacza to, że nawet jeśli utworzysz nowy atrybut w <declare-styleable>
elemencie, można go użyć poza nim i nie możesz utworzyć innego atrybutu o tej samej nazwie innego typu.
<attr>
Element ma dwa atrybuty xml name
i format
. name
pozwala ci to nazwać czymś i tak w ten sposób ostatecznie odwołujesz się do niego w kodzie, np R.attr.my_attribute
. format
Atrybut może mieć różne wartości w zależności od typu „” atrybutu chcesz.
Możesz ustawić format na wiele typów, używając |
np format="reference|color"
.
enum
atrybuty można zdefiniować w następujący sposób:
<attr name="my_enum_attr">
<enum name="value1" value="1" />
<enum name="value2" value="2" />
</attr>
flag
atrybuty są podobne, z tym wyjątkiem, że wartości muszą zostać zdefiniowane, aby można je było bitować razem:
<attr name="my_flag_attr">
<flag name="fuzzy" value="0x01" />
<flag name="cold" value="0x02" />
</attr>
Oprócz atrybutów istnieje <declare-styleable>
element. Pozwala to zdefiniować atrybuty, których może używać widok niestandardowy. Robisz to poprzez określenie <attr>
elementu, jeśli został wcześniej zdefiniowany, nie określasz format
. Jeśli chcesz ponownie użyć Androida ATTR, na przykład Android: grawitacja, możesz to zrobić w name
następujący sposób.
Przykład niestandardowego widoku <declare-styleable>
:
<declare-styleable name="MyCustomView">
<attr name="my_custom_attribute" />
<attr name="android:gravity" />
</declare-styleable>
Podczas definiowania niestandardowych atrybutów w formacie XML w widoku niestandardowym należy wykonać kilka czynności. Najpierw musisz zadeklarować przestrzeń nazw, aby znaleźć swoje atrybuty. Robisz to na elemencie układu głównego. Zwykle jest tylko xmlns:android="http://schemas.android.com/apk/res/android"
. Musisz teraz również dodaćxmlns:whatever="http://schemas.android.com/apk/res-auto"
.
Przykład:
<?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>
Wreszcie, aby uzyskać dostęp do tego niestandardowego atrybutu, zwykle robi się to w konstruktorze niestandardowego widoku w następujący sposób.
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();
}
Koniec. :)
View
: github.com/commonsguy/cw-advandroid/tree/master/Views/…
xmlns:my="http://schemas.android.com/apk/lib/my.namespace"
- bez kopiowania attrs.xml. Uwaga: ścieżka identyfikatora URI przestrzeni nazw musi mieć postać / apk / * lib * not / apk / res.
apk/lib
sztuczka nie działała dla mnie na niestandardowych atrybutach z formatem referencyjnym z projektu bibliotecznego. Co zrobił praca była do użytku apk/res-auto
, jak zasugerowano w stackoverflow.com/a/13420366/22904 tuż poniżej, a także w stackoverflow.com/a/10217752
enum
i flag
: pierwsza pozwala nam wybrać jedną i tylko jedną wartość, druga pozwala połączyć kilka. Napisałem dłuższą odpowiedź w podobnym pytaniu tutaj i po znalezieniu tego pytania pomyślałem, że będę z nim link.
a.recycle()
jest bardzo ważne, aby zwolnić pamięć
Odpowiedź Qberticusa jest dobra, ale brakuje jednego przydatnego szczegółu. Jeśli implementujesz je w bibliotece, zamień:
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
z:
xmlns:whatever="http://schemas.android.com/apk/res-auto"
W przeciwnym razie aplikacja korzystająca z biblioteki będzie miała błędy w czasie wykonywania.
Powyższa odpowiedź obejmuje wszystko bardzo szczegółowo, z wyjątkiem kilku rzeczy.
Po pierwsze, jeśli nie ma stylów, (Context context, AttributeSet attrs)
do utworzenia preferencji zostanie użyta sygnatura metody. W takim przypadku wystarczy użyć, context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)
aby uzyskać TypedArray.
Po drugie, nie obejmuje to, jak radzić sobie z zasobami kamienia nazębnego (ciągi ilości). Nie można sobie z tym poradzić przy użyciu TypedArray. Oto fragment kodu z mojego SeekBarPreference, który ustawia podsumowanie preferencji formatując jego wartość zgodnie z wartością preferencji. Jeśli xml dla preferencji ustawia android: podsumowanie na ciąg tekstowy lub ciąg znaków resouce, wartość preferencji jest formatowana do ciągu (powinien zawierać% d, aby podnieść wartość). Jeśli Android: podsumowanie jest ustawione na zasób plaurals, to jest on używany do formatowania wyniku.
// 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
metodę preferencji .Tradycyjne podejście jest pełne kodu podstawowego i niezręcznej obsługi zasobów. Dlatego stworzyłem szkielet Spyglass . Aby zademonstrować, jak to działa, oto przykład pokazujący, jak utworzyć niestandardowy widok, który wyświetla tytuł String.
Krok 1: Utwórz niestandardową klasę widoku.
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);
}
}
Krok 2: Zdefiniuj atrybut ciągu w values/attrs.xml
pliku zasobów:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Krok 3: Zastosuj @StringHandler
adnotację do setTitle
metody, aby poinformować środowisko Spyglass, aby kierowała wartość atrybutu do tej metody, gdy widok jest zawyżony.
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Teraz, gdy Twoja klasa ma adnotację Spyglass, struktura Spyglass wykryje ją w czasie kompilacji i automatycznie wygeneruje CustomView_SpyglassCompanion
klasę.
Krok 4: Użyj wygenerowanej klasy w init
metodzie widoku niestandardowego :
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();
}
Otóż to. Teraz, gdy tworzysz instancję klasy z XML, towarzysz Spyglass interpretuje atrybuty i wykonuje wymagane wywołanie metody. Na przykład, jeśli napompujemy następujący układ, setTitle
zostaniemy wywołani z "Hello, World!"
argumentem.
<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>
Struktura nie ogranicza się do zasobów łańcuchowych i zawiera wiele różnych adnotacji do obsługi innych typów zasobów. Zawiera także adnotacje do definiowania wartości domyślnych i przekazywania wartości zastępczych, jeśli metody mają wiele parametrów.
Zobacz repozytorium Github, aby uzyskać więcej informacji i przykładów.
android:title="@{"Hello, world!"}"
.
jeśli pominiesz format
atrybut w attr
elemencie, możesz go użyć do odwołania się do klasy z układów XML.
Refactor > Rename
PracujeFind Usages
Pracujenie określaj format
atrybutu w ... / 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>
użyj go w pliku układu ... / 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>
przeanalizuj klasę w kodzie inicjalizacji widoku ... / 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()
}
}
}