Respostas:
De acordo com a View
documentação
O identificador não precisa ser exclusivo na hierarquia dessa exibição. O identificador deve ser um número positivo.
Portanto, você pode usar qualquer número inteiro positivo que desejar, mas nesse caso, pode haver algumas visualizações com IDs equivalentes. Se você deseja procurar alguma exibição na hierarquia, chamar setTag
com alguns objetos-chave pode ser útil.
findViewById
retornará a primeira que encontrar.
setContentView()
tiver, digamos, 10 visualizações com o ID definido para o mesmo número de ID na mesma hierarquia , uma chamada para findViewById([repeated_id])
retornará a primeira exibição definida com esse ID repetido. Foi isso que eu quis dizer.
A partir do nível 17 da API, você pode chamar: View.generateViewId ()
Em seguida, use View.setId (int) .
Se seu aplicativo estiver direcionado abaixo do nível da API 17, use ViewCompat.generateViewId ()
AtomicInteger
implementação de métodos.
for(;;)
que eu nunca vi isso antes. Como se chama isso?
Você pode definir os IDs que serão usados posteriormente na R.id
aula usando um arquivo de recurso xml e permitir que o Android SDK forneça valores exclusivos durante o tempo de compilação.
res/values/ids.xml
<item name="my_edit_text_1" type="id"/>
<item name="my_button_1" type="id"/>
<item name="my_time_picker_1" type="id"/>
Para usá-lo no código:
myEditTextView.setId(R.id.my_edit_text_1);
"int currentId = 1000; whateverView.setId(currentId++);
- Isso incrementa o ID toda vez que currentId++
é usado, garantindo um ID exclusivo, e posso armazenar o IDs em meu ArrayList para acesso posterior.
<resources>
.
Também você pode definir ids.xml
em res/values
. Você pode ver um exemplo exato no código de exemplo do Android.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Desde a API 17, a View
classe tem um método estático generateViewId()
que irá
gerar um valor adequado para uso em setId (int)
Isso funciona para mim:
static int id = 1;
// Returns a valid id that isn't in use
public int findId(){
View v = findViewById(id);
while (v != null){
v = findViewById(++id);
}
return id++;
}
findViewById()
é uma operação lenta. A abordagem funciona, mas à custa do desempenho.
(Este foi um comentário à resposta do diletante, mas ficou muito tempo ... hehe)
É claro que uma estática não é necessária aqui. Você pode usar SharedPreferences para salvar, em vez de estático. De qualquer forma, o motivo é salvar o progresso atual para que não seja muito lento para layouts complicados. Porque, de fato, depois de usado uma vez, será bastante rápido mais tarde. No entanto, não acho que seja uma boa maneira de fazê-lo, porque se você precisar reconstruir sua tela novamente (digamos que onCreate
seja chamado novamente), provavelmente desejará recomeçar do início, eliminando a necessidade de estática. Portanto, apenas faça dela uma variável de instância em vez de estática.
Aqui está uma versão menor que roda um pouco mais rápido e pode ser mais fácil de ler:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
Esta função acima deve ser suficiente. Porque, até onde eu sei, os IDs gerados pelo Android estão na casa dos bilhões, então isso provavelmente retornará 1
pela primeira vez e sempre será bastante rápido. Porque, na verdade, ele não passará dos IDs usados para encontrar um ID não utilizado. No entanto, o loop é lá que deve realmente encontrar um ID usado.
No entanto, se você ainda deseja salvar o progresso entre as recriações subsequentes do seu aplicativo e evitar o uso de estática. Aqui está a versão SharedPreferences:
SharedPreferences sp = getSharedPreferences("your_pref_name", MODE_PRIVATE);
public int findUnusedId() {
int fID = sp.getInt("find_unused_id", 0);
while( findViewById(++fID) != null );
SharedPreferences.Editor spe = sp.edit();
spe.putInt("find_unused_id", fID);
spe.commit();
return fID;
}
Esta resposta a uma pergunta semelhante deve informar tudo o que você precisa saber sobre IDs com o Android: https://stackoverflow.com/a/13241629/693927
EDIT / FIX: Acabei de perceber que eu estava totalmente enganado. Eu devo estar bêbado.
A biblioteca 'Compat' agora também suporta o generateViewId()
método para níveis de API anteriores a 17.
Apenas certifique-se de usar uma versão da Compat
biblioteca que é27.1.0+
Por exemplo, no seu build.gradle
arquivo, coloque:
implementation 'com.android.support:appcompat-v7:27.1.1
Em seguida, você pode simplesmente usar o generateViewId()
da ViewCompat
classe em vez da View
classe da seguinte maneira:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
Feliz codificação!
Apenas uma adição à resposta de @phantomlimb,
embora View.generateViewId()
exija nível de API> = 17,
essa ferramenta é compatível com toda a API.
de acordo com o nível atual da API,
ele decide o clima usando a API do sistema ou não.
para que você possa usar ViewIdGenerator.generateViewId()
e View.generateViewId()
ao mesmo tempo e não se preocupe em obter o mesmo ID
import java.util.concurrent.atomic.AtomicInteger;
import android.annotation.SuppressLint;
import android.os.Build;
import android.view.View;
/**
* {@link View#generateViewId()}要求API Level >= 17,而本工具类可兼容所有API Level
* <p>
* 自动判断当前API Level,并优先调用{@link View#generateViewId()},即使本工具类与{@link View#generateViewId()}
* 混用,也能保证生成的Id唯一
* <p>
* =============
* <p>
* while {@link View#generateViewId()} require API Level >= 17, this tool is compatibe with all API.
* <p>
* according to current API Level, it decide weather using system API or not.<br>
* so you can use {@link ViewIdGenerator#generateViewId()} and {@link View#generateViewId()} in the
* same time and don't worry about getting same id
*
* @author fantouchx@gmail.com
*/
public class ViewIdGenerator {
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
@SuppressLint("NewApi")
public static int generateViewId() {
if (Build.VERSION.SDK_INT < 17) {
for (;;) {
final int result = sNextGeneratedId.get();
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
int newValue = result + 1;
if (newValue > 0x00FFFFFF)
newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
} else {
return View.generateViewId();
}
}
}
for (;;) { … }
vem do Código-fonte do Android.
generateViewId()
else { return View.generateViewId(); }
isso vai dar um loop infinito para o nível da API menor que 17 dispositivos?
Para gerar dinamicamente a API 17 do formulário do ID da visualização, use
O que gerará um valor adequado para uso em setId(int)
. Este valor não colidirá com os valores de ID gerados no tempo de compilação pelo aapt para R.id
.
int fID;
do {
fID = Tools.generateViewId();
} while (findViewById(fID) != null);
view.setId(fID);
...
public class Tools {
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
public static int generateViewId() {
if (Build.VERSION.SDK_INT < 17) {
for (;;) {
final int result = sNextGeneratedId.get();
int newValue = result + 1;
if (newValue > 0x00FFFFFF)
newValue = 1; // Roll over to 1, not 0.
if (sNextGeneratedId.compareAndSet(result, newValue)) {
return result;
}
}
} else {
return View.generateViewId();
}
}
}
Eu uso:
public synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
Usando um número aleatório, sempre tenho uma grande chance de obter o ID exclusivo na primeira tentativa.
public String TAG() {
return this.getClass().getSimpleName();
}
private AtomicInteger lastFldId = null;
public int generateViewId(){
if(lastFldId == null) {
int maxFld = 0;
String fldName = "";
Field[] flds = R.id.class.getDeclaredFields();
R.id inst = new R.id();
for (int i = 0; i < flds.length; i++) {
Field fld = flds[i];
try {
int value = fld.getInt(inst);
if (value > maxFld) {
maxFld = value;
fldName = fld.getName();
}
} catch (IllegalAccessException e) {
Log.e(TAG(), "error getting value for \'"+ fld.getName() + "\' " + e.toString());
}
}
Log.d(TAG(), "maxId="+maxFld +" name="+fldName);
lastFldId = new AtomicInteger(maxFld);
}
return lastFldId.addAndGet(1);
}
findViewById
faz qualquer garantia quanto a qual exibição é retornada se houver mais de uma com o mesmo ID? Os documentos não mencionam nada.