はじめに
最近、案件でAndroid
のSpinner
クラスについてゴリゴリと実装をし、ある程度知見が溜まったのでメモ書きも含めてやりたいこととその実装例を画像+テキストベースで説明をしてみたいと思います。
(2019/02/25時点では開閉タイミングのみ)
TL;DR
- Spinnerの開閉タイミングは
Spinner
クラスをラップする必要がある。標準では不可能 - Spinnerは思うようにコントロールしづらいので使用する際はUIなど細かい仕様にならないように調整する必要がある
Spinner
の開閉タイミングをハンドリングする
開閉タイミングをコントロールするには標準のSpinner
では実現できません。Spinner
クラスをラップ(厳密にはAppCompatSpinner
クラス)し、以下のソースコードでオリジナルのSpinner
クラスを生成する必要があります。
使用タイミングとしては、閉じたときだけ(開いたときだけ)文字のサイズを変更したいやViewの画像部分などを非表示にしたいなどに対応できます。
ソースコード
public class CustomSpinner extends android.support.v7.widget.AppCompatSpinner {
public CustomSpinner(Context context) {
super(context);
}
public CustomSpinner(Context context, int mode) {
super(context, mode);
}
public CustomSpinner(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode) {
super(context, attrs, defStyleAttr, mode);
}
public CustomSpinner(Context context, AttributeSet attrs, int defStyleAttr, int mode, Resources.Theme popupTheme) {
super(context, attrs, defStyleAttr, mode, popupTheme);
}
/**
* An interface which a client of this Spinner could use to receive
* open/closed events for this Spinner.
*/
public interface OnSpinnerEventsListener {
/**
* Callback triggered when the spinner was opened.
*/
void onSpinnerOpened(Spinner spinner);
/**
* Callback triggered when the spinner was closed.
*/
void onSpinnerClosed(Spinner spinner);
}
private OnSpinnerEventsListener mListener;
private boolean mOpenInitiated = false;
// implement the Spinner constructors that you need
@Override
public boolean performClick() {
// register that the Spinner was opened so we have a status
// indicator for when the container holding this Spinner may lose focus
mOpenInitiated = true;
if (mListener != null) {
mListener.onSpinnerOpened(this);
}
return super.performClick();
}
@Override
public void onWindowFocusChanged (boolean hasFocus) {
if (hasBeenOpened() && hasFocus) {
performClosedEvent();
}
}
/**
* Register the listener which will listen for events.
*/
public void setSpinnerEventsListener(OnSpinnerEventsListener onSpinnerEventsListener) {
mListener = onSpinnerEventsListener;
}
/**
* Propagate the closed Spinner event to the listener from outside if needed.
*/
public void performClosedEvent() {
mOpenInitiated = false;
if (mListener != null) {
mListener.onSpinnerClosed(this);
}
}
/**
* A boolean flag indicating that the Spinner triggered an open event.
*
* @return true for opened Spinner
*/
public boolean hasBeenOpened() {
return mOpenInitiated;
}
}
使い方
CustomSpinner customSpinner = (CustomSpinner) findViewById(R.id.custom_spinner);
// adapterの設定は省略しています
customSpinner.setSpinnerEventsListener(new CustomSpinner.OnSpinnerEventsListener() {
@Override
public void onSpinnerOpened(Spinner spinner) {
Log.v("opened!!");
}
@Override
public void onSpinnerClosed(Spinner spinner) {
Log.v("closed!!");
}
});
注意点としては、AppCompatSpinner
クラスをラップしているので標準のSpinner
クラスのようなUIにはなりません。レイアウトにandroid:dropDownVerticalOffset="30dp"
を追加するか、customSpinner.setDropDownVerticalOffset(30)
を設定してドロップダウンの場所を調整する必要があります。