Related
Dropdown list for country and city:
I want to make a spinner in android just like this attached image. For example, you select the list and you can choose any city from the country.Only the cities should be clickable and can be stored from the list. The country name should not be clickable.Having problem in disabling some of the items from the spinner.
This is the xml for the spinner.
`<Spinner
android:id="#+id/districtSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:hint="District"
android:textSize="16sp"
android:theme="#style/MyEditText"
/>`
And this is the code:
`Spinner DistrictBetterSpinner;
String[] District_Spinner = new String[]{"Punjab", "Rawalpindi", "Attock", "Faisalabad", "Chakwal", "Bhakkar", "Jhelum", "Multan", "Chiniot"
, "Bhawalpur", "Jhelum", "Gujrat", "Gujranwala", "Lahore", "Mianwali", "Khyber Pakhtunkhwa (KPK)", "Abbotabad", "Haripur", "Kohat",
"Sindh", "Hyderabad", "Jacobabad", "Balochistan", "Awaran", "Gawadar", "Federally Administered Tribal Areas", "Khyber Agency",
"Mohmat Agency", "Azad Jammu and Kashmir", "Neelum", "Bagh", "Gilgit Baltistan", "Skardu", "Astore"};
List<String> spinnerlist;
ArrayAdapter<String> arrayadapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_post);
DistrictBetterSpinner = (Spinner) findViewById(R.id.districtSpinner);
spinnerlist = new ArrayList<>(Arrays.asList(District_Spinner));
arrayadapter = new ArrayAdapter<String>(AddPostActivity.this, R.layout.spinner_item, spinnerlist);
public boolean isEnabled(int position){
if (position == 0 || position==1) {
return false;
} else {
return true;
}
}
#Override
public View getDropDownView ( int position, View convertView, ViewGroup parent){
View view = super.getDropDownView(position, convertView, parent);
TextView spinnerTextView = (TextView) view;
if (position == 0) {
// Set the disable item text color
spinnerTextView.setTextColor(Color.BLACK);
} else {
spinnerTextView.setTextColor(Color.GRAY);
}
return view;
}
}
;
arrayadapter.setDropDownViewResource(R.layout.spinner_item);
DistrictBetterSpinner.setAdapter( arrayadapter);
`
I think the following may be what you are after (new lines added to getDropDownView after you set the TextColor for Cities to grey):-
#Override
public View getDropDownView ( int position, View convertView, ViewGroup parent){
View view = super.getDropDownView(position, convertView, parent);
TextView spinnerTextView = (TextView) view;
if (position == 0) {
// Set the disable item text color
spinnerTextView.setTextColor(Color.BLACK);
} else {
spinnerTextView.setTextColor(Color.GRAY);
spinnerTextView.setOnclickListener( new View.OnClickListener() {
#Override
public void onClick(View view) {
//YOUR ONCLICK HANDLING CODE HERE
}
});
}
return view;
}
P.S. in my comment above I mentioned getView as an alternative. It isn't as that's the selection view rather than the dropdown (got mixed up with ListViews).
Looking more closely at your Answer and Code. I see that you haven't got anywhere really. So here's a rudimentary example based upon your code.
First a new layout file for an entry in the dropdown list (I've also cheated a little and used this for the selected entry, you probably have a separate layout for each). This is spinner_item.xml :-
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/spinnerTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
As you want customised features you should probably have a custom adapter (which is what you appeared to be attemtping in your code). The adapter handles placing the data (arrayList in your case) into the views used by the spinner (the term used is inflate). So here's the custom adapter which I've placed into it's own file. This is MyArrayAdapter.java :-
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
/**
* Created by Mike092015 on 31/10/2016.
*/
public class MyArrayAdapter extends ArrayAdapter<String> {
private Context context;
private TextView district;
private ArrayList<String> districts;
public MyArrayAdapter(Activity context, int layout, ArrayList<String> districts) {
super(context, layout, districts);
this.context = context;
this.districts = districts;
}
#Override
public View getDropDownView(int position, View convertview, ViewGroup parent) {
View v = convertview;
if (v == null) {
v = LayoutInflater.from(this.context).inflate(R.layout.spinner_item,parent,false);
}
district = (TextView) v.findViewById(R.id.spinnerTextView);
district.setText(districts.get(position));
district.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(context,"You clicked me",Toast.LENGTH_SHORT).show();
}
});
return v;
}
public View getView(int position, View convertview, ViewGroup parent) {
View v = convertview;
if (v == null) {
v = LayoutInflater.from(this.context).inflate(R.layout.spinner_item,parent,false);
}
district = (TextView) v.findViewById(R.id.spinnerTextView);
district.setText(districts.get(position));
return v;
}
}
Note the onClickListener in the getDropDownView this is where you catch and handle the clicking of an entry (in this case it just issues a toast).
activity_main.xml (the layout that includes the spinner, little, if any different from your layout) is :-
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Testing"/>
<Spinner
android:id="#+id/districtSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:hint="District"
android:textSize="16sp"
/>
</LinearLayout>
Lastly the activity, to test this I've used MainActivity, so MainActicity.java is:-
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Spinner;
import java.util.ArrayList;
import java.util.Arrays;
/**
* Created by Mike092015 on 3/06/2016.
*/
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Spinner DistrictBetterSpinner;
String[] District_Spinner = new String[]{"Punjab", "Rawalpindi", "Attock", "Faisalabad", "Chakwal", "Bhakkar", "Jhelum", "Multan", "Chiniot"
, "Bhawalpur", "Jhelum", "Gujrat", "Gujranwala", "Lahore", "Mianwali", "Khyber Pakhtunkhwa (KPK)", "Abbotabad", "Haripur", "Kohat",
"Sindh", "Hyderabad", "Jacobabad", "Balochistan", "Awaran", "Gawadar", "Federally Administered Tribal Areas", "Khyber Agency",
"Mohmat Agency", "Azad Jammu and Kashmir", "Neelum", "Bagh", "Gilgit Baltistan", "Skardu", "Astore"};
ArrayList<String> spinnerlist;
MyArrayAdapter arrayadapter;
DistrictBetterSpinner = (Spinner) findViewById(R.id.districtSpinner);
spinnerlist = new ArrayList<>(Arrays.asList(District_Spinner));
arrayadapter = new MyArrayAdapter(this,R.layout.spinner_item,spinnerlist);
arrayadapter.setDropDownViewResource(R.layout.spinner_item);
DistrictBetterSpinner.setAdapter(arrayadapter);
}
}
Note there are some changes but it is very much based upon your code.
This question already has answers here:
How to change color and font on ListView
(9 answers)
Closed 8 years ago.
Thanks all of you!! I am beginning of Android.I have a little problem.I use ListView, When I run this programe then my all List Item is White Color!!! How do this text color black or anthor please anyone help me!!
package com.example.shikkokoverflow_listview;
import android.os.Bundle;
import android.app.ListActivity;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.Toast;
public class MainActivity extends ListActivity {
String[] country={"Bangladesh","usa","america","india","Florida"};
ArrayAdapter<String> adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
adapter=new ArrayAdapter<String>(getApplicationContext(), android.R.layout.simple_list_item_1, country);
setListAdapter(adapter);
}
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
//super.onListItemClick(l, v, position, id);
Toast.makeText(getApplicationContext(), country[position], Toast.LENGTH_LONG).show();
}
}
getListView().setCacheColorHint(Color.rgb(36, 33, 32));
Here, do these steps
Go to sdk folder \sdk\platforms\android-\data\res\layout
Copy simple_list_item_1 and paste it in your projects res\layout folder
Now open \res\layout\simple_list_item_1
Add color attribute there.
then change your
adapter=new ArrayAdapter<String>(getApplicationContext(),android.R.layout.simple_list_item_1, country);
to
adapter=new ArrayAdapter<String>(getApplicationContext(), R.layout.simple_list_item_1, country);
Use a customized layout. Define a layout file with your own Views where you can customize your font, font color, font size, images if you need... and then simply use it in your ArrayAdapter declaration.
myAdapt = new MyArrayArrayAdapter(this, R.id.your_layout, list);
Make a new layout file in res/layout. In that make the root element, a TextView (which cannot have any child). Then set all the necessary attributes and in addition to that set android:textColor="#000000". In your code, while making the ArrayAdapter write this:
adapter=new ArrayAdapter<String>(getApplicationContext(), R.layout.new_layout, country);
setListAdapter(adapter);
You will use instead of predefined Adapter into custom list adapter the process of custom adapter is...
You can get the custom adapter in class like this...
ListView listView = (ListView)findViewById(R.id.listview);
CustomAdapter mAdapter = new CustomAdapter(this, R.layout.listitem, mListItems);//listitem is your custom layout.....
listView .setAdapter(mAdapter);
Custom adapter class you just add this....in your project...
public class CustomAdapter extends ArrayAdapter<Sample> {
public ArrayList<Sample> mlist;
public Context context;
public LayoutInflater inflater;
private LinearLayout layout;
private View view;
private View mLastView;
public CustomAdapter(Context context, int resource, ArrayList<Sample> mlist) {
super(context, resource);
this.mlist = mlist;
this.context = context;
inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getPosition(Sample item) {
return super.getPosition(item);
}
#Override
public Sample getItem(int position) {
return mlist.get(position);
}
#Override
public int getCount() {
return mlist.size();
}
#Override
public long getItemId(int position) {
return super.getItemId(position);
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
view = inflater.inflate(R.layout.listitem, null);//listitem will be a your cutom layout here i use two textview in the same item...
layout = (LinearLayout)view.findViewById(R.id.linearlayoutSample);;
TextView text1 = (TextView) view.findViewById(R.id.item1);
TextView text2 = (TextView) view.findViewById(R.id.item2);
layout.setBackgroundColor(Color.BLACK);
text1.setText(mlist.get(position).getListitem1());
text2.setText(mlist.get(position).getListitem2());
return view;
}
}
You should make another activity with a colored textview and relate your listview to it ..... with
list1.setAdapter(new ArrayAdapter<String>(this,R.layout.row,R.id.row_txt,item));
I try to show something into listview using arraylist and simple adapter.
I tried something like below but in my result shows the last names of the arraylist.
What is my wrong i cant understand.
final ListView listView = (ListView) findViewById(R.id.mylist);
ArrayList<HashMap<String, String>> list_of_bookmarks = new ArrayList<HashMap<String, String>>();
HashMap<String, String> b = new HashMap<String, String>();
String[] from = { "php_key","c_key","android_key","hacking_key" };
String[] name_of_bookmarks = { "php","c","android","hacking" };
for(int i=0;i<4;i++)
{
b.put(from[i],name_of_bookmarks[i]);
list_of_bookmarks.add(b);
}
};
int[] to = { R.id.txt1,R.id.txt1,R.id.txt1,R.id.txt1};
SimpleAdapter adapter = new SimpleAdapter(getBaseContext(), list_of_bookmarks, R.layout.list_layout, from, to);
listView.setAdapter(adapter);
I just want to show "php","c","android","hacking" in a listview.
And what should be more efficient way to do that.I am a beginner so you may suggest a better way which should i follow
My advice to you would be to create a separate class that extends the Adapter(or some subclass of it)
Here is a simple example of a String array adapter.
package ro.gebs.captoom.adapters;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import antistatic.spinnerwheel.adapters.AbstractWheelTextAdapter;
import com.example.captoom.R;
public class LanguagesAdapter extends AbstractWheelTextAdapter {
// Countries names
private String languages[];
public LanguagesAdapter(Context context) {
super(context, R.layout.lang_item, NO_RESOURCE);
languages = context.getResources().getStringArray(R.array.lang_array);
setItemTextResource(R.id.language_txt);
}
#Override
public View getItem(int index, View cachedView, ViewGroup parent) {
View view = super.getItem(index, cachedView, parent);
return view;
}
#Override
public int getItemsCount() {
return languages.length;
}
#Override
protected CharSequence getItemText(int index) {
return languages[index];
}
}
and the usage is simple just use the method .setAdapter();
Or another example which uses an arrayAdapter:
package apc.example;
import java.util.ArrayList;
import utils.BitmapManager;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class PersonAdapter extends ArrayAdapter<Person> {
Context context;
int layoutResourceId;
ArrayList<Person> data = null;
public PersonAdapter(Context context, int layoutResourceId,
ArrayList<Person> data) {
super(context, layoutResourceId, data);
this.layoutResourceId = layoutResourceId;
this.context = context;
this.data = data;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
ItemHolder holder = null;
if (row == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ItemHolder();
holder.imgIcon = (ImageView) row.findViewById(R.id.icon);
holder.txtName = (TextView) row.findViewById(R.id.title);
holder.txtDescription = (TextView) row.findViewById(R.id.desc);
row.setTag(holder);
} else {
holder = (ItemHolder) row.getTag();
}
Person bean = data.get(position);
holder.txtName.setText(bean.getName());
holder.txtDescription.setText(bean.getDescription());
Bitmap b = BitmapFactory.decodeResource(context.getResources(), R.drawable.user);
BitmapManager.INSTANCE.setPlaceholder(b);
BitmapManager.INSTANCE.loadBitmap(bean.getUrl(), holder.imgIcon, 80, 80);
return row;
}
public static class ItemHolder {
public ImageView imgIcon;
TextView txtName;
TextView txtDescription;
}
public void updateAdapter(ArrayList<Person> pers){
this.data = pers;
}
}
This is an example of an adapter for a more complex class that has more fields rather than a simple string. But that can easily be modified to ArrayAdapter<String> and then go from there.
Anyways i think it's always a best practice to write your custom adapters for listviews.
Hope this helps!
Main.xml
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="60dp" >
<ListView
android:id="#+id/zone_list"
android:layout_marginBottom="70dp"
android:background="#drawable/batteryborder"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</ListView>
</LinearLayout>
setlanguage.xml
<?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="60dp">
<TextView
android:id="#+id/tvName"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:textSize="18dp"
android:gravity="center_vertical" />
</LinearLayout>
add in onCreate() of your activity file
ListView listView;
String[] from = { "php_key","c_key","android_key","hacking_key" };
ArrayAdapter arrayAdapter;
listView = (ListView) findViewById(R.id.zone_list);
arrayAdapter = new ArrayAdapter<>(this,R.layout.setlanguage, R.id.tvName, from);
listView.setAdapter(arrayAdapter);
You're reusing the same view in your int[] object.
int[] to = { R.id.txt1,R.id.txt1,R.id.txt1,R.id.txt1};
It looks like it's treating them all as the same object, so each time it adds a new item it changes the previous ones.
In order to use the SimpleAdapter you will need to define each view in the XML with different IDs.
int[] to = { R.id.txt1,R.id.txt2,R.id.txt3,R.id.txt4};
The SimpleAdapter may be simpler in regard to it's internal complexity, but it's definitely not simpler to actually use. With an ArrayAdapter you can just pass it the list of items and let it generate views automatically. It can be any size you need it to be so long as you don't run out of memory. (See below for example)
Once you start working with custom adapters I highly recommend you watch Romain Guy & Adam Powell's I/O talk. It's all a lot to take in when learning, but they do a great job of explaining how ListViews work.
//List of Items
String[] name_of_bookmarks = { "php","c","android","hacking" };
//Create your List object for the ArrayAdapter
//and make it the same size as name_of_books
List<String> listBookmarks = new ArrayList<String>(Array.getLength(name_of_bookmarks));
//Add name_of_bookmarks contents to listBookmarks
Collections.addAll(listBookmarks, name_of_books);
//Create an ArrayAdapter passing it the Context, a generic list item and your list
//An alternative to "this" would be "getApplicationContext()" from your main activity
//or "getActivity()" from a fragment. "getBaseContext()" is not recommended.
ArrayAdapter arrayAdapter = new ArrayAdapter(this, R.layout.list_item_text, listBookmarks);
//Set the adapter to your ListView
final ListView listView = (ListView) findViewById(R.id.mylist);
listView.setAdapter(arrayAdapter);
Try this one
public class MyFragment extends ListFragment{
String[] from = { "php_key","c_key","android_key","hacking_key" };
String[] name_of_bookmarks = { "php","c","android","hacking" };
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
List<HashMap<String, String>> list= new ArrayList<HashMap<String,String>>();
for (int i = 0; i < name_of_bookmarks.length; i++) {
HashMap<String, String> map= new HashMap<String, String>();
map.put("key", name_of_bookmarks[i]);
list.add(map);
}
String[] from = { "key" };
int[] to = { R.id.txt};
SimpleAdapter adapter = new SimpleAdapter(getActivity().getBaseContext(), list, R.layout.list_layout, from, to);
setListAdapter(adapter);
return super.onCreateView(inflater, container, savedInstanceState);
}
}
Whatever You Are Face Problem Exactly I Am Face Of The Problem Called "List
View Display Last Position Of Data Of An Array..."
The Problem Is Generated With Hash Map
final ListView listView = (ListView) findViewById(R.id.mylist);
ArrayList<HashMap<String, String>> list_of_bookmarks = new ArrayList<HashMap<String, String>>();
String[] from = { "php_key","c_key","android_key","hacking_key" };
String[] name_of_bookmarks = { "php","c","android","hacking" };
for(int i=0;i<4;i++)
{
HashMap<String, String> b = new HashMap<String, String>();
b.put(from[i],name_of_bookmarks[i]);
list_of_bookmarks.add(b);
}
};
int[] to = { R.id.txt1,R.id.txt1,R.id.txt1,R.id.txt1};
SimpleAdapter adapter = new SimpleAdapter(getBaseContext(), list_of_bookmarks, R.layout.list_layout, from, to);
listView.setAdapter(adapter);
Try Out This One If Any Doubt Created In Your Mind Then Ask Me Whatever Question Again
Simply You Have To Declared Your Hash Map Inside Your For Loop ...
Using Hash Map Inside Your For Loop Variable 'b' Created Each & Every Time With Considered As An Different Object. And Then Simply Array List Display Different Object Of Hash Map.
You Are Using Same Object To Store Value Of Hash Map And That Variable Was Override With Same Name That's Why you Are Faced The Problem
Thank You...
I have implementing a tutorial and I have a problem to adapt to my way.
The main.xml with de ListView
<?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">
<ListView android:id="#+id/listView1" android:layout_height="match_parent" android:layout_width="match_parent" />
</LinearLayout>
The rowview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content" android:weightSum="1">
<TextView android:layout_width="wrap_content"
android:layout_height="match_parent" android:id="#+id/text"
android:layout_weight="0.5" android:textSize="25sp" />
<Spinner android:layout_width="0dp" android:layout_height="wrap_content"
android:id="#+id/spin" android:prompt="#string/choice_prompt"
android:layout_weight="0.5" />
</LinearLayout>
The strings.xml file.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, ListViewTestActivity!</string>
<string name="app_name">ListViewTest</string>
<string name="choice_prompt">Select a choice</string>
<string-array name="choices">
<item>Alpha</item>
<item>Bravo</item>
<item>Charlie</item>
</string-array>
</resources>
The ListViewActivity class:
public class ListViewTestActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView listView = (ListView) findViewById(R.id.listView1);
DataHolder data = new DataHolder(this);
DataHolder data1 = new DataHolder(this);
DataHolder data2 = new DataHolder(this);
DataHolder data3 = new DataHolder(this);
DataHolder data4 = new DataHolder(this);
DataAdapter d = new DataAdapter(this, R.layout.rowview, new DataHolder[] { data, data1, data2, data3, data4 });
listView.setAdapter(d);
}
}
The DataHolder class:
public class DataHolder {
private int selected;
private ArrayAdapter<CharSequence> adapter;
public DataHolder(Context parent) {
adapter = ArrayAdapter.createFromResource(parent, R.array.choices, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}
public ArrayAdapter<CharSequence> getAdapter() {
return adapter;
}
public String getText() {
return (String) adapter.getItem(selected);
}
public int getSelected() {
return selected;
}
public void setSelected(int selected) {
this.selected = selected;
}
}
All the DataHolder class :
public class DataAdapter extends ArrayAdapter<DataHolder> {
private Activity myContext;
public DataAdapter(Activity context, int textViewResourceId, DataHolder[] objects) {
super(context, textViewResourceId, objects);
myContext = context;
}
// We keep this ViewHolder object to save time. It's quicker than findViewById() when repainting.
static class ViewHolder {
protected DataHolder data;
protected TextView text;
protected Spinner spin;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View view = null;
// Check to see if this row has already been painted once.
if (convertView == null) {
// If it hasn't, set up everything:
LayoutInflater inflator = myContext.getLayoutInflater();
view = inflator.inflate(R.layout.rowview, null);
// Make a new ViewHolder for this row, and modify its data and spinner:
final ViewHolder viewHolder = new ViewHolder();
viewHolder.text = (TextView) view.findViewById(R.id.text);
viewHolder.data = new DataHolder(myContext);
viewHolder.spin = (Spinner) view.findViewById(R.id.spin);
viewHolder.spin.setAdapter(viewHolder.data.getAdapter());
// Used to handle events when the user changes the Spinner selection:
viewHolder.spin.setOnItemSelectedListener(new OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
viewHolder.data.setSelected(arg2);
viewHolder.text.setText(viewHolder.data.getText());
}
#Override
public void onNothingSelected(AdapterView<?> arg0) {
}
});
// Update the TextView to reflect what's in the Spinner
viewHolder.text.setText(viewHolder.data.getText());
view.setTag(viewHolder);
Log.d("DBGINF", viewHolder.text.getText() + "");
} else {
view = convertView;
}
// This is what gets called every time the ListView refreshes
ViewHolder holder = (ViewHolder) view.getTag();
holder.text.setText(getItem(position).getText());
holder.spin.setSelection(getItem(position).getSelected());
return view;
}
}
To see the result of this code, see -> Android Listview with spinner and a checkbox
This code works, I have a listView with a spinner for each item, but how I can fill spinners with my own Strings? I tryed to fill them in the DataHolder class but it fails. (I have a list of Strings).
Thank's in advance.
All the work is done in the ADAPTER here your DataAdapter.
The cells are constructed in the method GetView, each cells is recycled : if convertView is null you create a new cell to use or else you use convertView.
i assume you must change some code in your DataHolder
public DataHolder(Context parent) {
adapter = ArrayAdapter.createFromResource(parent, R.array.choices, android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}
For now you must understand that the array of choices is provided statically :
R.array.choices
One solution would be to pass your data when constructing the viewHolders here :
viewHolder.data = new DataHolder(myContext);
should looks like something like this :
viewHolder.data = new DataHolder(myContext, myDataArray);
and then in your class change the instanciation of the adapter with something more like that..
public DataHolder(Context parent, ArrayList<String> dataArray) {
adapter = new ArrayAdapter<String>(myContext, yourTextViewId, dataArray);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
}
hopes it help
good luck
Following is some test code , done to recreate a strange bug: After deleting some items from a ListView , it stops refreshing when data is invalidated. More items are deleted but list does not refresh. Even Log cat does not show debug messages for deletion. I will appreciate if any one could find out what's wrong.
Item Layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="horizontal">
<TextView android:id="#+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button android:id="#+id/deleteButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Delete"
/>
</LinearLayout>
Item class:
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class Item implements View.OnClickListener {
private String name;
private View itemView;
private MyActivity owner;
//--- getters--
public String getName() {
return name;
}
public View getView() {
return itemView;
}
public Item(String n, Context c , MyActivity o)
{
//---store the name given--
name = n;
//---store reference to the owner activity--
owner = o;
//--- create a View for this item----
LayoutInflater inflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
itemView = inflater.inflate(R.layout.item,null);
//---set up data to show--
TextView nameTextView = (TextView) itemView.findViewById(R.id.nameTextView);
Button deleteButton = (Button) itemView.findViewById(R.id.deleteButton);
nameTextView.setText(name);
//---set up events to be handled--
deleteButton.setOnClickListener(this);
Log.d("My_Test","Item: Hello world, my name is " + name);
}
//----request owner to delete this item---
#Override
public void onClick(View view) {
Log.d("My_Test","Item:"+name+" requesting owner to delete me");
owner.deleteItem(this);
}
Activity layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<ListView android:id="#+id/myListView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
Activity class:
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import java.util.ArrayList;
public class MyActivity extends Activity {
private ArrayList<Item> myItems;
private ListView myListView;
private ArrayAdapter<Item> myArrayAdapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//-----adapter for item list----
//----since each item has its own view , it just returns the same---
myArrayAdapter = new ArrayAdapter<Item>(this,0){
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
Item item = getItem(position);
Log.d("My_Test","Adapter : View for Item: " + item.getName() +"is requested." );
return item.getView();
}
};
//-----set up my list view with the adapter------
myListView = (ListView) findViewById(R.id.myListView);
myListView.setAdapter(myArrayAdapter);
//------add items-------
//----each item has its own view and a reference to this activity as their owner----
myArrayAdapter.add(new Item("Sunday", this, this));
myArrayAdapter.add(new Item("Monday", this, this));
myArrayAdapter.add(new Item("Tuesday", this, this));
myArrayAdapter.add(new Item("Wednesday", this, this));
myArrayAdapter.add(new Item("Thursday", this, this));
myArrayAdapter.add(new Item("Friday", this, this));
myArrayAdapter.add(new Item("Saturday", this, this));
myArrayAdapter.notifyDataSetChanged();
}
//----- called by items requesting to be deleted from the item list----
public void deleteItem(Item item) {
myArrayAdapter.remove(item);
Log.d("My_Test","Owner : Deleted item :" + item.getName());
myArrayAdapter.notifyDataSetChanged();
}
}
Looks like ListView stops re-drawing it self. Even when List Item is no more in the item array, and myAdapter.notifyDataSetInvalidated(); is called, The List Item stays visible , with further code execution some how blocked.
Use an ArrayAdapter to do this. Try something like this instead...
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
public class MyActivity extends Activity{
private ListView myListView;
private ArrayAdapter<Item> myArrayAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
myArrayAdapter = new ArrayAdapter<Item>(this,R.layout.item){
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View returnedView = convertView;
//inflate your view here
if(returnedView == null){
LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
returnedView = inflater.inflate(R.layout.item,null);
}
final Item item = getItem(position);
//set the views
if(returnedView != null){
TextView nameTextView = (TextView) returnedView.findViewById(R.id.nameTextView);
nameTextView.setText(item.getName());
Button deleteButton = (Button) returnedView.findViewById(R.id.deleteButton);
deleteButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
remove(item);
notifyDataSetChanged();
}
});
}
return returnedView;
}
};
myArrayAdapter.add(new Item("Sunday"));
myArrayAdapter.add(new Item("Monday"));
myArrayAdapter.add(new Item("Tuesday"));
myArrayAdapter.add(new Item("Wednesday"));
myArrayAdapter.add(new Item("Thursday"));
myArrayAdapter.add(new Item("Friday"));
myArrayAdapter.add(new Item("Saturday"));
myListView = (ListView) findViewById(R.id.myListView);
myListView.setAdapter(myArrayAdapter);
}
}
public class Item{
private String name;
public Item(String n){
this.name = n;
}
public String getName() {
return name;
}
}
Looking inside the working of ListViews and ListAdapters, I came to know there's a lot of recycling of objects, specifically List View Item Objects which adapters produce. Here are the lessons learnt along with solution to Original problem:
When a ListView has to draw/show a list item, it requests a View from ListAdapter, and some times (NOT ALWAYS) also provides an old View object to reuse. This reuse of objects is there so as to increase performance,there's an in built Re-Cycler in ListView to do this, why inflate new layouts for each new list item, when there are already some whose properties can be modified so that they look like the new view item. Until this point , its OK for adapter to change some text on old views and give them back, or create new ones if no recycled views are available, or even discard recyclable views and always create new one.
However, If your List Item's state is more than just some text in a TextView , that is, another object is registered as an onClickListener for your list item, or your list item has a reference to some object somewhere and vice-verca; it is NOT OK for adapter to just change appearance of the reusable Views or simply discard them. Adapter has to update entire state of a reusable item. that includes de-registering old event listeners, re-registering new ones and updating all reference to external objects that may be there.
Changed getView() method for adapter to:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Item item = getItem(position);
Log.d("My_Test","Adapter : View for Item: " + item.getName() +"is requested." );
if(convertView != null)
{
(convertView.findViewById(R.id.deleteButton))
.setOnClickListener(item);
((TextView)convertView.findViewById(R.id.nameTextView))
.setText(item.getName());
return convertView;
}
else
{
return item.getView();
}
}
NOTE: While always creating new items in this case does not cause any errors, the ListView fails to detect changes and redraw. Making use of recycled items seems to solve this.