Android: View.setID (int id) программно - как избежать конфликтов ID? - PullRequest
294 голосов
/ 11 ноября 2009

Я программно добавляю TextViews в цикл for и добавляю их в ArrayList.

Как мне использовать TextView.setId(int id)? Какой Integer ID мне нужен, чтобы он не конфликтовал с другими ID?

Ответы [ 14 ]

505 голосов
/ 16 марта 2013

На уровне API 17 и выше вы можете вызвать: View.generateViewId ()

Затем используйте View.setId (int) .

Если ваше приложение нацелено ниже уровня API 17, используйте ViewCompat.generateViewId ()

135 голосов
/ 11 ноября 2009

Согласно View документации

Идентификатор не обязательно должен быть уникальным в иерархии этого представления. Идентификатор должен быть положительным числом.

Таким образом, вы можете использовать любое положительное целое число, которое вам нравится, но в этом случае могут быть некоторые представления с эквивалентными идентификаторами. Если вы хотите найти какое-то представление в иерархии, может пригодиться вызов setTag с некоторыми ключевыми объектами.

122 голосов
/ 05 апреля 2014

Вы можете установить идентификаторы, которые будете использовать позже в 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);
59 голосов
/ 27 ноября 2009

Также вы можете определить ids.xml в res/values. Точный пример вы можете увидеть в примере кода Android.

samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
26 голосов
/ 20 февраля 2013

Начиная с API 17, класс View имеет статический метод generateViewId(), который будет

генерирует значение, подходящее для использования в setId (int)

25 голосов
/ 23 июля 2012

Это работает для меня:

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++;  
}
10 голосов
/ 27 января 2013

(Это был комментарий к ответу дилетанта, но он слишком длинный ... хе-хе)

Конечно, статика здесь не нужна. Вы можете использовать SharedPreferences для сохранения вместо статического. В любом случае, причина заключается в том, чтобы сохранить текущий прогресс, чтобы он не был слишком медленным для сложных макетов. Потому что, на самом деле, после того, как его использовали один раз, это будет довольно быстро позже. Однако я не чувствую, что это хороший способ сделать это, потому что если вам придется перестраивать ваш экран снова (скажем, onCreate снова вызывается), то вы, вероятно, все равно захотите начать сначала, исключая необходимость в статическом. Поэтому просто сделайте его переменной экземпляра вместо статического.

Вот уменьшенная версия, которая работает немного быстрее и может быть проще для чтения:

int fID = 0;

public int findUnusedId() {
    while( findViewById(++fID) != null );
    return fID;
}

Эта функция должна быть достаточной. Потому что, насколько я могу судить, сгенерированные Android идентификаторы исчисляются миллиардами, так что это, вероятно, вернет 1 в первый раз и всегда будет довольно быстрым. Потому что на самом деле он не будет проходить мимо используемых идентификаторов, чтобы найти неиспользуемый. Тем не менее, цикл равен , если он действительно должен найти используемый идентификатор.

Однако, если вы все еще хотите сохранить прогресс между последующими воссозданиями вашего приложения и хотите избежать использования static. Вот версия 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

РЕДАКТИРОВАТЬ / ИСПРАВИТЬ: Просто понял, что я полностью облажался. Должно быть, я был пьян.

8 голосов
/ 20 июля 2018

Библиотека 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()

Удачного кодирования!

6 голосов
/ 08 января 2014

Просто дополнение к ответу @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();
        }

    }
}
3 голосов
/ 13 августа 2013

Чтобы динамически генерировать API-интерфейс формы View Id 17, используйте

generateViewId ()

, который сгенерирует значение, подходящее для использования в setId(int). Это значение не будет конфликтовать со значениями идентификатора, сгенерированными во время сборки aapt для R.id.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...