Odpowiedzi:
Zgodnie z View
dokumentacją
Identyfikator nie musi być unikalny w hierarchii tego widoku. Identyfikator powinien być liczbą dodatnią.
Możesz więc użyć dowolnej dodatniej liczby całkowitej, którą lubisz, ale w tym przypadku mogą istnieć niektóre widoki o równoważnych identyfikatorach. Jeśli chcesz wyszukać jakiś widok w hierarchii, setTag
przydatne może być wywołanie z niektórymi kluczowymi obiektami.
findViewById
, zwróci pierwszą znalezioną.
setContentView()
ma powiedzmy, 10 widoków z ich identyfikatorem ustawionym na ten sam numer identyfikacyjny w tej samej hierarchii , wówczas wywołanie findViewById([repeated_id])
zwróci pierwszy zestaw widoków z tym jednym powtarzającym się identyfikatorem. O to mi chodziło.
Od poziomu API 17 i wyższego można wywoływać: View.generateViewId ()
Następnie użyj View.setId (int) .
Jeśli twoja aplikacja jest kierowana poniżej poziomu API 17, użyj ViewCompat.generateViewId ()
AtomicInteger
implementację metod.
for(;;)
nigdy wcześniej nie widziałem. Jak to się nazywa?
Możesz ustawić identyfikatory, których będziesz używać później w R.id
klasie, używając pliku zasobów xml, i pozwól, aby zestaw SDK systemu Android nadał im unikalne wartości podczas kompilacji.
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"/>
Aby użyć go w kodzie:
myEditTextView.setId(R.id.my_edit_text_1);
"int currentId = 1000; whateverView.setId(currentId++);
- to zwiększa identyfikator za każdym razem currentId++
, zapewniając unikalny identyfikator, i mogę przechowywać Identyfikatory w mojej ArrayList do późniejszego dostępu.
<resources>
.
Możesz także zdefiniować ids.xml
w res/values
. Dokładny przykład można zobaczyć w przykładowym kodzie Androida.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Od API 17 View
klasa ma statyczną metodę, generateViewId()
która będzie
wygeneruj wartość odpowiednią do użycia w setId (int)
To działa dla mnie:
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()
jest powolną operacją. Podejście to działa, ale kosztem wydajności.
(To był komentarz do odpowiedzi dilettante, ale stał się za długi ... hehe)
Oczywiście statyczny nie jest tutaj potrzebny. Możesz użyć SharedPreferences do zapisania, zamiast statycznego. Tak czy inaczej, powodem jest zapisanie bieżącego postępu, aby nie był zbyt wolny dla skomplikowanych układów. Ponieważ w rzeczywistości po jednorazowym użyciu będzie później dość szybki. Jednak nie uważam, że jest to dobry sposób, aby to zrobić, ponieważ jeśli musisz ponownie przebudować ekran (powiedzmy, że onCreate
jest wywoływany ponownie), prawdopodobnie i tak chcesz zacząć od początku, eliminując potrzebę statycznego. Dlatego po prostu ustaw zmienną instancji zamiast statycznej.
Oto mniejsza wersja, która działa nieco szybciej i może być łatwiejsza do odczytania:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
Ta powyższa funkcja powinna wystarczyć. Ponieważ, o ile wiem, generowane przez Androida identyfikatory są w miliardach, więc prawdopodobnie wróci 1
za pierwszym razem i zawsze będzie dość szybkie. Ponieważ tak naprawdę nie będzie pętli obok używanych identyfikatorów, aby znaleźć nieużywany. Jednak pętla to nie powinno to rzeczywiście znaleźć używany identyfikator.
Jeśli jednak nadal chcesz, aby postęp był zapisywany między kolejnymi odtworzeniami aplikacji i chcesz uniknąć korzystania ze statycznego. Oto wersja 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;
}
Ta odpowiedź na podobne pytanie powinna powiedzieć ci wszystko, co musisz wiedzieć o identyfikatorach w Androidzie: https://stackoverflow.com/a/13241629/693927
EDYCJA / POPRAWKA: Właśnie zdałem sobie sprawę, że całkowicie wygłupiłem rzut obronny. Musiałem być pijany.
Biblioteka „Compat” obsługuje teraz także generateViewId()
metodę poziomów API wcześniejszych niż 17.
Upewnij się tylko, że używasz wersji Compat
biblioteki27.1.0+
Na przykład w build.gradle
pliku umieść:
implementation 'com.android.support:appcompat-v7:27.1.1
Następnie możesz po prostu użyć generateViewId()
z ViewCompat
klasy zamiast z View
klasy w następujący sposób:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
Miłego kodowania!
Tylko dodatek do odpowiedzi @phantomlimb,
natomiast View.generateViewId()
wymagają API poziom> = 17,
to narzędzie jest compatibe ze wszystkimi API.
zgodnie z bieżącym poziomem interfejsu API
decyduje o pogodzie przy użyciu systemowego interfejsu API lub nie.
dzięki czemu można używać ViewIdGenerator.generateViewId()
i View.generateViewId()
w tym samym czasie i nie martw się o uzyskanie tego samego identyfikatora
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 (;;) { … }
pochodzi z kodu źródłowego Androida.
generateViewId()
else { return View.generateViewId(); }
czy będzie to nieskończona pętla dla poziomu interfejsu API mniejszego niż 17 urządzeń?
W celu dynamicznego generowania Widoku formularza należy użyć API 17
Co wygeneruje wartość odpowiednią do użycia w setId(int)
. Ta wartość nie będzie kolidować z wartościami ID wygenerowanymi w czasie kompilacji przez aapt for 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();
}
}
}
Używam:
public synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
Używając losowej liczby, zawsze mam ogromną szansę na uzyskanie unikalnego identyfikatora za pierwszym razem.
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
jakiekolwiek gwarancje, który widok zostanie zwrócony, jeśli istnieje więcej niż jeden z tym samym identyfikatorem? Dokumenty nic nie wspominają.