ZOFTINO.COM android and web dev tutorials

Android Spinner Custom Adapter & Layout

Spinner is a dropdown view which allows user to select an item from all available choices displayed in spinner. If each item in the spinner is just text, you can use array adapter and simple item layouts provided by android to provide dropdown in your app.

But each item contains multiple data elements or images then you need to create your own item layout and adapter class.

You can find detailed information in my previous post with examples on spinner, spinner attributes, populating spinner with data in various formats, and item select listener.

In this post, I’ll show how to create custom item layout with multiple views in it and custom adapter to populate data in spinner using one of the screens in coupon app.

android spinner custom layout and custom adapter

Spinner Custom Item Layout

For this example, the screen in coupon app allows user to select type of offer and based on the selection it shows offers in the category. Types of offer data is populated in spinner with each item containing offer type, number of offers and maximum discount. To accommodate these three elements, we’ll create a dropdown item layout with three text view elements.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
    android:layout_marginBottom="8dp"
    android:layout_marginTop="8dp"
    android:background="@drawable/spinneritemborder">
    <TextView
        android:id="@+id/offer_type_txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginTop="8dp"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"></TextView>

    <TextView
        android:id="@+id/max_discount_txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:layout_marginLeft="16dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toBottomOf="@+id/offer_type_txt"></TextView>

    <TextView
        android:id="@+id/num_offers_txt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="TextView"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/max_discount_txt"
        android:layout_marginLeft="8dp"
        app:layout_constraintBaseline_toBaselineOf="@+id/max_discount_txt"
        app:layout_constraintHorizontal_bias="0.775"></TextView>
</android.support.constraint.ConstraintLayout>

Spinner Custom Adapter

We will create spinner custom adapter by extending ArrayAdapter and overriding getView and getDropDownView methods which return item views for spinner in normal state and drop down state respectively.

package com.zoftino.androidui;


import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import java.util.List;

public class CustomArrayAdapter extends ArrayAdapter<String>{

    private final LayoutInflater mInflater;
    private final Context mContext;
    private final List<Offer> items;
    private final int mResource;

    public CustomArrayAdapter(@NonNull Context context, @LayoutRes int resource,
                              @NonNull List objects) {
        super(context, resource, 0, objects);

        mContext = context;
        mInflater = LayoutInflater.from(context);
        mResource = resource;
        items = objects;
    }
    @Override
    public View getDropDownView(int position, @Nullable View convertView,
                                @NonNull ViewGroup parent) {
        return createItemView(position, convertView, parent);
    }

    @Override
    public @NonNull View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        return createItemView(position, convertView, parent);
    }

    private View createItemView(int position, View convertView, ViewGroup parent){
        final View view = mInflater.inflate(mResource, parent, false);

        TextView offTypeTv = (TextView) view.findViewById(R.id.offer_type_txt);
        TextView numOffersTv = (TextView) view.findViewById(R.id.num_offers_txt);
        TextView maxDiscTV = (TextView) view.findViewById(R.id.max_discount_txt);

        Offer offerData = items.get(position);

        offTypeTv.setText(offerData.getOfferType());
        numOffersTv.setText(offerData.getNumberOfCoupons());
        maxDiscTV.setText(offerData.getMaxDicount());

        return view;
    }
}

Spinner Item Divider

To create a divider to separate items in spinner, we defined rectangle shape object in xml and saved it in res/drawer folder. Then set the defined shape as a back ground for spinner item layout.

In the definition of rectangle shape, gradient element’s attribute angle is used to draw line for only one side of the rectangle by using 0, 90, 180 and 360. In the definition, gradient angle is set to 90 so that line is drawn for bottom side of rectangle.

 <?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <gradient
        android:angle="90"
        android:startColor="#2a4ada"
        android:centerColor="@android:color/transparent"
        android:centerX="0.01" />
</shape>
 

Data Transfer Object

 package com.zoftino.androidui;

public class Offer {
    private String offerType;
    private String maxDicount;
    private String numberOfCoupons;

    public String getOfferType() {
        return offerType;
    }

    public void setOfferType(String offerType) {
        this.offerType = offerType;
    }

    public String getMaxDicount() {
        return maxDicount;
    }

    public void setMaxDicount(String maxDicount) {
        this.maxDicount = maxDicount;
    }

    public String getNumberOfCoupons() {
        return numberOfCoupons;
    }

    public void setNumberOfCoupons(String numberOfCoupons) {
        this.numberOfCoupons = numberOfCoupons;
    }
}
 

Activity

 package com.zoftino.androidui;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Spinner;
import android.widget.TextView;

public class ActivityCustomSpinner extends AppCompatActivity {

    private Spinner spinnerOfferType;
    private TextView selectedOffer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.customspinner);

        spinnerOfferType = (Spinner) findViewById(R.id.spinnerOfferType);
        CustomArrayAdapter adapter = new CustomArrayAdapter(this,
                 R.layout.customspinneritem, CouponStoreData.getOfferData());


        selectedOffer = (TextView) findViewById(R.id.selectedOffer);

        spinnerOfferType.setAdapter(adapter);

        spinnerOfferType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){
            public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) {
                String item = ((TextView)view.findViewById(R.id.offer_type_txt)).getText().toString();
                selectedOffer.setText(item);
            }

            public void onNothingSelected(AdapterView<?> parent) {

            }
        });
    }
}
 

Activity Layout

 <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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"
    android:layout_marginBottom="8dp"
    android:layout_marginTop="8dp">
    <Spinner
        android:id="@+id/spinnerOfferType"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginTop="8dp"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginStart="8dp"
        android:layout_marginEnd="8dp"
        android:spinnerMode="dropdown"></Spinner>

    <TextView
        android:id="@+id/selectedOffer"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text=""
        android:layout_marginTop="350dp"
        app:layout_constraintTop_toBottomOf="@+id/spinnerOfferType"
        android:layout_marginLeft="8dp"
        app:layout_constraintLeft_toLeftOf="parent"
        android:layout_marginRight="8dp"
        app:layout_constraintRight_toRightOf="parent"></TextView>

</android.support.constraint.ConstraintLayout>