私はこれを解決するためのエレガントではない方法をいくつか考えましたが、私は何かを見逃しているに違いありません。
私のonItemSelected
は、ユーザーとの対話なしにすぐに起動します。これは望ましくない動作です。私はそれが何かをする前にユーザーが何かを選択するまでUIが待つことを望みます。
私はリスナーをonResume()
に設定しようとさえしました。それが助けになることを願っていましたが、そうではありませんでした。
ユーザーがコントロールに触れることができるようになる前にこれが発火するのを止めるにはどうすればいいですか?
public class CMSHome extends Activity {
private Spinner spinner;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// Heres my spinner ///////////////////////////////////////////
spinner = (Spinner) findViewById(R.id.spinner);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.pm_list, Android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
};
public void onResume() {
super.onResume();
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
}
public class MyOnItemSelectedListener implements OnItemSelectedListener {
public void onItemSelected(AdapterView<?> parent,
View view, int pos, long id) {
Intent i = new Intent(CMSHome.this, ListProjects.class);
i.putExtra("bEmpID", parent.getItemAtPosition(pos).toString());
startActivity(i);
Toast.makeText(parent.getContext(), "The pm is " +
parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
}
public void onNothingSelected(AdapterView parent) {
// Do nothing.
}
}
}
私はあなたの解決策がうまくいくことを期待していたでしょう - リスナーを設定する前にアダプタを設定してもselectionイベントは発生しないでしょう。
そうは言っても、単純なブールフラグを使用すると、不正な最初の選択イベントを検出して無視できます。
Runnablesの使用はまったく間違っています。
最初の選択でsetSelection(position, false);
の前にsetOnItemSelectedListener(listener)
を使う
このようにして、アニメーションなしで選択範囲を設定します。これにより、項目選択リスナが呼び出されます。しかしリスナーはnullなので、何も実行されません。それからあなたのリスナーは割り当てられる。
だから、この正確なシーケンスに従ってください:
Spinner s = (Spinner)Util.findViewById(view, R.id.sound, R.id.spinner);
s.setAdapter(adapter);
s.setSelection(position, false);
s.setOnItemSelectedListener(listener);
Dan Dyerの答えを参考にして、OnSelectListener
をpost(Runnable)
メソッドに登録してみてください。
spinner.post(new Runnable() {
public void run() {
spinner.setOnItemSelectedListener(listener);
}
});
そうすることで、私の望む行動がついに起こりました。
この場合、リスナーは変更されたアイテムに対してのみ起動することも意味します。
ユーザーに通知せずにSpinner
の選択を変更するための小さなユーティリティメソッドを作成しました。
private void setSpinnerSelectionWithoutCallingListener(final Spinner spinner, final int selection) {
final OnItemSelectedListener l = spinner.getOnItemSelectedListener();
spinner.setOnItemSelectedListener(null);
spinner.post(new Runnable() {
@Override
public void run() {
spinner.setSelection(selection);
spinner.post(new Runnable() {
@Override
public void run() {
spinner.setOnItemSelectedListener(l);
}
});
}
});
}
リスナーを無効にし、選択を変更し、その後リスナーを再度有効にします。
トリックは、呼び出しがUIスレッドとは非同期であるため、連続したハンドラーポストで行う必要があります。
残念ながら、この問題に対して最も一般的に提案されている2つの解決策、つまりコールバックの発生回数をカウントすることと、後でコールバックを設定するためにRunnableをポストすることは両方とも失敗することがあります。これらの問題を回避するためのヘルパークラスがあります。コメントブロックにさらに詳しい説明があります。
import Android.view.View;
import Android.widget.AdapterView;
import Android.widget.AdapterView.OnItemSelectedListener;
import Android.widget.Spinner;
import Android.widget.SpinnerAdapter;
/**
* Spinner Helper class that works around some common issues
* with the stock Android Spinner
*
* A Spinner will normally call it's OnItemSelectedListener
* when you use setSelection(...) in your initialization code.
* This is usually unwanted behavior, and a common work-around
* is to use spinner.post(...) with a Runnable to assign the
* OnItemSelectedListener after layout.
*
* If you do not call setSelection(...) manually, the callback
* may be called with the first item in the adapter you have
* set. The common work-around for that is to count callbacks.
*
* While these workarounds usually *seem* to work, the callback
* may still be called repeatedly for other reasons while the
* selection hasn't actually changed. This will happen for
* example, if the user has accessibility options enabled -
* which is more common than you might think as several apps
* use this for different purposes, like detecting which
* notifications are active.
*
* Ideally, your OnItemSelectedListener callback should be
* coded defensively so that no problem would occur even
* if the callback was called repeatedly with the same values
* without any user interaction, so no workarounds are needed.
*
* This class does that for you. It keeps track of the values
* you have set with the setSelection(...) methods, and
* proxies the OnItemSelectedListener callback so your callback
* only gets called if the selected item's position differs
* from the one you have set by code, or the first item if you
* did not set it.
*
* This also means that if the user actually clicks the item
* that was previously selected by code (or the first item
* if you didn't set a selection by code), the callback will
* not fire.
*
* To implement, replace current occurrences of:
*
* Spinner spinner =
* (Spinner)findViewById(R.id.xxx);
*
* with:
*
* SpinnerHelper spinner =
* new SpinnerHelper(findViewById(R.id.xxx))
*
* SpinnerHelper proxies the (my) most used calls to Spinner
* but not all of them. Should a method not be available, use:
*
* spinner.getSpinner().someMethod(...)
*
* Or just add the proxy method yourself :)
*
* (Quickly) Tested on devices from 2.3.6 through 4.2.2
*
* @author Jorrit "Chainfire" Jongma
* @license WTFPL (do whatever you want with this, nobody cares)
*/
public class SpinnerHelper implements OnItemSelectedListener {
private final Spinner spinner;
private int lastPosition = -1;
private OnItemSelectedListener proxiedItemSelectedListener = null;
public SpinnerHelper(Object spinner) {
this.spinner = (spinner != null) ? (Spinner)spinner : null;
}
public Spinner getSpinner() {
return spinner;
}
public void setSelection(int position) {
lastPosition = Math.max(-1, position);
spinner.setSelection(position);
}
public void setSelection(int position, boolean animate) {
lastPosition = Math.max(-1, position);
spinner.setSelection(position, animate);
}
public void setOnItemSelectedListener(OnItemSelectedListener listener) {
proxiedItemSelectedListener = listener;
spinner.setOnItemSelectedListener(listener == null ? null : this);
}
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (position != lastPosition) {
lastPosition = position;
if (proxiedItemSelectedListener != null) {
proxiedItemSelectedListener.onItemSelected(
parent, view, position, id
);
}
}
}
public void onNothingSelected(AdapterView<?> parent) {
if (-1 != lastPosition) {
lastPosition = -1;
if (proxiedItemSelectedListener != null) {
proxiedItemSelectedListener.onNothingSelected(
parent
);
}
}
}
public void setAdapter(SpinnerAdapter adapter) {
if (adapter.getCount() > 0) {
lastPosition = 0;
}
spinner.setAdapter(adapter);
}
public SpinnerAdapter getAdapter() { return spinner.getAdapter(); }
public int getCount() { return spinner.getCount(); }
public Object getItemAtPosition(int position) { return spinner.getItemAtPosition(position); }
public long getItemIdAtPosition(int position) { return spinner.getItemIdAtPosition(position); }
public Object getSelectedItem() { return spinner.getSelectedItem(); }
public long getSelectedItemId() { return spinner.getSelectedItemId(); }
public int getSelectedItemPosition() { return spinner.getSelectedItemPosition(); }
public void setEnabled(boolean enabled) { spinner.setEnabled(enabled); }
public boolean isEnabled() { return spinner.isEnabled(); }
}
私はしたくないときのスピナーの発射に関して多くの問題を抱えています、そしてここでのすべての答えは信頼できません。彼らは働きます - しかし時々だけ。あなたは結局それらが失敗してあなたのコードにバグを導入するシナリオに遭遇するでしょう。
私にとって効果的だったのは、最後に選択したインデックスを変数に格納し、それをリスナーで評価することでした。それが新しく選択されたインデックスと同じなら何もせずに戻って、そうでなければリスナーを続けます。これを行う:
//Declare a int member variable and initialize to 0 (at the top of your class)
private int mLastSpinnerPosition = 0;
//then evaluate it in your listener
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if(mLastSpinnerPosition == i){
return; //do nothing
}
mLastSpinnerPosition = i;
//do the rest of your code now
}
私がこれを言うとき私を信頼しなさい、これははるかに最も信頼できる解決策である。ハックですが、うまくいきます。
私は似たような状況にあり、私には簡単な解決策があります。
メソッドsetSelection(int position)
とsetSelected(int position, boolean animate)
は異なる内部実装を持っているようです。
2番目のメソッドsetSelected(int position, boolean animate)
をfalse animateフラグ付きで使用すると、onItemSelected
リスナーを起動せずに選択を取得します。
OnTouchListenerを使用してsetOnItemSelectedListenerへの自動呼び出し(Activityの初期化などに含まれる)と実際のユーザー操作によってトリガーされる呼び出しを区別するためのヒントを具体化するために、ここで他の提案を試してから次のことを行いました最も少ないコード行でうまく動作することがわかりました。
Activity/FragmentのBooleanフィールドを以下のように設定するだけです。
private Boolean spinnerTouched = false;
次に、スピナーのsetOnItemSelectedListenerを設定する直前に、onTouchListenerを設定します。
spinner.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("Real touch felt.");
spinnerTouched = true;
return false;
}
});
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
...
if (spinnerTouched){
//Do the stuff you only want triggered by real user interaction.
}
spinnerTouched = false;
spinner.setSelection(Adapter.NO_SELECTION, false);
長い間髪の毛を引き抜いてきた今、私は私自身のスピナークラスを作りました。リスナーを適切に切断して接続するメソッドを追加しました。
public class SaneSpinner extends Spinner {
public SaneSpinner(Context context) {
super(context);
}
public SaneSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SaneSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
// set the ceaseFireOnItemClickEvent argument to true to avoid firing an event
public void setSelection(int position, boolean animate, boolean ceaseFireOnItemClickEvent) {
OnItemSelectedListener l = getOnItemSelectedListener();
if (ceaseFireOnItemClickEvent) {
setOnItemSelectedListener(null);
}
super.setSelection(position, animate);
if (ceaseFireOnItemClickEvent) {
setOnItemSelectedListener(l);
}
}
}
このようにあなたのXMLでそれを使用します。
<my.package.name.SaneSpinner
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:id="@+id/mySaneSpinner"
Android:entries="@array/supportedCurrenciesFullName"
Android:layout_weight="2" />
あなたがしなければならないのは、インフレーションの後にSaneSpinnerのインスタンスを検索して、次のように集合選択を呼び出すことです:
mMySaneSpinner.setSelection(1, true, true);
これにより、イベントは発生せず、ユーザーの操作は中断されません。これにより、コードの複雑さが大幅に軽減されました。それは実際にはピタですので、これは株式のAndroidに含まれるべきです。
レイアウトが完成するまでリスナーの追加を延期しても、レイアウト段階で不要なイベントは発生しません。
spinner.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
// Ensure you call it only once works for JELLY_BEAN and later
spinner.getViewTreeObserver().removeOnGlobalLayoutListener(this);
// add the listener
spinner.setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
// check if pos has changed
// then do your work
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
}
});
私は非常に単純な答えを得ました。100%確実に機能します。
boolean Touched=false; // this a a global variable
public void changetouchvalue()
{
Touched=true;
}
// this code is written just before onItemSelectedListener
spinner.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
System.out.println("Real touch felt.");
changetouchvalue();
return false;
}
});
//inside your spinner.SetonItemSelectedListener , you have a function named OnItemSelected iside that function write the following code
if(Touched)
{
// the code u want to do in touch event
}
あなたがコードで選択をしているならば、これは起こるでしょう。
mSpinner.setSelection(0);
上記の代わりに
mSpinner.setSelection(0,false);//just simply do not animate it.
編集:このメソッドはMi AndroidのバージョンMi UIでは機能しません。
私はこれに対してもっとエレガントな解決策を見つけました。それはArrayAdapter(あなたの場合は "adapter")が何回呼び出されたかを数えることを含みます。スピナーが1人いるとしましょう。
int iCountAdapterCalls = 0;
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
this, R.array.pm_list, Android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(Android.R.layout.simple_spinner_dropdown_item);
spinner.setAdapter(adapter);
OnCreateの後にintカウンタを宣言し、次にonItemSelected()メソッドの中でatapterが何回呼び出されたかを確認するための "if"条件を設定します。あなたのケースでは、あなたはそれが一度だけ呼ばれています:
if(iCountAdapterCalls < 1)
{
iCountAdapterCalls++;
//This section executes in onCreate, during the initialization
}
else
{
//This section corresponds to user clicks, after the initialization
}
私の小さな貢献は、私に数回適している上記のいくつかのバリエーションです。
整数変数をデフォルト値(または設定に保存されている最後の使用値)として宣言します。リスナーが登録される前に、spinner.setSelection(myDefault)を使用してその値を設定してください。 onItemSelectedで、新しいスピナー値が他のコードを実行する前に割り当てた値と等しいかどうかを確認します。
これには、ユーザーが同じ値をもう一度選択した場合にコードが実行されないという追加の利点があります。
同じ問題を抱えた後、私はタグを使ってこの解決策を見つけました。その背後にある考え方は簡単です。スピナーがプログラム的に変更されるときはいつでも、タグが選択された位置を反映することを確認してください。リスナーでは、選択した位置がタグと等しいかどうかを確認します。もしそうなら、スピナーの選択はプログラム的に変更されました。
以下は私の新しい "スピナープロキシ"クラスです:
package com.samplepackage;
import com.samplepackage.R;
import Android.widget.Spinner;
public class SpinnerFixed {
private Spinner mSpinner;
public SpinnerFixed(View spinner) {
mSpinner = (Spinner)spinner;
mSpinner.setTag(R.id.spinner_pos, -2);
}
public boolean isUiTriggered() {
int tag = ((Integer)mSpinner.getTag(R.id.spinner_pos)).intValue();
int pos = mSpinner.getSelectedItemPosition();
mSpinner.setTag(R.id.spinner_pos, pos);
return (tag != -2 && tag != pos);
}
public void setSelection(int position) {
mSpinner.setTag(R.id.spinner_pos, position);
mSpinner.setSelection(position);
}
public void setSelection(int position, boolean animate) {
mSpinner.setTag(R.id.spinner_pos, position);
mSpinner.setSelection(position, animate);
}
// If you need to proxy more methods, use "Generate Delegate Methods"
// from the context menu in Eclipse.
}
Values
ディレクトリにタグ設定のあるXMLファイルも必要です。私のファイルをspinner_tag.xml
と命名しましたが、それはあなた次第です。それはこのように見えます:
<resources xmlns:Android="http://schemas.Android.com/apk/res/Android">
<item name="spinner_pos" type="id" />
</resources>
今すぐ交換
Spinner myspinner;
...
myspinner = (Spinner)findViewById(R.id.myspinner);
あなたのコードで
SpinnerFixed myspinner;
...
myspinner = new SpinnerFixed(findViewById(R.id.myspinner));
そして、あなたのハンドラをこのように見せてください。
myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (myspinner.isUiTriggered()) {
// Code you want to execute only on UI selects of the spinner
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
関数isUiTriggered()
は、スピナーがユーザーによって変更された場合に限りtrueを返します。この関数には副作用があることに注意してください。タグが設定されるため、同じリスナー呼び出しの2回目の呼び出しでは常にfalse
が返されます。
このラッパーは、レイアウト作成中にリスナーが呼び出される問題も処理します。
楽しんでください、Jens。
何も私にはうまくいきませんでした、そして私は私の見解に2人以上のスピナーがいます(そして私見がブールマップを持っていることはやり過ぎです)私はクリックを数えるのにタグを使います:
spinner.setTag(0);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Integer selections = (Integer) parent.getTag();
if (selections > 0) {
// real selection
}
parent.setTag(++selections); // (or even just '1')
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
すでにたくさんの答えがあります、私のものです。
私はAppCompatSpinner
を拡張し、選択コールバックを引き起こさずにプログラムによる選択設定を可能にするメソッドpgmSetSelection(int pos)
を追加します。私はこれをRxJavaでコーディングしたので、選択イベントはObservable
を介して配信されます。
package com.controlj.view;
import Android.content.Context;
import Android.util.AttributeSet;
import Android.view.View;
import Android.widget.AdapterView;
import io.reactivex.Observable;
/**
* Created by clyde on 22/11/17.
*/
public class FilteredSpinner extends Android.support.v7.widget.AppCompatSpinner {
private int lastSelection = INVALID_POSITION;
public void pgmSetSelection(int i) {
lastSelection = i;
setSelection(i);
}
/**
* Observe item selections within this spinner. Events will not be delivered if they were triggered
* by a call to setSelection(). Selection of nothing will return an event equal to INVALID_POSITION
*
* @return an Observable delivering selection events
*/
public Observable<Integer> observeSelections() {
return Observable.create(emitter -> {
setOnItemSelectedListener(new OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if(i != lastSelection) {
lastSelection = i;
emitter.onNext(i);
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
onItemSelected(adapterView, null, INVALID_POSITION, 0);
}
});
});
}
public FilteredSpinner(Context context) {
super(context);
}
public FilteredSpinner(Context context, int mode) {
super(context, mode);
}
public FilteredSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public FilteredSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
super(context, attrs, defStyleAttr, mode);
}
}
その使用例。例えばFragment
のonCreateView()
で呼ばれます。
mySpinner = view.findViewById(R.id.history);
mySpinner.observeSelections()
.subscribe(this::setSelection);
ここで、setSelection()
は、囲みビュー内のこのように見えるメソッドで、ユーザー選択イベントからObservable
を介して、およびプログラム的に呼び出されるので、選択を処理するためのロジックは両方の選択メソッドに共通です。
private void setSelection(int position) {
if(adapter.isEmpty())
position = INVALID_POSITION;
else if(position >= adapter.getCount())
position = adapter.getCount() - 1;
MyData result = null;
mySpinner.pgmSetSelection(position);
if(position != INVALID_POSITION) {
result = adapter.getItem(position);
}
display(result); // show the selected item somewhere
}
OnClickListenerオブジェクトの作成中に初期インデックスを保存します。
int thisInitialIndex = 0;//change as needed
myspinner.setSelection(thisInitialIndex);
myspinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
int initIndex = thisInitialIndex;
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (id != initIndex) { //if selectedIndex is the same as initial value
// your real onselecteditemchange event
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
私はこの投稿について遅すぎて答えるかもしれませんが、私はAndroid Data binding library Android Databinding を使ってこれを達成することに成功しました。ユーザーが同じ位置を何度も選択していてもイベントが発生しない場合でも、選択された項目が変更されるまでリスナーが呼び出されないようにするためにカスタムバインディングを作成しました。
レイアウトxmlファイル
<layout>
<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:layout_marginTop="@dimen/activity_vertical_margin"
xmlns:app="http://schemas.Android.com/apk/res-auto">
<Spinner
Android:id="@+id/spinner"
Android:layout_width="150dp"
Android:layout_height="wrap_content"
Android:spinnerMode="dropdown"
Android:layout_below="@id/member_img"
Android:layout_marginTop="@dimen/activity_vertical_margin"
Android:background="@drawable/member_btn"
Android:padding="@dimen/activity_horizontal_margin"
Android:layout_marginStart="@dimen/activity_horizontal_margin"
Android:textColor="@color/colorAccent"
app:position="@{0}"
/>
</RelativeLayout>
</layout>
app:position
はあなたが選択される位置を渡しているところです。
カスタムバインディング
@BindingAdapter(value={ "position"}, requireAll=false)
public static void setSpinnerAdapter(Spinner spinner, int selected)
{
final int [] selectedposition= new int[1];
selectedposition[0]=selected;
// custom adapter or you can set default adapter
CustomSpinnerAdapter customSpinnerAdapter = new CustomSpinnerAdapter(spinner.getContext(), <arraylist you want to add to spinner>);
spinner.setAdapter(customSpinnerAdapter);
spinner.setSelection(selected,false);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String item = parent.getItemAtPosition(position).toString();
if( position!=selectedposition[0]) {
selectedposition[0]=position;
// do your stuff here
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
あなたはここでカスタムデータバインディングについてもっと読むことができます Android Custom Setter
NOTE
Gradleファイルでデータバインディングを有効にすることを忘れないでください
Android {
....
dataBinding {
enabled = true
}
}
レイアウトファイルを<layout>
タグに含める
ViewHolderでmSpinner
を使用する必要があるので、mOldPosition
フラグは無名の内部クラスに設定されます。
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
int mOldPosition = mSpinner.getSelectedItemPosition();
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long l) {
if (mOldPosition != position) {
mOldPosition = position;
//Do something
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
//Do something
}
});
電話しよう
spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
setAdapter()を呼び出した後。また、アダプタの前に呼び出してみてください。
サブクラス化には常に解決策があります。この場合、オーバーライドされたsetAdapterメソッドにブールフラグをラップしてイベントをスキップすることができます。
if () {
spinner.setSelection(0);// No reaction to create spinner !!!
} else {
spinner.setSelection(intPosition);
}
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
if (position > 0) {
// real selection
}
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
ブール値のフラグまたはカウンタを使用した解決策は役に立ちませんでした。
私はAndroid.widget.Spinner
をサブクラス化して少し追加しました。関連部分は以下の通りです。この解決策は私のために働いた。
private void setHandleOnItemSelected()
{
final StackTraceElement [] elements = Thread.currentThread().getStackTrace();
for (int index = 1; index < elements.length; index++)
{
handleOnItemSelected = elements[index].toString().indexOf("PerformClick") != -1; //$NON-NLS-1$
if (handleOnItemSelected)
{
break;
}
}
}
@Override
public void setSelection(int position, boolean animate)
{
super.setSelection(position, animate);
setHandleOnItemSelected();
}
@Override
public void setSelection(int position)
{
super.setSelection(position);
setHandleOnItemSelected();
}
public boolean shouldHandleOnItemSelected()
{
return handleOnItemSelected;
}
これも優れた解決策ではありません。実際、それはむしろRube-Goldbergですが、うまくいくようです。私はスピナーがアレイアダプタを拡張してそのgetDropDownViewをオーバーライドすることによって少なくとも一度使われたことを確かめます。新しいgetDropDownViewメソッドでは、ドロップダウンメニューを表示するように設定されたブール値フラグが少なくとも一度は使用されています。フラグが設定されるまでリスナーへの呼び出しは無視します。
MainActivity.onCreate():
ActionBar ab = getActionBar();
ab.setDisplayShowTitleEnabled(false);
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
ab.setListNavigationCallbacks(null, null);
ArrayList<String> abList = new ArrayList<String>();
abList.add("line 1");
...
ArAd abAdapt = new ArAd (this
, Android.R.layout.simple_list_item_1
, Android.R.id.text1, abList);
ab.setListNavigationCallbacks(abAdapt, MainActivity.this);
オーバーライドされたアレイアダプタ
private static boolean viewed = false;
private class ArAd extends ArrayAdapter<String> {
private ArAd(Activity a
, int layoutId, int resId, ArrayList<String> list) {
super(a, layoutId, resId, list);
viewed = false;
}
@Override
public View getDropDownView(int position, View convertView,
ViewGroup parent) {
viewed = true;
return super.getDropDownView(position, convertView, parent);
}
}
修正されたリスナー
@Override
public boolean onNavigationItemSelected(
int itemPosition, long itemId) {
if (viewed) {
...
}
return false;
}
あなたがその場で活動を作り直す必要があるならば。
onUserInteraction()関数を使用してユーザーの活動を検出します。
これが私の最終的な使いやすい解決策です。
public class ManualSelectedSpinner extends Spinner {
//get a reference for the internal listener
private OnItemSelectedListener mListener;
public ManualSelectedSpinner(Context context) {
super(context);
}
public ManualSelectedSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ManualSelectedSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void setOnItemSelectedListener(@Nullable OnItemSelectedListener listener) {
mListener = listener;
super.setOnItemSelectedListener(listener);
}
public void setSelectionWithoutInformListener(int position){
super.setOnItemSelectedListener(null);
super.setSelection(position);
super.setOnItemSelectedListener(mListener);
}
public void setSelectionWithoutInformListener(int position, boolean animate){
super.setOnItemSelectedListener(null);
super.setSelection(position, animate);
super.setOnItemSelectedListener(mListener);
}
}
デフォルトの動作にはデフォルトのsetSelection(...)
を使用するか、OnItemSelectedListenerコールバックをトリガーせずにスピナー内の項目を選択するにはsetSelectionWithoutInformListener(...)
を使用します。
私のソリューションはonTouchListener
を使用していますが、その使用を制限するものではありません。必要に応じて、onTouchListener
を設定する場所でonItemSelectedListener
のラッパーを作成します。
public class Spinner extends Android.widget.Spinner {
/* ...constructors... */
private OnTouchListener onTouchListener;
private OnItemSelectedListener onItemSelectedListener;
@Override
public void setOnItemSelectedListener(OnItemSelectedListener listener) {
onItemSelectedListener = listener;
super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
}
@Override
public void setOnTouchListener(OnTouchListener listener) {
onTouchListener = listener;
super.setOnTouchListener(wrapTouchListener(onTouchListener, onItemSelectedListener));
}
private OnTouchListener wrapTouchListener(final OnTouchListener onTouchListener, final OnItemSelectedListener onItemSelectedListener) {
return onItemSelectedListener != null ? new OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
Spinner.super.setOnItemSelectedListener(onItemSelectedListener);
return onTouchListener != null && onTouchListener.onTouch(view, motionEvent);
}
} : onTouchListener;
}
}
私は完了が最も簡単な方法であります。
private AdapterView.OnItemSelectedListener listener;
private Spinner spinner;
onCreate();
spinner = (Spinner) findViewById(R.id.spinner);
listener = new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int position, long l) {
Log.i("H - Spinner selected position", position);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
};
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
spinner.setOnItemSelectedListener(listener);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
完了