
Android Drop-down list (Spinner) with a progress bar
Greetings to you, reader!
I present to your attention a short essay on how I wanted to see the progress bar ("infinite circle") while loading data into the drop-down list, which is called Spinner in Android.
This need arose when developing a small utility for working with a web service. The parameters of a certain calculation are stored on a centralized server. The .NET web service provides lists of possible parameters in the form of arrays of different lengths (from 2 to 50 elements). To display these parameters, a drop-down list was selected. List initialization, as expected, occurs asynchronously. And while the data is loading, looking at empty static elements without any progress is boring, dull and generally.
The standard Spinner looks like this:

After a little refinement, it turns out something like this (CustomSpinner):

“What is the salt !?” - you ask? And salt is in an intermediate state (loading data):

in a light topic:

To get such an effect, I see 2 ways:
1 Inherited from Spinner; override onDraw () and, possibly, some other methods; implement state processing (loading / loading)
2 Inherited from Layout; place Spinner and ProgressBar on it; organize the work of the control in accordance with its requirements.
The first way is probably more correct. But every time I stepped on it, I ran into Exception'y and crashes of the application. Although I’ve known Android for a long time, I honestly admit that I still don’t understand what exactly needs to be done in the onDraw () method. I really do not want to poke around in the source, although sometimes it is useful. In general, this path ended, so only without starting.
The second way is described in more detail on the network. I walked through it quickly and naturally. And he was like that ...
First, we need to “sketch” our new control (custom_spinner.xml). There is nothing complicated about it - a root layout and two daughter elements (spinner and progress bar). RelativeLayout is a good fit for this. I got it like this:
To work with the control, you need to implement the CustomSpinner class. Create a class inherited from RelativeLayout:
To achieve the original goal (showing a progress bar when loading data), we modify the CustomSpinner class:
In the case when the control is in the process of loading, you need to hide the possible values of the list available in it -
Now with asynchronous loading, for which I use AsyncTask, we can control the behavior of the control:
Well, of course, and where without them!
Recall that essentially we wanted Spinner. Therefore, the control behavior should be appropriate. With the selected implementation, you need to implement several gags:
Methods
Attention should be paid to the method
Drop-down list handler:
If our custom control was inherited directly from Spinner, then these crutches might not have been. But alas.
It remains to insert our new element into the application interface (layout):
There are 25 such controls on my form. Once I kind of read that you can specify the namespace of your application in the layout header and then it seems like there will be no need to write the full class name
Thanks for attention. I would be very happy if someone gives links to sensible articles on the implementation of such control in the first way (inherited from Spinner). Or he will explain how to draw a progress bar in the onDraw () method, and then hide it if necessary.
I present to your attention a short essay on how I wanted to see the progress bar ("infinite circle") while loading data into the drop-down list, which is called Spinner in Android.
This need arose when developing a small utility for working with a web service. The parameters of a certain calculation are stored on a centralized server. The .NET web service provides lists of possible parameters in the form of arrays of different lengths (from 2 to 50 elements). To display these parameters, a drop-down list was selected. List initialization, as expected, occurs asynchronously. And while the data is loading, looking at empty static elements without any progress is boring, dull and generally.
The goal itself
The standard Spinner looks like this:

After a little refinement, it turns out something like this (CustomSpinner):

“What is the salt !?” - you ask? And salt is in an intermediate state (loading data):

in a light topic:

