程序化视图如何设置唯一ID?

时间:2011-07-22 13:22:50

标签: android view uniqueidentifier

我正在我的应用程序中创建一堆程序化View。因为它们似乎默认具有相同的id=-1。为了使用它们,我需要生成唯一的id。

我尝试了几种方法 - 随机数生成并基于当前时间,但无论如何,没有100%保证不同的视图将具有不同的ID

只是想知道是否有更可靠的方法来生成独特的方法?可能有特殊的方法/类?

5 个答案:

答案 0 :(得分:52)

只想添加Kaj的答案,从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的ID号是为/ res xml文件中定义的静态视图保留的。 (很可能是我项目中R.java的0x7f ******。)

从代码中,Android不希望你使用0作为视图的id,并且需要在0x01000000之前翻转它以避免与静态资源ID的混淆。

答案 1 :(得分:41)

只是@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();
        }

    }
}

答案 2 :(得分:13)

创建一个具有原子整数的单例类。 Bump整数,并在需要视图ID时返回值。

在执行流程期间,id将是唯一的,但在重新启动流程时将重置。

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;
    }
}

请注意,如果视图“图表”中已存在ID,则id可能不是唯一的。你可以尝试从一个Integer.MAX_VALUE数字开始,然后减去它而不是从1 - >; MAX_VALUE

答案 3 :(得分:11)

自支持库27.1.0起,ViewCompat中便具有generateViewId()

ViewCompat.generateViewId()

答案 4 :(得分:2)

关于API&lt; 17的回退解决方案,我看到建议的解决方案从0或1开始生成ID.View类有另一个生成器实例,并且从第一个开始计数,这将导致你的和View生成器生成相同的ID,您最终会在View层次结构中拥有具有相同ID的不同视图。不幸的是,没有一个好的解决方案,但它是一个应该被充分记录的黑客:

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;
        }
    }
}

}