Відповіді:
Відповідно до View
документації
Ідентифікатор не повинен бути унікальним у ієрархії цього перегляду. Ідентифікатор повинен бути додатним числом.
Таким чином, ви можете використовувати будь-яке додатне ціле число, яке вам подобається, але в цьому випадку можуть бути деякі перегляди з еквівалентними ідентифікаторами. Якщо ви хочете шукати певний вигляд в ієрархії, виклик до setTag
деяких ключових об'єктів може бути корисним.
findViewById
повернете перше, що знайде.
setContentView()
має, скажімо, 10 переглядів із встановленим ідентичним номером на той самий ідентифікаційний номер у тій же ієрархії , то виклик findViewById([repeated_id])
поверне перший набір перегляду з цим повтореним ідентифікатором. Це я мав на увазі.
З рівня 17 і вище API ви можете зателефонувати: View.generateViewId ()
Потім використовуйте View.setId (int) .
Якщо ваш додаток орієнтовано нижче рівня 17 API, використовуйте ViewCompat.generateViewId ()
AtomicInteger
реалізацію методів.
for(;;)
я ніколи раніше не бачив. Як це називається?
Ви можете встановити ідентифікатори, які ви будете використовувати пізніше в R.id
класі, використовуючи файл ресурсів xml, і дозволити SDK Android давати їм унікальні значення під час компіляції.
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"/>
Щоб використовувати його в коді:
myEditTextView.setId(R.id.my_edit_text_1);
"int currentId = 1000; whateverView.setId(currentId++);
- Це збільшує ідентифікатор кожного разу currentId++
, забезпечуючи унікальний ідентифікатор, і я можу зберігати Ідентифікатори в моєму ArrayList для подальшого доступу.
<resources>
.
Також ви можете визначити ids.xml
в res/values
. Точний приклад ви можете побачити в зразковому коді Android.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Оскільки API 17, View
клас має статичний метод, generateViewId()
який буде
генерувати значення, придатне для використання в setId (int)
Це працює для мене:
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()
це повільна робота. Підхід працює, але ціною виконання.
(Це був коментар до відповіді дилетанта, але це було занадто довго ... хе-хе)
Звичайно, статика тут не потрібна. Ви можете використовувати SharedPreferences для збереження, а не статичного. Так чи інакше, причина полягає в збереженні поточного прогресу, щоб його не надто повільно було складною схемою. Тому що насправді після його використання один раз буде пізніше. Однак я не вважаю, що це хороший спосіб зробити це, тому що якщо вам доведеться знову перебудувати екран (скажімо,onCreate
викликається знову), то, ймовірно, ви хочете все-таки почати все з початку, усуваючи необхідність статичного. Тому просто зробіть його змінною екземпляра замість статичної.
Ось менша версія, яка працює трохи швидше і може бути простішою для читання:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
Ця вищезазначена функція повинна бути достатньою. Тому що, наскільки я можу сказати, андроїд-генеровані ідентифікатори є в мільярдах, тому це, мабуть, повернеться 1
в перший раз і завжди буде досить швидким. Тому що насправді не буде пробиратися повз використані ідентифікатори, щоб знайти невикористаний. Однак цикл є , якщо він дійсно знайде використаний ідентифікатор.
Однак якщо ви все ще хочете, щоб прогрес був збережений між наступними зменшеннями вашої програми, і ви хочете уникати використання статичних. Ось версія 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;
}
Ця відповідь на подібне запитання повинна розповісти вам все, що вам потрібно знати про ідентифікатори з Android: https://stackoverflow.com/a/13241629/693927
EDIT / FIX: Щойно зрозумів, що я цілком збільшив економію. Я, мабуть, був п’яний.
Бібліотека "Compat" тепер також підтримує generateViewId()
метод для рівнів API до 17.
Просто переконайтеся, що використовується версія Compat
бібліотеки, яка є27.1.0+
Наприклад, у свій build.gradle
файл помістіть:
implementation 'com.android.support:appcompat-v7:27.1.1
Тоді ви можете просто використовувати клас generateViewId()
із ViewCompat
класу замість View
класу, як описано нижче:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
Щасливого кодування!
Просто доповнення до відповіді @phantomlimb,
при цьому View.generateViewId()
потрібен рівень API> = 17,
цей інструмент сумісний з усіма API.
відповідно до поточного рівня API,
він визначає погоду, використовуючи системний API чи ні.
так що ви можете використовувати ViewIdGenerator.generateViewId()
і View.generateViewId()
в той же час і не турбуватися про те , щоб той же ідентифікатор
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 (;;) { … }
походить від вихідного коду Android.
generateViewId()
else { return View.generateViewId(); }
це буде нескінченним циклом для рівня api менше 17 пристроїв?
Для динамічного генерування API перегляду форми Id скористайтеся API 17
Що генерує значення, придатне для використання в setId(int)
. Це значення не зіткнеться зі значеннями ID, згенерованими під час збирання 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();
}
}
}
Я використовую:
public synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
Використовуючи випадкове число, у мене завжди є величезний шанс отримати унікальний ідентифікатор у першій спробі.
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
дають будь-які гарантії щодо того, який вид повертається, якщо є більше одного з тим самим ідентифікатором? Документи нічого не згадують.