Necesito implementar mis propios atributos como en com.android.R.attr
No encontré nada en la documentación oficial, así que necesito información sobre cómo definir estos atributos y cómo usarlos desde mi código.
Necesito implementar mis propios atributos como en com.android.R.attr
No encontré nada en la documentación oficial, así que necesito información sobre cómo definir estos atributos y cómo usarlos desde mi código.
Respuestas:
Actualmente la mejor documentación es la fuente. Puedes echarle un vistazo aquí (attrs.xml) .
Puede definir atributos en el <resources>
elemento superior o dentro de un <declare-styleable>
elemento. Si voy a usar un atributo en más de un lugar, lo pongo en el elemento raíz. Tenga en cuenta que todos los atributos comparten el mismo espacio de nombres global. Eso significa que incluso si crea un nuevo atributo dentro de un<declare-styleable>
elemento, puede usarse fuera de él y no puede crear otro atributo con el mismo nombre de un tipo diferente.
Un <attr>
elemento tiene dos atributos xml name
y format
. name
le permite llamar a algo y esto es como se llega a referirse a ella en el código, por ejemplo, R.attr.my_attribute
. El format
atributo puede tener valores diferentes según el 'tipo' de atributo que desee.
Puede establecer el formato de múltiples tipos utilizando |
, por ejemplo, format="reference|color"
.
enum
Los atributos se pueden definir de la siguiente manera:
<attr name="my_enum_attr">
<enum name="value1" value="1" />
<enum name="value2" value="2" />
</attr>
flag
los atributos son similares, excepto que los valores necesitan ser definidos para que puedan ser mordidos juntos:
<attr name="my_flag_attr">
<flag name="fuzzy" value="0x01" />
<flag name="cold" value="0x02" />
</attr>
Además de los atributos existe el <declare-styleable>
elemento. Esto le permite definir atributos que una vista personalizada puede usar. Para ello, especifica un <attr>
elemento, si se definió previamente, no especifique el format
. Si desea reutilizar un atributo de Android, por ejemplo, android: gravity, puede hacerlo de la name
siguiente manera.
Un ejemplo de una vista personalizada <declare-styleable>
:
<declare-styleable name="MyCustomView">
<attr name="my_custom_attribute" />
<attr name="android:gravity" />
</declare-styleable>
Al definir sus atributos personalizados en XML en su vista personalizada, debe hacer algunas cosas. Primero debe declarar un espacio de nombres para encontrar sus atributos. Hace esto en el elemento de diseño raíz. Normalmente solo hay xmlns:android="http://schemas.android.com/apk/res/android"
. Ahora también debes agregar xmlns:whatever="http://schemas.android.com/apk/res-auto"
.
Ejemplo:
<?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>
Finalmente, para acceder a ese atributo personalizado, normalmente lo hace en el constructor de su vista personalizada de la siguiente manera.
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();
}
El fin. :)
View
: github.com/commonsguy/cw-advandroid/tree/master/Views/…
xmlns:my="http://schemas.android.com/apk/lib/my.namespace"
, sin copiar attrs.xml. Tenga en cuenta que la ruta URI del espacio de nombres debe ser / apk / * lib * not / apk / res.
apk/lib
truco no funcionó para mí en atributos personalizados con formato de referencia de un proyecto de biblioteca. Lo que sí funcionó fue usar apk/res-auto
, como se sugiere en stackoverflow.com/a/13420366/22904 justo debajo y también en stackoverflow.com/a/10217752
enum
y flag
: el primero nos permite elegir un único valor, el segundo nos permite combinar varios. Escribí una respuesta más larga en una pregunta similar aquí , y ahora que he encontrado esta pregunta, pensé que vincularía a eso.
a.recycle()
es muy importante aquí para liberar memoria
La respuesta de Qberticus es buena, pero falta un detalle útil. Si está implementando estos en una biblioteca, reemplace:
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
con:
xmlns:whatever="http://schemas.android.com/apk/res-auto"
De lo contrario, la aplicación que usa la biblioteca tendrá errores de tiempo de ejecución.
La respuesta anterior cubre todo con gran detalle, aparte de un par de cosas.
Primero, si no hay estilos, la (Context context, AttributeSet attrs)
firma del método se usará para instanciar la preferencia. En este caso, solo use context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)
para obtener el TypedArray.
En segundo lugar, no cubre cómo tratar con los recursos naturales (cadenas de cantidad). No se pueden tratar con TypedArray. Aquí hay un fragmento de código de mi SeekBarPreference que establece el resumen de la preferencia formateando su valor de acuerdo con el valor de la preferencia. Si el xml para la preferencia establece android: summary en una cadena de texto o una cadena de recursos, el valor de la preferencia está formateado en la cadena (debe tener% d, para recoger el valor). Si android: summary está configurado como un recurso plaural, entonces se usa para formatear el resultado.
// 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()
al onDialogClosed
método de preferencia .El enfoque tradicional está lleno de código repetitivo y manejo de recursos torpe. Es por eso que hice el framework Spyglass . Para demostrar cómo funciona, aquí hay un ejemplo que muestra cómo hacer una vista personalizada que muestre un título de Cadena.
Paso 1: crea una clase de vista personalizada.
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);
}
}
Paso 2: defina un atributo de cadena en el values/attrs.xml
archivo de recursos:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Paso 3: aplique la @StringHandler
anotación al setTitle
método para indicarle al marco de Spyglass que enrute el valor del atributo a este método cuando la vista se infla.
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Ahora que su clase tiene una anotación Spyglass, el marco Spyglass lo detectará en tiempo de compilación y generará automáticamente la CustomView_SpyglassCompanion
clase.
Paso 4: use la clase generada en el init
método de la vista personalizada :
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();
}
Eso es. Ahora, cuando crea una instancia de la clase desde XML, el compañero Spyglass interpreta los atributos y realiza la llamada al método requerido. Por ejemplo, si inflamos el siguiente diseño setTitle
, se llamará con "Hello, World!"
el argumento.
<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>
El marco no se limita a los recursos de cadena tiene muchas anotaciones diferentes para manejar otros tipos de recursos. También tiene anotaciones para definir valores predeterminados y para pasar valores de marcador de posición si sus métodos tienen múltiples parámetros.
Eche un vistazo al repositorio de Github para obtener más información y ejemplos.
android:title="@{"Hello, world!"}"
.
si omite el format
atributo del attr
elemento, puede usarlo para hacer referencia a una clase de diseños XML.
Refactor > Rename
trabajosFind Usages
trabajosno especifique un format
atributo en ... / 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>
úselo en algún archivo de diseño ... / 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>
analizar la clase en su código de inicialización de vista ... / 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()
}
}
}