Ответы:
Согласно View
документации
Идентификатор не обязательно должен быть уникальным в иерархии этого представления. Идентификатор должен быть положительным числом.
Таким образом, вы можете использовать любое положительное целое число, которое вам нравится, но в этом случае могут быть некоторые представления с эквивалентными идентификаторами. Если вы хотите найти какое-то представление в иерархии, setTag
может пригодиться обращение к некоторым ключевым объектам.
findViewById
вернет первое, что он найдет.
setContentView()
, скажем, 10 видов с идентификатором, установленным на один и тот же номер идентификатора в одной и той же иерархии , то при вызове findViewById([repeated_id])
будет возвращен первый набор с этим повторяющимся идентификатором. Это то, что я имел в виду.
Начиная с уровня API 17 и выше, вы можете вызывать: View.generateViewId ()
Затем используйте View.setId (int) .
Если ваше приложение нацелено ниже уровня API 17, используйте ViewCompat.generateViewId ()
AtomicInteger
реализацию методов.
for(;;)
я такого никогда раньше не видел. Как это называется?
Вы можете установить идентификаторы, которые будете использовать позже в R.id
классе, используя файл ресурсов xml, и позволить Android SDK давать им уникальные значения во время компиляции.
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;
}
Эта функция должна быть достаточной. Потому что, насколько я могу судить, сгенерированные Android идентификаторы исчисляются миллиардами, так что это, вероятно, вернется 1
в первый раз и всегда будет довольно быстрым. Потому что на самом деле он не будет проходить мимо используемых идентификаторов, чтобы найти неиспользуемый. Однако, цикл является там действительно должно найти подержанный ID.
Однако, если вы все еще хотите сохранить прогресс между последующими воссозданиями вашего приложения и хотите избежать использования статического. Вот версия 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
РЕДАКТИРОВАТЬ / ИСПРАВИТЬ: Просто понял, что я полностью облажался. Должно быть, я был пьян.
Библиотека Compat теперь также поддерживает generateViewId()
метод уровней API до 17.
Просто убедитесь, что вы используете версию Compat
библиотеки, которая27.1.0+
Например, в вашем build.gradle
файле введите :
implementation 'com.android.support:appcompat-v7:27.1.1
Тогда вы можете просто использовать класс generateViewId()
from 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 формы View Id используйте 17
Который будет генерировать значение, пригодное для использования в setId(int)
. Это значение не будет конфликтовать со значениями идентификатора, сгенерированными во время сборки aapt для 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
дает гарантии относительно того, какое представление возвращается, если существует более одного с одним и тем же идентификатором? В документах ничего не упоминается.