Android - API - Creating a Spinner with colors as choices

With Android we haven't a classic ComboBox like in other frameworks but we have instead a Spinner.

Actually it's exactly the same and only the name differs.

In this Android Spinner tutorial we're going to create our own cutom Spinner by replacing classic texts with squares of color.

First of all

The minimum SDK for this tutorial is the API 14 (Android 4.0 or also known as IceCreamSandwich).

Spinner, Object class and 2's complement will be see in this example.

The MainActiviy is quite simple and so easy to understand.

We have then the BaprogArrayAdapter which is a class that extends the Android ArrayAdapter.

Within we're going to fill an array of Integers (list of colors) and use this array to display every element (color) in this array.

We use Integer and not int type because we can't use primitive types, indeed Integer inherits from Object whereas int doesn't.

In our example we don't specify the type when we declare our ArrayAdapter but as this class is a template one we could have replaced Object[] objects with Integer[] objects in the constructor parameters.

And thus not having to cast it.

The FirstFragment contains data needed to create our Spinner of colors.

We start by creating an array of Integers in which we add the 3 colors present in the file colors.xml.

Then we link our BadprogArrayAdapter with this Fragment.

And finally when the user will click on a color, this color will be automatically changed in the View in the center of the Fragment.

It's interesting to notice here that the value gets from the Spinner.getSelectedItem() method is an Object type.

It's an ID that only the Android OS knows how to use it.

So we can't directly use this Object like that, we have to cast it.

In our case we cast it into an int.

Once converted into an int, we can't either use it, we have to call the ContextCompat.getColor() method in order to retrieve a decimal value.

This decimal value could be for example "-16777216".

Impossible to understand what it is except if we convert it in HEX format with two's complement.

That is #FF000000 (black color).

Code

MainActivity.java

// badprog.com
package com.badprog.badprogspinnercolors;

import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.appcompat.widget.Toolbar;

// ===============================================================================================
//
// ===============================================================================================
public class MainActivity extends AppCompatActivity {

 // ===============================================================================================
 //
 // ===============================================================================================
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  //
  super.onCreate(savedInstanceState);

  //
  setContentView(R.layout.activity_main);

  //
  Toolbar toolbar = findViewById(R.id.toolbar);
  setSupportActionBar(toolbar);
 }
}

BadprogArrayAdapter.java

// badprog.com
package com.badprog.badprogspinnercolors;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;

import androidx.annotation.NonNull;

// ===============================================================================================
//
// ===============================================================================================
public class BadprogArrayAdapter extends ArrayAdapter {

 private Integer[] _listOfColors;

 // ===============================================================================================
 //
 // ===============================================================================================
 public BadprogArrayAdapter(@NonNull Context context, int resource, @NonNull Object[] objects) {
  //
  super(context, resource, objects);

  //
  _listOfColors = (Integer[]) objects;
 }

 // ===============================================================================================
 //
 // ===============================================================================================
 @Override
 public View getDropDownView(int position, View convertView, ViewGroup parent) {
  //
  return getBadprogView(position, convertView, parent);
 }

 // ===============================================================================================
 //
 // ===============================================================================================
 @Override
 public View getView(int position, View convertView, ViewGroup parent) {
  //
  return getBadprogView(position, convertView, parent);
 }

 // ===============================================================================================
 //
 // ===============================================================================================
 public View getBadprogView(int position, View convertView, ViewGroup parent) {
  //
  LayoutInflater inflater = LayoutInflater.from(getContext());
  View viewXml = inflater.inflate(R.layout.badprog_spinner_color, parent, false);

  //
  View viewElement   = viewXml.findViewById(R.id.view);
  viewElement.setBackgroundColor(getContext().getResources().getColor(_listOfColors[position]));

  //
  return viewXml;
 }
}

FirstFragment.java

// badprog.com
package com.badprog.badprogspinnercolors;

import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;

import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.fragment.app.Fragment;

// ===============================================================================================
//
// ===============================================================================================
public class FirstFragment extends Fragment implements AdapterView.OnItemSelectedListener {
 //
 private View _viewDisplay;
 private Spinner _spinner;

 Integer _arrayOfColors [] = { R.color.colorAccent, R.color.colorPrimary, R.color.colorPrimaryDark };

 // ===============================================================================================
 //
 // ===============================================================================================
 @Override
 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  // Inflate the layout for this fragment
  return inflater.inflate(R.layout.fragment_first, container, false);
 }

 // ===============================================================================================
 //
 // ===============================================================================================
 public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
  //
  super.onViewCreated(view, savedInstanceState);

  //
  ArrayAdapter arrayAdapter = new BadprogArrayAdapter(FirstFragment.this.getContext(), android.R.layout.simple_spinner_item, _arrayOfColors);
  arrayAdapter.setDropDownViewResource(android.R.layout.select_dialog_multichoice);

  //
  _spinner = view.findViewById(R.id.spinner_colors);
  _spinner.setOnItemSelectedListener(this);
  _spinner.setAdapter(arrayAdapter);

  //
  _viewDisplay = view.findViewById(R.id.view_display);
 }

 // ===============================================================================================
 //
 // ===============================================================================================
 @Override
 public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
  //
  Object object = _spinner.getSelectedItem();
  int color = ContextCompat.getColor(getContext(), (int)object);

  //
  _viewDisplay.setBackgroundColor(color);
 }

 // ===============================================================================================
 //
 // ===============================================================================================
 @Override
 public void onNothingSelected(AdapterView<?> parent) {
  //
 }

} 

res/layout/activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout 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"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay" />

    </com.google.android.material.appbar.AppBarLayout>

    <include layout="@layout/content_main" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

res/layout/badprog_spinner_color.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="5sp"
    >

    <View
        android:id="@+id/view"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:text="Button" />

</LinearLayout>

res/layout/content_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior">

    <fragment
        android:id="@+id/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:defaultNavHost="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout> 

res/layout/fragment_first.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    tools:context=".FirstFragment">

    <Spinner
        android:id="@+id/spinner_colors"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/view_display"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:text="Button"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/spinner_colors" />

</androidx.constraintlayout.widget.ConstraintLayout>

res/menu/menu_main.xml

<menu 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"
    tools:context="com.badprog.badprogspinnercolors.MainActivity">
    <item
        android:id="@+id/action_settings"
        android:orderInCategory="100"
        android:title="Title"
        app:showAsAction="never" />
</menu>

res/navigation/nav_graph.xml

<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="@+id/nav_graph"
    app:startDestination="@id/FirstFragment">

    <fragment
        android:id="@+id/FirstFragment"
        android:name="com.badprog.badprogspinnercolors.FirstFragment"
        tools:layout="@layout/fragment_first">
    </fragment>
</navigation>

res/values/colors.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="colorPrimary">#6200EE</color>
    <color name="colorPrimaryDark">#3700B3</color>
    <color name="colorAccent">#03DAC5</color>
</resources>

res/values/dimens.xml

<resources>
    <dimen name="fab_margin">16dp</dimen>
</resources>

res/values/string.xml

<resources>
    <string name="app_name">BadprogSpinnerColors</string>
</resources>

res/values/styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar" />

    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light" />

</resources>

Conclusion

You are now able to create your own Spinner in which you can add everything you want.

For example adding icons instead of colors should be a good idea.

Anyway, good job you did it. cool

Add new comment

Plain text

  • No HTML tags allowed.
  • Lines and paragraphs break automatically.
CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
Image CAPTCHA
Enter the characters shown in the image.