Android:以编程方式View.setID(int id) – 如何避免ID冲突?
我以编程方式在for循环中添加TextView,并将它们添加到ArrayList。
我如何使用TextView.setId(int id)
? 什么整数ID我想出来,所以它不与其他ID冲突?
根据View
文档
标识符在该视图的层次结构中不必是唯一的。 标识符应该是一个正数。
所以你可以使用你喜欢的任何正整数,但在这种情况下,可以有一些视图与等效的ID。 如果你想在层次结构中search一些视图,调用setTag
和一些关键对象可能会很方便。
Google终于意识到需要为编程创build的视图生成唯一的ID …
从API级别17和以上,你可以打电话
View.generateViewId()
然后使用View.setId(int) 。
如果你需要它低于17级的目标,这里是它在View.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的ID号保留给在/ res xml文件中定义的静态视图。 (很可能来自我的项目中的R.java的0x7f ******。)
在你的代码中,你可以这样做:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) { myView.setId(Utils.generateViewId()); } else { myView.setId(View.generateViewId()); }
您可以使用xml资源文件设置稍后在R.id
类中使用的ID,并让Android SDK在编译期间R.id
提供唯一值。
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);
你也可以在res/values
定义ids.xml
。 你可以在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)中使用的值
(这是对dilettante的回答评论,但它太长了…嘿嘿)
当然,静态在这里是不需要的。 你可以使用SharedPreferences来保存,而不是静态的。 无论哪种方式,原因是保存当前的进度,以便其复杂的布局不会太慢。 因为其实在使用一次之后,会比较快。 但是,我不觉得这是一个很好的方法,因为如果你不得不再次重新onCreate
你的屏幕(比如再次调用onCreate
),那么你可能想从头开始,无需任何静电。 因此,只要把它变成一个实例variables而不是静态的。
这是一个更小的版本,运行速度更快,可能更易于阅读:
int fID = 0; public int findUnusedId() { while( findViewById(++fID) != null ); return fID; }
上述function应该足够了。 因为,据我所知,Android生成的身份证是在数十亿,所以这可能会返回1
第一次,总是相当快。 因为它实际上不会循环使用的ID来find一个未使用的ID。 但是,循环是否应该实际上find一个使用的ID。
但是,如果您仍希望在应用程序的后续重新创build之间保存进度,并且希望避免使用静态。 这是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; }
这个类似的问题的答案应该告诉你一切你需要知道的ID与Android: https : //stackoverflow.com/a/13241629/693927
编辑/修正:刚才意识到我完全疯狂保存。 我一定喝醉了。
除了@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(); } } }
为了dynamic生成View Id表单API 17使用
generateViewId()
这将生成一个适合在setId(int)
使用的值。 此值不会与生成时由aapt生成的ID值相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 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); }
我用:
public synchronized int generateViewId() { Random rand = new Random(); int id; while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null); return id; }
通过使用一个随机数我总是有一个很大的机会获得唯一的身份证在第一次尝试。