Last Updated on February 6, 2018
Hi and welcome to another tutorial from Codingdemos, in this tutorial you will learn how create android custom spinner with images and text. The spinner will have a list of country names and flags, when you tap on any of the item an Android toast message will appear on the screen.
By the end of this article, we will have an app that looks like this. (Large preview)
In this tutorial we will be using the following:
-
– Android studio version 2.3.3
– Android emulator Nexus 5X with API 24
– Minimum SDK API 16
1- Open up Android Studio and create a new project and give it a name, in our case we’ve named it (SpinnerImages), choose API 16 as the minimum SDK, then choose a blank activity, click “Finish” and wait for Android Studio to build your project.
2- Let’s create a new xml file and name it custom_spinner_row.xml
, this file will have all the views that will be shown for each row inside Android spinner.
3- Inside custom_spinner_row.xml
will have 2 Android textviews and 1 Android imageview, the 2 textviews will be used for country name and population while the imageview will be used for the country flag.
< ?xml version="1.0" encoding="utf-8"?>
< RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
< ImageView
android:id="@+id/ivFlag"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp"
app:srcCompat="@mipmap/ic_launcher" />
< TextView
android:id="@+id/tvPopulation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/tvName"
android:layout_toEndOf="@+id/ivFlag"
android:layout_toRightOf="@+id/ivFlag"
android:padding="5dp"
android:text="TextView"
android:textStyle="bold" />
< TextView
android:id="@+id/tvName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/ivFlag"
android:layout_toRightOf="@+id/ivFlag"
android:padding="5dp"
android:text="TextView"
android:textColor="@color/colorAccent"
android:textSize="20sp"
android:textStyle="italic" />
< /RelativeLayout>
4- Open activity_main.xml
file, here we will add an Android spinner.
<?xml version="1.0" encoding="utf-8"?>
< LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.codingdemos.spinnerimages.MainActivity">
< Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="40dp" />
< /LinearLayout>
5- Next we need to create a custom adapter, this adapter will be used to initialize the views inside custom_spinner_row.xml
so that we can use it later with android spinner to show the data. Right click on the project package name com.codingdemos.spinnerimages
→ New
→ Java Class
6- I’ve named the custom adapter class as CustomAdapter.java
. To be able to access the functions of ArrayAdapter
we need to use extends
with ArrayAdapter<String>
. Once you do that you will see a red line under the class name, hover your mouse over that error and Android Studio will warn you that you need to create a constructor.
Android custom adapter constructor. (Large preview)
How to create Android custom adapter constructor. (Large preview)
7- From Choose Super Class Constructor
dialog box, choose the first option for the type of constructor ArrayAdapter(context:Context, resources:int)
public CustomAdapter(@NonNull Context context, @LayoutRes int resource) {
super(context, resource);
}
8- Let’s modify the constructor by replacing resource
parameter with 3 more parameters: String[]titles
, int[]images
and String[]population
String[] spinnerTitles;
int[] spinnerImages;
String[] spinnerPopulation;
Context mContext;
public CustomAdapter(@NonNull Context context, String[] titles, int[] images, String[] population) {
super(context, R.layout.custom_spinner_row);
this.spinnerTitles = titles;
this.spinnerImages = images;
this.spinnerPopulation = population;
this.mContext = context;
}
9- We will need to override few methods: getCount
, getView
and getDropDownView
-
–
getCount
: Return the number of items in the list. If you don’t override this method, the spinner list will be empty.–
getView
: This is where we work with initializing the views that we added them in the custom layout.–
getDropDownView
: This will show the data when you tap on Android spinner, if you don’t override this method your app will crash when you try to tap on the spinner.
@Override
public int getCount() {
return super.getCount();
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
return super.getView(position, convertView, parent);
}
@Override
public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
return super.getDropDownView(position, convertView, parent);
}
10- Declare an inner class and name it ViewHolder
, inside this class we will declare the views inside custom_spinner_row.xml
private static class ViewHolder {
ImageView mFlag;
TextView mName;
TextView mPopulation;
}
11- Inside getCount
method we will return the number of titles inside the spinner
@Override
public int getCount() {
return spinnerTitles.length;
}
12- Let’s initialize the views inside getView
method
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
ViewHolder mViewHolder = new ViewHolder();
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater) mContext.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.custom_spinner_row, parent, false);
mViewHolder.mFlag = (ImageView) convertView.findViewById(R.id.ivFlag);
mViewHolder.mName = (TextView) convertView.findViewById(R.id.tvName);
mViewHolder.mPopulation = (TextView) convertView.findViewById(R.id.tvPopulation);
convertView.setTag(mViewHolder);
} else {
mViewHolder = (ViewHolder) convertView.getTag();
}
mViewHolder.mFlag.setImageResource(spinnerImages[position]);
mViewHolder.mName.setText(spinnerTitles[position]);
mViewHolder.mPopulation.setText(spinnerPopulation[position]);
return convertView;
}
13- Inside getDropDownView
method we will return getView
by passing the position, the view and the parent.
@Override
public View getDropDownView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
return getView(position, convertView, parent);
}
14- Now we are done with CustomAdapter.java
class, next we will work with MainActivity.java
class to initialize the spinner, spinner titles, images and population.
15- Declare and initialize the titles, images and population.
String[] spinnerTitles;
String[] spinnerPopulation;
int[] spinnerImages;
spinnerTitles = new String[]{"Australia", "Brazil", "China", "France", "Germany", "India", "Ireland", "Italy", "Mexico", "Poland"};
spinnerPopulation = new String[]{"24.13 Million", "207.7 Million", "1.379 Billion", "66.9 Million", "82.67 Million", "1.324 Billion", "4.773 Million", "60.6 Million", "127.5 Million", "37.95 Million"};
spinnerImages = new int[]{R.drawable.flag_australia
, R.drawable.flag_brazil
, R.drawable.flag_china
, R.drawable.flag_france
, R.drawable.flag_germany
, R.drawable.flag_india
, R.drawable.flag_ireland
, R.drawable.flag_italy
, R.drawable.flag_maxico
, R.drawable.flag_poland};
16- Initialize the spinner and the custom adapter
CustomAdapter mCustomAdapter = new CustomAdapter(MainActivity.this, spinnerTitles, spinnerImages, spinnerPopulation);
mSpinner.setAdapter(mCustomAdapter);
17- Allow the user to tap on the items in the spinner by using Spinner.setOnItemSelectedListener
and show the selected item in an Android toast message.
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
Toast.makeText(MainActivity.this, spinnerTitles[i], Toast.LENGTH_SHORT).show();
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
18- Compile and run the app, you will see an android spinner being populated by country name, flag and it’s population.
19- One thing you will notice when you run the app, you will immediately see Android toast message containing spinner item appears on the screen without making any selection! To prevent that from happening, we need to override a method called <a href="https://developer.android.com/reference/android/app/Activity.html#onUserInteraction()">onUserInteraction</a>
. This method is used to detect that the user have interacted with the device while the activity is still running.
20- Let’s override onUserInteraction
@Override
public void onUserInteraction() {
super.onUserInteraction();
isUserInteracting = true;
}
21- We will use the boolean variable isUserInteracting
inside android spinner onItemSelected
method to show the toast message.
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
if (isUserInteracting) {
Toast.makeText(MainActivity.this, spinnerTitles[i], Toast.LENGTH_SHORT).show();
}
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
22- Compile and run the app again, this time you will not see the spinner item shows up immediately on the screen thanks to onUserInteraction
. The source code for the android custom spinner tutorial is available on Github. I hope you find this tutorial helpful and if you have any question please post them in the comment below.
It is very nice tutorial
how to control the size and margin of the drop down list?
Hi, you can change the width of the spinner by changing
android:layout_width="match_parent"
toandroid:layout_width="wrap_content"
and you can also reduce the text and image size.Very useful.
Not sure why you need the ViewHolder?
Surely if yo uhave the View then you can just get the child text and images and set them on thw view…
First thanks,
I have problems with getDropDownView I am getting convertView in null and this exception
E/AndroidRuntime: FATAL EXCEPTION: main Process: pe.com.startapps.implementos, PID: 8770 java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.view.View.toString()' on a null object reference at pe.com.startapps.implementos.adapters.CustomSpinnerAdapter.getDropDownView(CustomSpinnerAdapter.java:61) at android.widget.Spinner$DropDownAdapter.getDropDownView(Spinner.java:994) at android.widget.Spinner$DropDownAdapter.getView(Spinner.java:990) at android.widget.AbsListView.obtainView(AbsListView.java:2366) at android.widget.ListView.measureHeightOfChildren(ListView.java:1408) at android.widget.ListView.onMeasure(ListView.java:1315) at android.view.View.measure(View.java:23169) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749) at android.widget.FrameLayout.onMeasure(FrameLayout.java:185) at android.view.View.measure(View.java:23169) at com.android.internal.widget.AlertDialogLayout.tryOnMeasure(AlertDialogLayout.java:144) at com.android.internal.widget.AlertDialogLayout.onMeasure(AlertDialogLayout.java:69) at android.view.View.measure(View.java:23169) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749) at android.widget.FrameLayout.onMeasure(FrameLayout.java:185) at android.view.View.measure(View.java:23169) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749) at android.widget.FrameLayout.onMeasure(FrameLayout.java:185) at android.view.View.measure(View.java:23169) at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:6749) at android.widget.FrameLayout.onMeasure(FrameLayout.java:185) at com.android.internal.policy.DecorView.onMeasure(DecorView.java:716) at android.view.View.measure(View.java:23169) at android.view.ViewRootImpl.performMeasure(ViewRootImpl.java:2718) at android.view.ViewRootImpl.measureHierarchy(ViewRootImpl.java:1545) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1855) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1460) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7183) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:949) at android.view.Choreographer.doCallbacks(Choreographer.java:761) at android.view.Choreographer.doFrame(Choreographer.java:696) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:935) at android.os.Handler.handleCallback(Handler.java:873) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:6669) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)