Я добавляю TextViews программно в цикл for и добавляю их в ArrayList.
Как использовать TextView.setId(int id)
? Какой Integer ID я придумал, чтобы он не противоречил другим идентификаторам?
Я добавляю TextViews программно в цикл for и добавляю их в ArrayList.
Как использовать TextView.setId(int id)
? Какой Integer ID я придумал, чтобы он не противоречил другим идентификаторам?
Согласно View
документации
Идентификатор не должен быть уникальным в этой иерархии представлений. Идентификатор должен быть положительным числом.
Итак, вы можете использовать любое положительное целое число, которое вам нравится, но в этом случае могут быть некоторые представления с эквивалентными идентификаторами. Если вы хотите найти какое-либо представление в иерархии, вызывающее setTag
с некоторыми ключевыми объектами, может быть удобно.
Google наконец осознал необходимость создания уникальных идентификаторов для программно созданных просмотров...
От уровня API 17 и выше вы можете позвонить
Затем используйте View.setId(int).
Если вам нужно это для целей ниже уровня 17, вот его внутренняя реализация в View.java, которую вы можете использовать непосредственно в своем проекте, поместить в свой класс util или где-нибудь:
private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
/**
* Generate a value suitable for use in {@link #setId(int)}.
* This value will not collide with ID values generated at build time by aapt for R.id.
*
* @return a generated ID value
*/
public static int generateViewId() {
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;
}
}
}
Идентификационный номер, превышающий 0x00FFFFFF, зарезервирован для статических представлений, определенных в файлах /res xml. (Скорее всего, 0x7f ****** из R.java в моих проектах.)
В вашем коде вы можете:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
myView.setId(Utils.generateViewId());
} else {
myView.setId(View.generateViewId());
}
Вы можете установить идентификатор, который вы будете использовать позже в классе 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);
Также вы можете определить ids.xml
в res/values
. Вы можете увидеть точный пример кода примера для Android.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Это работает для меня:
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++;
}
Поскольку API 17, класс View
имеет статический метод generateViewId()
, который будет
генерирует значение, подходящее для использования в setId (int)
(Это был комментарий к ответу дилетанта, но он слишком долго... хе-хе)
Конечно, статика здесь не нужна. Вы можете использовать SharedPreferences для сохранения вместо статического. В любом случае, причина заключается в том, чтобы сохранить текущий прогресс, чтобы он не слишком медленный для сложных макетов. Потому что, по сути, после его использования один раз, он будет довольно быстрым позже. Тем не менее, я не считаю, что это хороший способ сделать это, потому что, если вам нужно снова перестроить свой экран (скажем, onCreate
снова вызывается), вы, вероятно, захотите начать все сначала, устраняя необходимость в статике. Поэтому просто сделайте вместо него переменную экземпляра, а не static.
Вот небольшая версия, которая работает немного быстрее и может быть легче читать:
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: fooobar.com/questions/28127/...
РЕДАКТИРОВАТЬ/ИСПРАВИТЬ: Я понял, что полностью сработал. Я, должно быть, был пьян.
Просто добавление к ответу @phantomlimb,
while View.generateViewId()
требуется API Level >= 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 [email protected]
*/
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();
}
}
}
Библиотека "Совместимость" теперь также поддерживает метод 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()
Счастливое кодирование!
Чтобы динамически генерировать API-интерфейс формы представления, используйте
Будет генерироваться значение, подходящее для использования в setId(int)
. Это значение не будет сталкиваться с значениями ID, сгенерированными во время сборки, с помощью 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);
}
Мой выбор:
// Method that could us an unique id
int getUniqueId(){
return (int)
SystemClock.currentThreadTimeMillis();
}