Eu preciso implementar meus próprios atributos como em com.android.R.attr
Não encontrei nada na documentação oficial, por isso preciso de informações sobre como definir esses atributos e como usá-los no meu código.
Eu preciso implementar meus próprios atributos como em com.android.R.attr
Não encontrei nada na documentação oficial, por isso preciso de informações sobre como definir esses atributos e como usá-los no meu código.
Respostas:
Atualmente, a melhor documentação é a fonte. Você pode dar uma olhada aqui (attrs.xml) .
Você pode definir atributos no <resources>
elemento superior ou dentro de um <declare-styleable>
elemento. Se eu vou usar um attr em mais de um lugar, eu o coloco no elemento raiz. Observe que todos os atributos compartilham o mesmo espaço para nome global. Isso significa que, mesmo que você crie um novo atributo dentro de um <declare-styleable>
elemento, ele poderá ser usado fora dele e você não poderá criar outro atributo com o mesmo nome de um tipo diferente.
Um <attr>
elemento possui dois atributos xml name
e format
. name
permite chamá-lo de algo e é assim que você acaba se referindo a ele no código, por exemplo R.attr.my_attribute
,. O format
atributo pode ter valores diferentes, dependendo do 'tipo' de atributo que você deseja.
Você pode definir o formato para vários tipos usando |
, por exemplo format="reference|color"
,.
enum
atributos podem ser definidos da seguinte maneira:
<attr name="my_enum_attr">
<enum name="value1" value="1" />
<enum name="value2" value="2" />
</attr>
flag
Os atributos são semelhantes, exceto que os valores precisam ser definidos para que possam ser agrupados:
<attr name="my_flag_attr">
<flag name="fuzzy" value="0x01" />
<flag name="cold" value="0x02" />
</attr>
Além dos atributos, existe o <declare-styleable>
elemento Isso permite definir atributos que uma exibição personalizada pode usar. Você faz isso especificando um <attr>
elemento; se ele foi definido anteriormente, você não especifica o format
. Se você deseja reutilizar um atributo android, por exemplo, android: gravity, faça isso da name
seguinte maneira.
Um exemplo de uma visualização personalizada <declare-styleable>
:
<declare-styleable name="MyCustomView">
<attr name="my_custom_attribute" />
<attr name="android:gravity" />
</declare-styleable>
Ao definir seus atributos customizados em XML em sua visualização customizada, você precisa fazer algumas coisas. Primeiro você deve declarar um espaço para nome para encontrar seus atributos. Você faz isso no elemento de layout raiz. Normalmente existe apenas xmlns:android="http://schemas.android.com/apk/res/android"
. Agora você também deve adicionar xmlns:whatever="http://schemas.android.com/apk/res-auto"
.
Exemplo:
<?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>
Por fim, para acessar esse atributo customizado, você normalmente o faz no construtor de sua visualização customizada, como a seguir.
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();
}
O fim. :)
View
: github.com/commonsguy/cw-advandroid/tree/master/Views/…
xmlns:my="http://schemas.android.com/apk/lib/my.namespace"
- sem copiar attrs.xml. Observe que o caminho do URI do namespace deve ser / apk / * lib * not / apk / res.
apk/lib
truque não funcionou para mim em atributos personalizados com formato de referência de um projeto de biblioteca. O que fez trabalho foi o uso apk/res-auto
, como sugerido na stackoverflow.com/a/13420366/22904 logo abaixo e também no stackoverflow.com/a/10217752
enum
e flag
: o primeiro nos permite escolher um e apenas um valor, o último nos permite combinar vários. Escrevi uma resposta mais longa em uma pergunta semelhante aqui e, tendo encontrado essa pergunta, achei que iria vincular a ela.
a.recycle()
é muito importante aqui para liberar memória
A resposta do Qberticus é boa, mas falta um detalhe útil. Se você os estiver implementando em uma biblioteca, substitua:
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
com:
xmlns:whatever="http://schemas.android.com/apk/res-auto"
Caso contrário, o aplicativo que usa a biblioteca terá erros de tempo de execução.
A resposta acima cobre tudo em grande detalhe, além de algumas coisas.
Primeiro, se não houver estilos, a (Context context, AttributeSet attrs)
assinatura do método será usada para instanciar a preferência. Nesse caso, use apenas context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)
para obter o TypedArray.
Em segundo lugar, não aborda como lidar com os recursos das plurais (sequências de quantidade). Isso não pode ser tratado com o TypedArray. Aqui está um trecho de código do meu SeekBarPreference que define o resumo da preferência, formatando seu valor de acordo com o valor da preferência. Se o xml da preferência definir android: summary como uma sequência de texto ou uma sequência de caracteres, o valor da preferência será formatado na sequência de caracteres (ele deve ter% d, para selecionar o valor). Se android: summary estiver definido como um recurso de plaurals, isso será usado para formatar o 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()
o onDialogClosed
método da preferência .A abordagem tradicional é cheia de código padrão e manipulação de recursos desajeitada. Foi por isso que criei a estrutura Spyglass . Para demonstrar como funciona, aqui está um exemplo que mostra como criar uma exibição personalizada que exibe um título de String.
Etapa 1: Crie uma classe de exibição 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);
}
}
Etapa 2: defina um atributo de sequência no values/attrs.xml
arquivo de recurso:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Etapa 3: aplique a @StringHandler
anotação ao setTitle
método para informar à estrutura do Spyglass para rotear o valor do atributo para esse método quando a exibição for inflada.
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
Agora que sua classe possui uma anotação Spyglass, a estrutura Spyglass a detectará em tempo de compilação e gerará automaticamente a CustomView_SpyglassCompanion
classe.
Etapa 4: use a classe gerada no init
método da visualização 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();
}
É isso aí. Agora, quando você instancia a classe a partir do XML, o companheiro Spyglass interpreta os atributos e faz a chamada de método necessária. Por exemplo, se inflarmos o seguinte layout, setTitle
ele será chamado com "Hello, World!"
o 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>
A estrutura não se limita aos recursos de cadeia de caracteres e possui muitas anotações diferentes para manipular outros tipos de recursos. Ele também possui anotações para definir valores padrão e para passar valores de espaço reservado, se seus métodos tiverem vários parâmetros.
Dê uma olhada no repositório do Github para obter mais informações e exemplos.
android:title="@{"Hello, world!"}"
.
se você omitir o format
atributo do attr
elemento, poderá usá-lo para referenciar uma classe a partir de layouts XML.
Refactor > Rename
trabalhoFind Usages
trabalhonão especifique um format
atributo em ... / 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>
use-o em algum arquivo de layout ... / 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>
analise a classe no seu código de inicialização de exibição ... / 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()
}
}
}