プログラマティックView
sのアプリの束を作成しています。あるように見えたので、デフォルトではすべて同じid=-1
。それらを使用するには、一意のIDを生成する必要があります。
私はいくつかのアプローチを試みました-乱数の生成と現在の時間に基づいていますが、とにかくビューごとに異なるIDを持つことを100%保証することはできません。
ユニークなものを生成するより信頼できる方法はありますか?おそらく特別なメソッド/クラスがありますか?
@phantomlimbの答えに加えて、
View.generateViewId()
にはAPIレベル> = 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 [email protected]
*/
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();
}
}
}
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は、ビューのIDとして0を使用したくないので、静的リソースIDとの競合を避けるために、0x01000000の前に反転する必要があります。
アトミック整数を持つシングルトンクラスを作成します。整数をバンプし、ビュー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から減らすことができます。
サポートライブラリ27.1.0以降、ViewCompatにgenerateViewId()があります。
API <17のフォールバックソリューションに関して、提案されたソリューションは0または1から始まるIDの生成を開始することがわかります。Viewクラスにはジェネレーターの別のインスタンスがあり、番号1からカウントを開始します。これにより、とビューのジェネレーターの両方が生成されます同じ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;
}
}
}
}