tisdag 27 mars 2012

Android : Custom spinner with custom object!

Lets spin forward by implementing a custom spinner with a custom drop down view!
Using style= tag on the spinner didnt work for me to override the styles, so i just changed the background.
First a custom drawable selector with the appropiate images.

drawable/btn_dropdown.xml
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_window_focused="false" android:state_enabled="true" android:drawable="@drawable/btn_dropdown_normal" />
    <item android:state_pressed="true" android:drawable="@drawable/btn_dropdown_pressed" />
    <item android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/btn_dropdown_pressed" />
    <item android:state_enabled="true" android:drawable="@drawable/btn_dropdown_normal" />
    <item android:drawable="@drawable/btn_dropdown_normal" />
</selector>

Next, i just reworked the btn_dropdown_* images that ships with the Android SDK. They should be located in ~androidsdkroot/platforms/android-(yourversion)/data/res/drawable-hdpi. There's 5 different states that can be used but i just made images for two. Normal & pressed.

Okay, the images done! Just need to change the spinner background to @drawable/btn_dropdown.xml to get our own look! I still use the android.R.layout.simple_spinner_item to display the objects in my spinner.
Lets get going, the main layout:

layout/main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <Button
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Custom Spinner with custom data" />
    <Spinner  android:id="@+id/SpinnerOrginal"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              />
    <Spinner android:id="@+id/SpinnerCustom"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:background="@drawable/btn_dropdown"
             />
    <Button android:id="@+id/buttonUseItem"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:text="Use Selection"/>
    <TextView android:id="@+id/myTextView"
              android:layout_height="wrap_content"
              android:layout_width="wrap_content"/>
</LinearLayout>
Next up our helper class from the previous entry with a little modification.
src/CountryInfo.java
public class CountryInfo {
    private String countryName;
    private long countryPopulation;
    private int countryFlag; // Populate it with our resource ID for the correct image.
    
    public CountryInfo(String cName, long cPopulation, int flagImage)
    {
        countryName = cName;
        countryPopulation = cPopulation;
        countryFlag = flagImage;
    }
    public String getCountryName()
    {
        return countryName;
    }
    public long getCountryPopulation()
    {
        return countryPopulation;
    }
    public int getCountryFlag()
    {
        return countryFlag;
    }
    public String toString()
    {
        return countryName;
    }
}
And lastly our main activity with our CountryAdapter class that will implement the view.
src/SpinnerTest.java (First part)

import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import java.util.ArrayList;

public class SpinnerTest extends Activity
{
    
    Button button_UseSelectedItem;
    Spinner mySpinner;
    TextView myTextView;
    ArrayList<CountryInfo> myCountries;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        myCountries = populateList();
        setContentView(R.layout.main);
        mySpinner = (Spinner) findViewById(R.id.SpinnerCustom);
        Spinner OrginalSpinner = (Spinner) findViewById(R.id.SpinnerOrginal);
        button_UseSelectedItem = (Button) findViewById(R.id.buttonUseItem);
        myTextView = (TextView) findViewById(R.id.myTextView);

        CountryAdapter myAdapter = new CountryAdapter(this, android.R.layout.simple_spinner_item, myCountries);

        mySpinner.setAdapter(myAdapter);
        OrginalSpinner.setAdapter(myAdapter);
        
        button_UseSelectedItem.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // Can also use mySpinner.setOnItemClickListener(......) 
                // Using a separate button here as there's often other data to select
                // or if you choose the wrong item.
                CountryInfo myCountry;
                if(mySpinner.getSelectedItem() != null)
                {
                    myCountry = (CountryInfo) mySpinner.getSelectedItem();
                    myTextView.setText(String.format("Country: " + myCountry.getCountryName() + "\t Population: " + myCountry.getCountryPopulation()));
                }
            }
        });
    }

    public ArrayList<CountryInfo> populateList()
    {
        ArrayList<CountryInfo> myCountries = new ArrayList<CountryInfo>();
        myCountries.add(new CountryInfo("USA", 308745538, R.drawable.usa)); // Image stored in /drawable
        myCountries.add(new CountryInfo("Sweden", 9482855, R.drawable.sweden));
        myCountries.add(new CountryInfo("Canada", 34018000, R.drawable.canada));
        return myCountries;
    }


