web-dev-qa-db-ja.com

アンドロイド:プログラムによるView.setID(int id) - IDの競合を回避する方法?

私はforループ内でプログラム的にTextViewを追加し、それらをArrayListに追加します。

TextView.setId(int id)はどうやって使うのですか?他のIDと競合しないように、どのような整数IDを設定しますか。

293
znq

Viewのドキュメントによると

識別子は、このビューの階層内で一意である必要はありません。識別子は正数でなければなりません。

したがって、好きな正の整数を使うことができますが、この場合、同等のIDを持つビューがいくつかあります。階層内のビューを検索したい場合は、いくつかのキーオブジェクトを指定してsetTagを呼び出すと便利です。

134
Nikolay Ivanov

APIレベル17以上からは、次のように呼び出すことができます。 View.generateViewId()

それから View.setId(int) を使ってください。

アプリのターゲットがAPIレベル17より低い場合は、 ViewCompat.generateViewId() を使用してください。

504
xy uber.com

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);
121
Aditya_Android

ids.xmlres/valuesを定義することもできます。あなたはAndroidのサンプルコードで正確な例を見ることができます。

samples/ApiDemos/src/com/example/Android/apis/RadioGroup1.Java
samples/ApiDemp/res/values/ids.xml
59
yenliangl

API 17以降、Viewクラスには 静的メソッドgenerateViewId()があります。

setId(int)での使用に適した値を生成します

26
Diederik

これは私のために働く:

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++;  
}
25
dilettante

(これは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

編集/修正:セーブが完全に間抜けだったことに気づいた。私は酔っていたに違いない。

10
Pimp Trizkit

'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()

ハッピーコーディング!

8
Alex Roussiere

@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();
        }

    }
}
6
fantouch

ビューIDフォームAPI 17を動的に生成するには

generateViewId()

これはsetId(int)での使用に適した値を生成します。この値は、ビルド時にR.idのaaptによって生成されたID値と衝突しません。

3
Arun C
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();
        }
    }
}
2
Dmitry

私が使う:

public synchronized int generateViewId() {
    Random Rand = new Random();
    int id;
    while (findViewById(id = Rand.nextInt(Integer.MAX_VALUE) + 1) != null);
    return id;
}

乱数を使用することによって、私は最初の試みで一意のIDを取得する大きなチャンスを常に持っています。

1
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);
}
0
chinwo