Câu trả lời:
Theo View
tài liệu
Mã định danh không phải là duy nhất trong hệ thống phân cấp của chế độ xem này. Mã định danh phải là một số dương.
Vì vậy, bạn có thể sử dụng bất kỳ số nguyên dương nào bạn thích, nhưng trong trường hợp này có thể có một số lượt xem với id tương đương. Nếu bạn muốn tìm kiếm một số chế độ xem theo thứ bậc, việc gọi đến setTag
với một số đối tượng chính có thể hữu ích.
findViewById
sẽ trả về lần đầu tiên nó tìm thấy.
setContentView()
có giả sử, 10 lượt xem với id của chúng được đặt thành cùng một số id trong cùng một cấu trúc phân cấp , thì một cuộc gọi findViewById([repeated_id])
sẽ trả về chế độ xem đầu tiên được đặt với một id lặp lại đó. Ý tôi là thế
Từ API cấp 17 trở lên, bạn có thể gọi: View.generateViewId ()
Sau đó sử dụng View.setId (int) .
Nếu ứng dụng của bạn được nhắm mục tiêu thấp hơn API cấp 17, hãy sử dụng ViewCompat.generateViewId ()
AtomicInteger
phương pháp thực hiện.
for(;;)
tôi chưa bao giờ nhìn thấy trước đây. Cái đó gọi là gì?
Bạn có thể đặt ID bạn sẽ sử dụng sau này trong R.id
lớp bằng tệp tài nguyên xml và để SDK Android cung cấp cho họ các giá trị duy nhất trong thời gian biên dịch.
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"/>
Để sử dụng nó trong mã:
myEditTextView.setId(R.id.my_edit_text_1);
"int currentId = 1000; whateverView.setId(currentId++);
- Điều đó làm tăng ID mỗi lần currentId++
được sử dụng, đảm bảo ID duy nhất và tôi có thể lưu trữ ID trong ArrayList của tôi để truy cập sau.
<resources>
.
Ngoài ra, bạn có thể xác định ids.xml
trong res/values
. Bạn có thể thấy một ví dụ chính xác trong mã mẫu của Android.
samples/ApiDemos/src/com/example/android/apis/RadioGroup1.java
samples/ApiDemp/res/values/ids.xml
Kể từ API 17, View
lớp có một phương thức tĩnh generateViewId()
sẽ
tạo ra một giá trị phù hợp để sử dụng trong setId (int)
Điều này làm việc cho tôi:
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()
là một hoạt động chậm. Cách tiếp cận hoạt động, nhưng với chi phí hiệu suất.
(Đây là một nhận xét cho câu trả lời của Dilettante nhưng nó đã quá lâu ... hehe)
Tất nhiên một động tĩnh là không cần thiết ở đây. Bạn có thể sử dụng SharedPreferences để lưu, thay vì tĩnh. Dù bằng cách nào, lý do là để lưu tiến trình hiện tại để nó không quá chậm đối với các bố cục phức tạp. Bởi vì, trên thực tế, sau khi được sử dụng một lần, nó sẽ khá nhanh về sau. Tuy nhiên, tôi không cảm thấy đây là một cách tốt để làm điều đó bởi vì nếu bạn phải xây dựng lại màn hình của mình (nói onCreate
sẽ được gọi lại), thì có lẽ bạn muốn bắt đầu lại từ đầu bằng mọi cách, loại bỏ nhu cầu tĩnh. Do đó, chỉ cần làm cho nó một biến thể hiện thay vì tĩnh.
Đây là một phiên bản nhỏ hơn chạy nhanh hơn một chút và có thể dễ đọc hơn:
int fID = 0;
public int findUnusedId() {
while( findViewById(++fID) != null );
return fID;
}
Chức năng trên phải là đủ. Bởi vì, theo như tôi có thể nói, ID do Android tạo ra có hàng tỷ, vì vậy điều này có thể sẽ trở lại 1
lần đầu tiên và luôn luôn khá nhanh. Bởi vì, nó thực sự sẽ không lặp qua các ID đã sử dụng để tìm một ID không được sử dụng. Tuy nhiên, các vòng lặp là có nên nó thực sự tìm thấy một ID được sử dụng.
Tuy nhiên, nếu bạn vẫn muốn tiến trình được lưu giữa các lần tạo lại ứng dụng tiếp theo và muốn tránh sử dụng tĩnh. Đây là phiên bản 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;
}
Câu trả lời này cho một câu hỏi tương tự sẽ cho bạn biết mọi thứ bạn cần biết về ID với android: https://stackoverflow.com/a/13241629/693927
EDIT / FIX: Chỉ cần nhận ra rằng tôi hoàn toàn ủng hộ việc tiết kiệm. Tôi phải say.
Thư viện 'Compat' hiện cũng hỗ trợ generateViewId()
phương thức cho các cấp API trước 17.
Chỉ cần đảm bảo sử dụng một phiên bản của Compat
thư viện27.1.0+
Ví dụ: trong build.gradle
tệp của bạn , đặt:
implementation 'com.android.support:appcompat-v7:27.1.1
Sau đó, bạn có thể chỉ cần sử dụng generateViewId()
từ ViewCompat
lớp thay vì View
lớp như sau:
//Will assign a unique ID
myView.id = ViewCompat.generateViewId()
Chúc mừng mã hóa!
Chỉ là một bổ sung cho câu trả lời của @ph Phantomlimb,
trong khi View.generateViewId()
yêu cầu Cấp API> = 17,
công cụ này tương thích với tất cả API.
theo Cấp độ API hiện tại,
nó quyết định thời tiết có sử dụng API hệ thống hay không.
vì vậy bạn có thể sử dụng ViewIdGenerator.generateViewId()
và View.generateViewId()
trong cùng một thời điểm và không lo lắng về việc cùng 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 (;;) { … }
đến từ Mã nguồn Android.
generateViewId()
else { return View.generateViewId(); }
điều này sẽ đi đến vòng lặp vô hạn cho cấp độ api nhỏ hơn 17 thiết bị?
Để tự động tạo API dạng xem Id 17, hãy sử dụng
Mà sẽ tạo ra một giá trị phù hợp để sử dụng trong setId(int)
. Giá trị này sẽ không va chạm với các giá trị ID được tạo tại thời điểm xây dựng bởi aapt for 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();
}
}
}
Tôi sử dụng:
public synchronized int generateViewId() {
Random rand = new Random();
int id;
while (findViewById(id = rand.nextInt(Integer.MAX_VALUE) + 1) != null);
return id;
}
Bằng cách sử dụng một số ngẫu nhiên, tôi luôn có cơ hội lớn để có được id duy nhất trong lần thử đầu tiên.
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
đảm bảo nào sau đó xem chế độ xem nào được trả về nếu có nhiều hơn một ID có cùng ID không? Các tài liệu không đề cập bất cứ điều gì.