As you see it looks like the previous one except we have created a custom adapter for our new spinner. populateList() method also gets the ints from the icons i have in the drawable/ directory.
Okay lets continue with the adapter!
src/SpinnerTest.java (2nd part)
public class CountryAdapter extends ArrayAdapter<CountryInfo>
    {
        private Activity context;
        ArrayList<CountryInfo> data = null;

        public CountryAdapter(Activity context, int resource, ArrayList<CountryInfo> data)
        {
            super(context, resource, data);
            this.context = context;
            this.data = data;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) 
        {   // Ordinary view in Spinner, we use android.R.layout.simple_spinner_item
            return super.getView(position, convertView, parent);   
        }

        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent)
        {   // This view starts when we click the spinner.
            View row = convertView;
            if(row == null)
            {
                LayoutInflater inflater = context.getLayoutInflater();
                row = inflater.inflate(R.layout.spinner_layout, parent, false);
            }

            CountryInfo item = data.get(position);

            if(item != null)
            {   // Parse the data from each object and set it.
                ImageView myFlag = (ImageView) row.findViewById(R.id.imageIcon);
                TextView myCountry = (TextView) row.findViewById(R.id.countryName);
                if(myFlag != null)
                {
                    myFlag.setBackgroundDrawable(getResources().getDrawable(item.getCountryFlag()));
                }
                if(myCountry != null)
                    myCountry.setText(item.getCountryName());

            }

            return row;
        }
    }
}



getView() method could be skipped, typed it along for clarity. getDropDownView() inflates my own layout for the drop down list. I just inflate it and set the corresponding ImageView & TextView from each object in the ArrayList. Plenty of information if you google ListView and custom adapters.

layout/spinner_layout.xml ( LinearLayout with a ImageView and TextView )

<?xml version="1.0" encoding="utf-8" ?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="fill_parent"
              android:layout_height="wrap_content"
              android:orientation="horizontal"
              android:background="@drawable/bluegradient">
<ImageView android:id="@+id/imageIcon"
           android:layout_width="wrap_content"
           android:layout_height="wrap_content"
           android:background="@drawable/canada"/>
<TextView android:id="@+id/countryName"
          android:singleLine="true"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          android:gravity="center"
          android:ellipsize="marquee"
          style="@style/SpinnerText"/>
</LinearLayout>

drawable/bluegradient.xml (Our background in the dropdown view, changes when item is pressed)
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" >
        <shape>
            <solid
                android:color="#2183b0" />
            <stroke
                android:width="1dp"
                android:color="#adc6e8" />
           <corners
                android:radius="4dp" />
            <padding
                android:left="4dp"
                android:top="4dp"
                android:right="4dp"
                android:bottom="4dp" />
        </shape>
    </item>
    <item>
        <shape>
            <gradient
                android:startColor="#2183b0"
                android:endColor="#7cbfde"
                android:angle="270" 
                android:type="linear"
                />
            <stroke
                android:width="1dp"
                android:color="#2183b0" />
            <corners
                android:radius="4dp" />
            <padding
                android:left="0dp"
                android:top="4dp"
                android:right="0dp"
                android:bottom="4dp" />
        </shape>
    </item>
</selector>


That's it! Final result, spinner closed, and open!


15 kommentarer:

  1. style="@style/SpinnerText" = ?

    SvaraRadera
    Svar
    1. Custom style for the text, let's see if i can find it.
      Here you go, http://pastebin.com/Mfrqxjgf
      Need to update this for the Holo look i think. <.<

      Radera
  2. Hey! Very nice tutorial...Can I use your image in my project ?

    SvaraRadera
  3. Where do I put the images to get them to override the gray ones that keep showing up no mater where I paste your blue images?

    SvaraRadera
    Svar
    1. Hi, have you specified the spinner background drawable ?
      drawable/btn_dropdown.xml which links to images based on the state.

      Radera
    2. I still had "android:drawable/btn_dropdown" in my activity. I removed the android keyword and it works now. Thanks.

      Radera
  4. Will you please upload whole application as project. I am getting error

    SvaraRadera
    Svar
    1. Sorry, project is lost locally. Whats the error ?

      Radera
  5. I get error with android 4.1.2 (API 16) in line:
    LayoutInflater inflater = context.getLayoutInflater();

    I fixed:
    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);

    SvaraRadera
  6. Good Insight, follow my site to get more insight

    Android App Developmemt

    SvaraRadera
  7. wordpress website design studio Need professional WordPress Web Design Services? We're experts in developing attractive mobile-friendly WordPress websites for businesses. Contact us today!

    SvaraRadera