我正在我的应用程序中创建大量编程视图View
。 实际上,它们默认全部具有相同的id=-1
。 为了使用它们,我需要生成唯一的id。
我尝试了几种方法-基于随机数生成和基于当前时间,但是无论如何都不能保证不同的视图将具有不同的id。
只是想知道是否有更可靠的方法来生成唯一的id? 可能有特殊的方法/类吗?
我正在我的应用程序中创建大量编程视图View
。 实际上,它们默认全部具有相同的id=-1
。 为了使用它们,我需要生成唯一的id。
我尝试了几种方法-基于随机数生成和基于当前时间,但是无论如何都不能保证不同的视图将具有不同的id。
只是想知道是否有更可靠的方法来生成唯一的id? 可能有特殊的方法/类吗?
仅想补充Kaj的回答,从API 17级别开始,您可以调用
然后使用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,并且需要将其翻转以避免与静态资源ID发生冲突,翻转后应该在0x01000000之前。
这是对@phantomlimb的回答的补充,
View.generateViewId()
需要API Level >=17,
而这个工具适用于所有API。
根据当前的API级别,
它会决定是否使用系统API。
因此,您可以同时使用ViewIdGenerator.generateViewId()
和View.generateViewId()
,不必担心获取相同的ID。
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();
}
}
}
ViewIdGenerator.generateViewId()
和View.generateViewId()
,那就意味着当前的Android版本(Build.VERSION.SDK_INT)大于等于17。因此,代码中的if (Build.VERSION.SDK_INT < 17) {…}
将永远不会执行。实际上,在这种情况下它总是使用View.generateViewId()
。 :) - fantouchView.generateViewId()
没有reset
,因此这段代码片段也没有。 - fantouch创建一个单例类,其中包含一个原子整数。增加整数并在需要视图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;
}
}
请注意,如果视图“graph”中已经有使用id的视图,则id可能不唯一。 您可以尝试从Integer.MAX_VALUE开始减少而不是从1-> MAX_VALUE。
关于API<17的后备方案,我看到建议的解决方案开始从0或1生成ID。View类还有另一个实例的生成器,并且也从数字1开始计数,这将导致您和View的生成器生成相同的ID,您最终将在您的视图层次结构中拥有具有相同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;
}
}
}
}