Программные представления, как установить уникальные идентификаторы? - PullRequest
28 голосов
/ 22 июля 2011

Я создаю в своем приложении набор программных View с. Как оказалось, все они по умолчанию имеют одинаковые значения id=-1. Чтобы работать с ними, мне нужно создать уникальные идентификаторы.

Я пробовал несколько подходов - генерация случайных чисел и на основе текущего времени, но в любом случае нет 100% гарантии, что разные виды будут иметь разные идентификаторы

Просто интересно, есть ли более надежный способ создания уникальных? Вероятно, есть специальный метод / класс?

Ответы [ 5 ]

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

Просто хотите добавить ответ Кая, с уровня API 17 вы можете вызвать

View.generateViewId ()

, а затем использовать View.setId (int) метод.

Если вам это нужно для целей ниже уровня 17, вот его внутренняя реализация в View.java, которую вы можете использовать непосредственно в вашем проекте:

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 в моих проектах.)

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

41 голосов
/ 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();
        }

    }
}
13 голосов
/ 22 июля 2011

Создать одноэлементный класс с атомарным целым числом. Увеличьте целое число и верните значение, когда вам нужен идентификатор представления.

Идентификатор будет уникальным во время выполнения вашего процесса, но будет сброшен при перезапуске.

public class ViewId {

    private static ViewId INSTANCE = new ViewId();

    private AtomicInteger seq;

    private ViewId() {
        seq = new AtomicInteger(0);
    }

    public int getUniqueId() {
        return seq.incrementAndGet();
    }

    public static ViewId getInstance() {
        return INSTANCE;
    }
}

Обратите внимание, что идентификатор может быть не уникальным, если уже есть представления, которые имеют идентификаторы в представлении «график». Вы можете попробовать начать с числа Integer.MAX_VALUE и уменьшить его вместо перехода от 1 -> MAX_VALUE

11 голосов
/ 10 августа 2018

Поскольку в библиотеке поддержки 27.1.0 есть generateViewId () в ViewCompat

ViewCompat.generateViewId ()

2 голосов
/ 03 сентября 2016

Что касается запасного решения для API <17, я вижу, что предлагаемые решения начинают генерировать идентификаторы, начиная с 0 или 1. Класс View имеет другой экземпляр генератора, а также начинает считать с номера один, что приведет как к вашему, так и к вашему. Генератор представления генерирует одинаковые идентификаторы, и в иерархии представлений у вас будут разные представления с одинаковыми идентификаторами. К сожалению, нет хорошего решения для этого, но это хак, который должен быть хорошо документирован: </p>

public class AndroidUtils {

/**
 *  Unique view id generator, like the one used in {@link View} class for view id generation.
 *  Since we can't access the generator within the {@link View} class before API 17, we create
 *  the same generator here. This creates a problem of two generator instances not knowing about
 *  each other, and we need to take care that one does not generate the id already generated by other one.
 *
 *  We know that all integers higher than 16 777 215 are reserved for aapt-generated identifiers
 *  (source: {@link View#generateViewId()}, so we make sure to never generate a value that big.
 *  We also know that generator within the {@link View} class starts at 1.
 *  We set our generator to start counting at 15 000 000. This gives us enough space
 *  (15 000 000 - 16 777 215), while making sure that generated IDs are unique, unless View generates
 *  more than 15M IDs, which should never happen.
 */
private static final AtomicInteger viewIdGenerator = new AtomicInteger(15000000);

/**
 * Generate a value suitable for use in {@link View#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() {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
        return generateUniqueViewId();
    } else {
        return View.generateViewId();
    }
}

private static int generateUniqueViewId() {
    while (true) {
        final int result = viewIdGenerator.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 (viewIdGenerator.compareAndSet(result, newValue)) {
            return result;
        }
    }
}

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