回答:
View
ドキュメントによると
識別子は、このビューの階層内で一意である必要はありません。識別子は正の数でなければなりません。
したがって、任意の正の整数を使用できますが、この場合、同等のIDを持つビューがいくつか存在する可能性があります。階層内のビューを検索する場合はsetTag
、いくつかのキーオブジェクトを使用してを呼び出すと便利です。
findViewById
、最初に見つかったを返します。
setContentView()
、たとえば、IDが同じ階層の同じID番号に設定された10個のビューがある場合、を呼び出すとfindViewById([repeated_id])
、その1つの繰り返しIDを持つ最初のビューセットが返されるということです。そういう意味です。
APIレベル17以上から、次を呼び出すことができます:View.generateViewId()
次に、View.setId(int)を使用します。
アプリのターゲットがAPIレベル17より低い場合は、ViewCompat.generateViewId()を使用します
AtomicInteger
メソッドの実装を見てみましょう。
for(;;)
これまで見たことがないものは何ですか。なんていうの?
後でR.id
xmlリソースファイルを使用してクラスで使用するIDを設定し、コンパイル時にAndroid SDKに一意の値を提供させることができます。
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);
"int currentId = 1000; whateverView.setId(currentId++);
- 使用するたびにIDをインクリメントしcurrentId++
、一意のIDを確保して、後でアクセスできるように、ArrayListのID。
<resources>
ます。
また、あなたは定義することができますids.xml
の中でres/values
。正確な例は、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++;
}
findViewById()
遅い操作です。このアプローチは機能しますが、パフォーマンスは犠牲になります。
(これはdilettanteの回答へのコメントでしたが、長くなりすぎました... hehe)
もちろん、ここではstaticは必要ありません。静的ではなく、SharedPreferencesを使用して保存できます。いずれにせよ、理由は現在の進行状況を保存して、複雑なレイアウトで遅くなりすぎないようにするためです。実際には、一度使用した後は、後でかなり高速になるからです。ただし、画面を再構築する必要がある場合(たとえばonCreate
、再度呼び出される場合)は、とにかく最初からやり直して静的の必要性を排除する必要があるため、これは良い方法ではないと思います。したがって、静的ではなくインスタンス変数にするだけです。
以下は、実行速度が少し速く、読みやすい小さなバージョンです。
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
この上記の機能で十分です。私の知る限りでは、Androidで生成されたIDは数十億に及ぶため、これはおそらく1
最初に返され、常に非常に高速です。なぜなら、実際には、使用されたIDをループして未使用のIDを見つけることはないからです。ただし、ループは実際に使用済みのIDを見つけた場合に発生します。
ただし、その後のアプリの再作成の間に進行状況を保存したい場合で、静的な使用を避けたい場合。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;
}
同様の質問に対するこの回答は、AndroidでのIDについて知っておく必要があるすべてを教えてくれるはずです。https://stackoverflow.com/a/13241629/693927
編集/修正:私は完全に保存を間違えたことに気づきました。酔っていたに違いない。
'Compat'ライブラリは、generateViewId()
17より前のAPIレベルのメソッドもサポートするようになりました。
ただあるCompat
ライブラリのバージョンを使用することを確認してください27.1.0+
たとえば、build.gradle
ファイルに次のように記述します。
implementation 'com.android.support:appcompat-v7:27.1.1
その後、次のようにgenerateViewId()
、ViewCompat
クラスの代わりにクラスのを使用するだけですView
。
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
ハッピーコーディング!
@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 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();
}
}
}
for (;;) { … }
はAndroidソースコードから取得されます。
generateViewId()
else { return View.generateViewId(); }
これは、17デバイスよりも小さいAPIレベルの無限ループになりますか?
ビューIDフォームAPI 17を動的に生成するには
での使用に適した値が生成されsetId(int)
ます。この値は、aapt forによってビルド時に生成された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 synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
乱数を使用することで、最初の試行で一意のIDを取得できる可能性が常に高くなります。
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);
}
findViewById
、同じIDを持つビューが複数ある場合にどのビューが返されるかについて、何か保証はありますか?ドキュメントは何も言及していません。