Como declaro um elemento da interface do usuário do Android usando XML?
Como declaro um elemento da interface do usuário do Android usando XML?
Respostas:
O Android Developer Guide possui uma seção chamada Building Custom Components . Infelizmente, a discussão dos atributos XML abrange apenas declarar o controle dentro do arquivo de layout e não lidar com os valores dentro da inicialização da classe. Os passos são os seguintes:
values\attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
<attr name="android:text"/>
<attr name="android:textColor"/>
<attr name="extraInformation" format="string" />
</declare-styleable>
</resources>
Observe o uso de um nome não qualificado na declare-styleable
tag. Atributos do Android não padrão, como extraInformation
necessidade de ter seu tipo declarado. As tags declaradas na superclasse estarão disponíveis nas subclasses sem precisar ser redeclaradas.
Como existem dois construtores que usam um AttributeSet
para inicialização, é conveniente criar um método de inicialização separado para os construtores chamarem.
private void init(AttributeSet attrs) {
TypedArray a=getContext().obtainStyledAttributes(
attrs,
R.styleable.MyCustomView);
//Use a
Log.i("test",a.getString(
R.styleable.MyCustomView_android_text));
Log.i("test",""+a.getColor(
R.styleable.MyCustomView_android_textColor, Color.BLACK));
Log.i("test",a.getString(
R.styleable.MyCustomView_extraInformation));
//Don't forget this
a.recycle();
}
R.styleable.MyCustomView
é um int[]
recurso gerado automaticamente , em que cada elemento é o ID de um atributo. Os atributos são gerados para cada propriedade no XML anexando o nome do atributo ao nome do elemento. Por exemplo, R.styleable.MyCustomView_android_text
contém o android_text
atributo para MyCustomView
. Os atributos podem ser recuperados TypedArray
usando várias get
funções. Se o atributo não estiver definido no definido no XML, null
será retornado. Exceto, é claro, se o tipo de retorno for um primitivo; nesse caso, o segundo argumento será retornado.
Se você não deseja recuperar todos os atributos, é possível criar essa matriz manualmente.O ID para os atributos padrão do Android está incluído android.R.attr
, enquanto os atributos deste projeto estão R.attr
.
int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};
Observe que você não deve usar nada android.R.styleable
, pois esse segmento pode mudar no futuro. Ainda está na documentação, pois é útil visualizar todas essas constantes em um único local.
layout\main.xml
Inclua a declaração do espaço para nome xmlns:app="http://schemas.android.com/apk/res-auto"
no elemento xml de nível superior. Os espaços para nome fornecem um método para evitar conflitos que às vezes ocorrem quando esquemas diferentes usam os mesmos nomes de elemento (consulte este artigo para obter mais informações). O URL é simplesmente uma maneira de identificar esquemas exclusivamente - nada precisa ser hospedado nesse URL . Se isso não parece estar fazendo nada, é porque você realmente não precisa adicionar o prefixo do espaço para nome, a menos que precise resolver um conflito.
<com.mycompany.projectname.MyCustomView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:text="Test text"
android:textColor="#FFFFFF"
app:extraInformation="My extra information"
/>
Faça referência à visualização customizada usando o nome completo.
Se você quiser um exemplo completo, veja a amostra de exibição de rótulo do Android.
TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
CharSequences=a.getString(R.styleable.LabelView_text);
<declare-styleable name="LabelView">
<attr name="text"format="string"/>
<attr name="textColor"format="color"/>
<attr name="textSize"format="dimension"/>
</declare-styleable>
<com.example.android.apis.view.LabelView
android:background="@drawable/blue"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
app:text="Blue" app:textSize="20dp"/>
Isso está contido em um LinearLayout
com um atributo de espaço para nome:xmlns:app="http://schemas.android.com/apk/res-auto"
Ótima referência. Obrigado! Uma adição a ele:
Se houver um projeto de biblioteca incluído que tenha declarado atributos personalizados para uma exibição personalizada, você deverá declarar o namespace do projeto, não o da biblioteca. Por exemplo:
Dado que a biblioteca possui o pacote "com.example.library.customview" e o projeto de trabalho possui o pacote "com.example.customview", então:
Não funcionará (mostra o erro "erro: nenhum identificador de recurso encontrado para o atributo 'newAttr' no pacote 'com.example.library.customview'"):
<com.library.CustomView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.library.customview"
android:id="@+id/myView"
app:newAttr="value" />
Funcionará:
<com.library.CustomView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.customview"
android:id="@+id/myView"
app:newAttr="value" />
xmlns:app="http://schemas.android.com/apk/res-auto"
Ver comentário 57 em code.google.com/p/android/issues/detail?id=9656
Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto
res-auto
porque estamos usando o Android Studio e o Gradle. Caso contrário (por exemplo, algumas versões do Eclipse), geralmente terminaria lib/[your package name]
. iehttp://schemas.android.com/apk/lib/[your package name]
Além da resposta mais votada.
Quero adicionar algumas palavras sobre o uso de obtençãoStyledAttributes (), quando criamos uma exibição personalizada usando atributos predefinidos android: xxx. Especialmente quando usamos TextAppearance.
Como foi mencionado em "2. Criando Construtores", a visualização customizada obtém o AttributeSet na sua criação. Uso principal que podemos ver no código fonte do TextView (API 16).
final Resources.Theme theme = context.getTheme();
// TextAppearance is inspected first, but let observe it later
TypedArray a = theme.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.TextView, defStyle, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++)
{
int attr = a.getIndex(i);
// huge switch with pattern value=a.getXXX(attr) <=> a.getXXX(a.getIndex(i))
}
a.recycle();
O que podemos ver aqui?
obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
O conjunto de atributos é processado por tema, de acordo com a documentação. Os valores dos atributos são compilados passo a passo. Os primeiros atributos são preenchidos a partir do tema, os valores são substituídos pelos valores do estilo e, finalmente, os valores exatos do XML para a instância de exibição especial substituem outros.
Matriz de atributos solicitados - com.android.internal.R.styleable.TextView
É uma matriz comum de constantes. Se estamos solicitando atributos padrão, podemos construir essa matriz manualmente.
O que não é mencionado na documentação - ordem dos elementos TypedArray do resultado.
Quando a visualização customizada é declarada em attrs.xml, constantes especiais para índices de atributos são geradas. E podemos extrair valores desta maneira:a.getString(R.styleable.MyCustomView_android_text)
. Mas para o manual int[]
não há constantes. Suponho que getXXXValue (arrayIndex) funcione bem.
E outra pergunta é: "Como podemos substituir constantes internas e solicitar atributos padrão?" Podemos usar android.R.attr. * Valores.
Portanto, se quisermos usar o atributo TextAppearance padrão no modo de exibição personalizado e ler seus valores no construtor, podemos modificar o código do TextView desta maneira:
ColorStateList textColorApp = null;
int textSize = 15;
int typefaceIndex = -1;
int styleIndex = -1;
Resources.Theme theme = context.getTheme();
TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.CustomLabel, defStyle, 0);
TypedArray appearance = null;
int apResourceId = a.getResourceId(R.styleable.CustomLabel_android_textAppearance, -1);
a.recycle();
if (apResourceId != -1)
{
appearance =
theme.obtainStyledAttributes(apResourceId, new int[] { android.R.attr.textColor, android.R.attr.textSize,
android.R.attr.typeface, android.R.attr.textStyle });
}
if (appearance != null)
{
textColorApp = appearance.getColorStateList(0);
textSize = appearance.getDimensionPixelSize(1, textSize);
typefaceIndex = appearance.getInt(2, -1);
styleIndex = appearance.getInt(3, -1);
appearance.recycle();
}
Onde CustomLabel está definido:
<declare-styleable name="CustomLabel">
<!-- Label text. -->
<attr name="android:text" />
<!-- Label text color. -->
<attr name="android:textColor" />
<!-- Combined text appearance properties. -->
<attr name="android:textAppearance" />
</declare-styleable>
Talvez eu esteja enganado de alguma maneira, mas a documentação do Android em obtençãoStyledAttributes () é muito ruim.
Ao mesmo tempo, podemos apenas estender o componente padrão da interface do usuário, usando todos os seus atributos declarados. Essa abordagem não é tão boa, porque o TextView, por exemplo, declara muitas propriedades. E será impossível implementar a funcionalidade completa no onMeasure () e noDraw () substituídos.
Mas podemos sacrificar a ampla reutilização teórica do componente personalizado. Diga "Eu sei exatamente quais recursos vou usar" e não compartilhe código com ninguém.
Então podemos implementar o construtor CustomComponent(Context, AttributeSet, defStyle)
. Após a chamada super(...)
, todos os atributos serão analisados e disponibilizados pelos métodos getter.
Parece que o Google atualizou sua página de desenvolvedor e adicionou vários treinamentos lá.
Um deles trata da criação de visualizações personalizadas e pode ser encontrado aqui
Muito obrigado pela primeira resposta.
Quanto a mim, tive apenas um problema com isso. Ao inflar minha visão, tive um bug: java.lang.NoSuchMethodException: MyView (Context, Attributes)
Eu resolvi isso criando um novo construtor:
public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
// some code
}
Espero que isso ajude!
Você pode incluir qualquer arquivo de layout em outro arquivo de layout, como
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_marginRight="30dp" >
<include
android:id="@+id/frnd_img_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
layout="@layout/include_imagefile"/>
<include
android:id="@+id/frnd_video_file"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
layout="@layout/include_video_lay" />
<ImageView
android:id="@+id/downloadbtn"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_centerInParent="true"
android:src="@drawable/plus"/>
</RelativeLayout>
aqui, os arquivos de layout na tag include são outros arquivos de layout .xml na mesma pasta res.