Jawaban:
Menurut View
dokumentasi
Pengidentifikasi tidak harus unik dalam hierarki tampilan ini. Pengidentifikasi harus berupa angka positif.
Jadi, Anda dapat menggunakan bilangan bulat positif apa pun yang Anda suka, tetapi dalam hal ini ada beberapa tampilan dengan id yang setara. Jika Anda ingin mencari beberapa tampilan dalam hierarki panggilan setTag
dengan beberapa objek utama mungkin berguna.
findViewById
akan mengembalikan yang pertama kali ditemukannya.
setContentView()
memiliki katakanlah, 10 tampilan dengan id mereka diatur ke nomor id yang sama dalam hierarki yang sama , maka panggilan untuk findViewById([repeated_id])
akan mengembalikan tampilan pertama diatur dengan satu id yang diulang. Itu yang saya maksud.
Dari API level 17 ke atas, Anda dapat menghubungi: View.generateViewId ()
Kemudian gunakan View.setId (int) .
Jika aplikasi Anda ditargetkan lebih rendah dari API level 17, gunakan ViewCompat.generateViewId ()
AtomicInteger
implementasi metode.
for(;;)
belum pernah saya lihat sebelumnya. Apa itu namanya?
Anda dapat mengatur ID yang akan Anda gunakan nanti di R.id
kelas menggunakan file sumber daya xml, dan biarkan Android SDK memberi mereka nilai unik selama waktu kompilasi.
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"/>
Untuk menggunakannya dalam kode:
myEditTextView.setId(R.id.my_edit_text_1);
"int currentId = 1000; whateverView.setId(currentId++);
- Itu menambah ID setiap kali currentId++
digunakan, memastikan ID unik, dan saya dapat menyimpan ID di ArrayList saya untuk akses nanti.
<resources>
.
Juga Anda dapat menentukan ids.xml
di res/values
. Anda dapat melihat contoh yang tepat dalam kode sampel android.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Sejak API 17, View
kelas memiliki metode statis generateViewId()
yang akan
menghasilkan nilai yang cocok untuk digunakan di setId (int)
Ini bekerja untuk saya:
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()
adalah operasi yang lambat. Pendekatannya berhasil, tetapi dengan biaya kinerja.
(Ini adalah komentar untuk jawaban dilettante tapi terlalu panjang ... hehe)
Tentu saja statis tidak diperlukan di sini. Anda bisa menggunakan SharedPreferences untuk menyimpan, bukan statis. Either way, alasannya adalah untuk menyimpan kemajuan saat ini sehingga tidak terlalu lambat untuk tata letak yang rumit. Karena, pada kenyataannya, setelah digunakan sekali, itu akan agak cepat nantinya. Namun, saya tidak merasa ini adalah cara yang baik untuk melakukannya karena jika Anda harus membangun kembali layar Anda lagi (katakanlahonCreate
dipanggil lagi), maka Anda mungkin ingin memulai dari awal lagi, menghilangkan kebutuhan untuk statis. Karena itu, buat saja variabel instan bukan statis.
Ini adalah versi yang lebih kecil yang berjalan sedikit lebih cepat dan mungkin lebih mudah dibaca:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
Fungsi di atas ini harus memadai. Karena, sejauh yang saya tahu, ID yang dihasilkan android ada dalam miliaran, jadi ini mungkin akan kembali 1
pertama kali dan selalu sangat cepat. Karena, itu sebenarnya tidak akan melewati ID yang digunakan untuk menemukan yang tidak digunakan. Namun, pengulangan itu harus ada jika benar-benar menemukan ID yang digunakan.
Namun, jika Anda tetap ingin progres disimpan di antara rekreasi ulang aplikasi Anda berikutnya, dan ingin menghindari penggunaan statis. Ini adalah versi 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;
}
Jawaban untuk pertanyaan serupa ini akan memberi tahu Anda segala yang perlu Anda ketahui tentang ID dengan android: https://stackoverflow.com/a/13241629/693927
EDIT / FIX: Baru sadar saya benar-benar melakukan kesalahan save. Saya pasti mabuk.
Pustaka 'Compat' sekarang juga mendukung generateViewId()
metode untuk level API sebelum 17.
Pastikan untuk menggunakan versi Compat
pustaka yang ada27.1.0+
Misalnya, di build.gradle
file Anda , masukkan:
implementation 'com.android.support:appcompat-v7:27.1.1
Maka Anda bisa menggunakan generateViewId()
dari ViewCompat
kelas bukan View
kelas sebagai berikut:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
Selamat coding!
Hanya tambahan untuk jawaban @ phantomlimb,
saat View.generateViewId()
membutuhkan API Level> = 17,
alat ini kompatibel dengan semua API.
menurut Level API saat ini,
ia memutuskan cuaca menggunakan sistem API atau tidak.
jadi Anda bisa menggunakan ViewIdGenerator.generateViewId()
dan View.generateViewId()
dalam waktu yang bersamaan dan tidak perlu khawatir mendapatkan id yang sama
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 (;;) { … }
berasal dari Kode Sumber Android.
generateViewId()
else { return View.generateViewId(); }
ini akan menuju loop tak terbatas untuk level api yang lebih kecil dari 17 perangkat?
Untuk menghasilkan secara dinamis bentuk Id Penggunaan API 17 digunakan
Yang akan menghasilkan nilai yang cocok untuk digunakan di setId(int)
. Nilai ini tidak akan bertabrakan dengan nilai ID yang dihasilkan saat membangun oleh aapt untuk 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();
}
}
}
Saya menggunakan:
public synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
Dengan menggunakan nomor acak saya selalu memiliki peluang besar untuk mendapatkan id unik dalam upaya pertama.
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
ada jaminan untuk tampilan yang dikembalikan jika ada lebih dari satu dengan ID yang sama? Dokumen tidak menyebutkan apa pun.