To get such an effect, I see 2 ways:
1 Inherited from Spinner; override onDraw () and, possibly, some other methods; implement state processing (loading / loading)
2 Inherited from Layout; place Spinner and ProgressBar on it; organize the work of the control in accordance with its requirements.
The first way is probably more correct. But every time I stepped on it, I ran into Exception'y and crashes of the application. Although I’ve known Android for a long time, I honestly admit that I still don’t understand what exactly needs to be done in the onDraw () method. I really do not want to poke around in the source, although sometimes it is useful. In general, this path ended, so only without starting.
The second way is described in more detail on the network. I walked through it quickly and naturally. And he was like that ...
XML layout
First, we need to “sketch” our new control (custom_spinner.xml). There is nothing complicated about it - a root layout and two daughter elements (spinner and progress bar). RelativeLayout is a good fit for this. I got it like this:
CustomSpinner Class
To work with the control, you need to implement the CustomSpinner class. Create a class inherited from RelativeLayout:
public class CustomSpinner extends RelativeLayout {
Context context;
Spinner spinner;
ProgressBar progress;
public CustomSpinner(Context c, AttributeSet attrs) {
super(c, attrs);
this.context = c;
//Отрисовываем внещний вид из нашего ранее определённого ресурса custom_spinner.xml
LayoutInflater.from(context).inflate(R.layout.custom_spinner, this,
true);
initViews();
}
//Инициализируем дочерние контролы для дальнейшей работы с ними
private void initViews() {
spinner = (Spinner) findViewById(R.id.spinner);
progress = (ProgressBar) findViewById(R.id.progress);
}
}
State management
To achieve the original goal (showing a progress bar when loading data), we modify the CustomSpinner class:
public class CustomSpinner extends RelativeLayout {
Context context;
Spinner spinner;
ProgressBar progress;
ArrayAdapter emptyAdapter;
public CustomSpinner(Context c, AttributeSet attrs) {
super(c, attrs);
this.context = c;
LayoutInflater.from(context).inflate(R.layout.custom_spinner, this,
true);
String[] strings = new String[] {};
List items = new ArrayList(Arrays.asList(strings));
emptyAdapter = new ArrayAdapter(c,
android.R.layout.simple_spinner_item, items);
initViews();
}
private void initViews() {
spinner = (Spinner) findViewById(R.id.spinner);
progress = (ProgressBar) findViewById(R.id.progress);
}
public void loading(boolean flag) {
if (flag)
spinner.setAdapter(emptyAdapter);
progress.setVisibility(flag ? View.VISIBLE : View.GONE);
}
}
In the case when the control is in the process of loading, you need to hide the possible values of the list available in it -
spinner.setAdapter(emptyAdapter);
. And, in fact, show the progress bar. Now with asynchronous loading, for which I use AsyncTask, we can control the behavior of the control:
CustomSpinner spinner;
...
@Override
protected void onPreExecute() {
spinner.loading(true);
}
...
@Override
protected SpinnerAdapter doInBackground(Map... params) {
//Здесь данные, полученные от веб-сервиса укладываются в SpinnerAdapter, которы впоследствии назначется нашему CustomSpinner
return null;
}
...
@Override
protected void onPostExecute(SpinnerAdapter result) {
spinner.loading(false);
if (result != null)
spinner.setAdapter(result);
}
Crutches
Well, of course, and where without them!
Recall that essentially we wanted Spinner. Therefore, the control behavior should be appropriate. With the selected implementation, you need to implement several gags:
public class CustomSpinner extends RelativeLayout {
...
//Обработчик события выбора значения выпадающего списка
public void setOnItemSelectedListener(OnItemSelectedListener l) {
spinner.setTag(getId());
spinner.setOnItemSelectedListener(l);
}
//Назначаем адаптер выпадающему списку
public void setAdapter(SpinnerAdapter adapter) {
spinner.setAdapter(adapter);
}
// Возвращаем индекс выбранного в списке элемента
public int getSelectedItemPosition() {
return this.spinner.getSelectedItemPosition();
}
//Получение адаптера выпадающего списка
public SpinnerAdapter getAdapter() {
return this.spinner.getAdapter();
}
}
Methods
getAdapter(), setAdapter(), getSelectedItemPosition()
simply “forward” actions to the internal Spinner. Attention should be paid to the method
setOnItemSelectedListener(OnItemSelectedListener l)
. I use one listener for all controls (I think it’s more correct) in which I switch(*some_unique_value*)...case(R.id.model)
determine with the help what to do next. Since the drop-down list inside our control does not have a unique global identifier (it is for all R.id.spinner), we write the identifier of the parent control ( spinner.setTag(getId());
) in the drop-down list tag . Now, when we call the change value handler in the drop-down list, we can identify which list has changed: Drop-down list handler:
class SpinnerItemSelectedListener implements OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView paramAdapterView,
View paramView, int paramInt, long paramLong) {
int id = ((SimpleParameter) paramAdapterView.getAdapter().getItem(
paramInt)).getId();
switch ((Integer) paramAdapterView.getTag()) {
case R.id.city:
initCityDependant(id);
break;
case ...:
otherMethod();
break;
default:
break;
}
}
@Override
public void onNothingSelected(AdapterView paramAdapterView) {
}
}
If our custom control was inherited directly from Spinner, then these crutches might not have been. But alas.
CustomSpinner on the mold
It remains to insert our new element into the application interface (layout):
There are 25 such controls on my form. Once I kind of read that you can specify the namespace of your application in the layout header and then it seems like there will be no need to write the full class name
org.solo12zw74.app.CustomSpinner
. But now for some reason it didn’t work out.Afterword
Thanks for attention. I would be very happy if someone gives links to sensible articles on the implementation of such control in the first way (inherited from Spinner). Or he will explain how to draw a progress bar in the onDraw () method, and then hide it if necessary.