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 namee format. namepermite chamá-lo de algo e é assim que você acaba se referindo a ele no código, por exemplo R.attr.my_attribute,. O formatatributo 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 nameseguinte 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/libtruque 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
enume 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 onDialogClosedmé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.xmlarquivo de recurso:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
Etapa 3: aplique a @StringHandleranotação ao setTitlemé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_SpyglassCompanionclasse.
Etapa 4: use a classe gerada no initmé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, setTitleele 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 formatatributo do attrelemento, poderá usá-lo para referenciar uma classe a partir de layouts XML.
Refactor > Rename trabalhoFind Usages trabalhonão especifique um formatatributo 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()
}
}
}