私はforループ内でプログラム的にTextViewを追加し、それらをArrayListに追加します。
TextView.setId(int id)
はどうやって使うのですか?他のIDと競合しないように、どのような整数IDを設定しますか。
View
のドキュメントによると
識別子は、このビューの階層内で一意である必要はありません。識別子は正数でなければなりません。
したがって、好きな正の整数を使うことができますが、この場合、同等のIDを持つビューがいくつかあります。階層内のビューを検索したい場合は、いくつかのキーオブジェクトを指定してsetTag
を呼び出すと便利です。
APIレベル17以上からは、次のように呼び出すことができます。 View.generateViewId()
それから View.setId(int) を使ってください。
アプリのターゲットがAPIレベル17より低い場合は、 ViewCompat.generateViewId() を使用してください。
Xmlリソースファイルを使用して、後で使用するIDをR.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);
ids.xml
にres/values
を定義することもできます。あなたはAndroidのサンプルコードで正確な例を見ることができます。
samples/ApiDemos/src/com/example/Android/apis/RadioGroup1.Java
samples/ApiDemp/res/values/ids.xml
API 17以降、View
クラスには 静的メソッドgenerateViewId()
があります。
setId(int)での使用に適した値を生成します
これは私のために働く:
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++;
}
(これはdilettanteの答えに対するコメントでしたが、長すぎました... hehe)
もちろんここではstaticは必要ありません。静的ではなく、SharedPreferencesを使用して保存することができます。どちらにしても、その理由は現在の進捗状況を保存して複雑なレイアウトには遅すぎないようにするためです。それは、実際には、一度使用された後は、かなり遅くなるからです。ただし、これを実行するのが良い方法ではないと思います(画面を再構築する必要がある場合(onCreate
が再度呼び出されるなど)、おそらく最初からやり直すことをお勧めします)。したがって、静的変数ではなくインスタンス変数にしてください。
これはもう少し速く動くより読みやすいかもしれない小さいバージョンです:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
上記の機能で十分です。私が言うことができる限りでは、Androidが生成したIDは数十億にあるので、これはおそらく最初に1
を返し、常に非常に速いでしょう。なぜなら、未使用のIDを見つけるために実際には使用済みIDをループしていないからです。しかし、ループはです。実際に使用済みのIDが見つかるはずです。
ただし、その後のアプリの再作成の間に進行状況を保存したい場合は、staticの使用を避けたい場合があります。これが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'ライブラリは、17以前のAPIレベルでもgenerateViewId()
メソッドをサポートするようになりました。
27.1.0+
であるCompat
ライブラリのバージョンを必ず使用するようにしてください。
たとえば、build.gradle
ファイルに次のように入力します。
implementation 'com.Android.support:appcompat-v7:27.1.1
次のように、ViewCompat
クラスの代わりにView
クラスのgenerateViewId()
を使用するだけです。
//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 [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();
}
}
}
ビューIDフォームAPI 17を動的に生成するには
これはsetId(int)
での使用に適した値を生成します。この値は、ビルド時にR.id
のaaptによって生成された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);
}