Android: Просмотр.setID (int id) программно-как избежать конфликтов ID?
Я добавляю текстовые представления программно в цикл for и добавляю их в ArrayList.
Как использовать TextView.setId(int id)
? Какой целочисленный идентификатор я придумываю, чтобы он не конфликтовал с другими идентификаторами?
14 ответов:
по данным
View
документацияидентификатор не должен быть уникальным в иерархии этого вида. Идентификатор должен быть положительным числом.
Так что вы можете использовать любое положительное целое число, но в данном случае не может быть взгляды с эквивалентом личности. Если вы ищите какой-нибудь вид в иерархии, призывающие
setTag
С некоторыми ключевыми объектами может быть удобно.
Google наконец осознал необходимость создания уникальных идентификаторов для программно созданных представлений...
С уровня API 17 и выше, вы можете позвонить
затем использовать вид.setId (int).
в случае, если вам это нужно для целей ниже уровня 17, вот его внутренняя реализация в поле зрения.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 зарезервирован для статических представлений, определенных в xml-файлах /res. (Скорее всего 0x7f***** * от R.java в моих проектах.)
в коде, вы можете сделать:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { myView.setId(Utils.generateViewId()); } else { myView.setId(View.generateViewId()); }
вы можете установить ID, который вы будете использовать позже в
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
inres/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
вызывается опять же), то вы, вероятно, хотите начать с самого начала, так или иначе, устраняя необходимость статики. Поэтому просто сделайте его переменной экземпляра вместо статического.вот уменьшенная версия, которая работает немного быстрее и может быть легче читать:
int fID = 0; public int findUnusedId() { while( findViewById(++fID) != null ); return fID; }
этой функции должно быть достаточно. Потому что, насколько я могу судить, android-генерируемые идентификаторы находятся в миллиардах, так что это, вероятно, вернется
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
file, put:
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()
в в то же время и не беспокойтесь о получении того же idimport 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(); } } }
для того, чтобы динамически генерировать вид код форма по API 17 использовать
которая будет генерировать значение, подходящее для использования в
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); }