This problem has been stuck for a while in my head.
What I need to do:
Show a listview with alternating resources for the items in the listView.
What is my problem:
So far I can alternate resources and show no data, or show the data but not alternate resources. The first item works well every time, but not form there onwards. I think I'm very close but I just can't think what is going wrong...
What have I done:
I have used a custom simple cursor adapter.
Where is the code:
public class DialogCursor extends SimpleCursorAdapter {
private LinearLayout wrapper;
private TextView burbuja;
public DialogCursor(Context context, int layout, Cursor c, String[] from,
int[] to, int flags) {
super(context, layout, c, from, to, flags);
// TODO Auto-generated constructor stub
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if (row == null) {
Context context = parent.getContext();
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row = inflater.inflate(R.layout.dialogo_row, parent, false);
}
burbuja = (TextView) row.findViewById(R.id.idiomaselec);
wrapper = (LinearLayout) row.findViewById(R.id.wrapper);
//get reference to the row
View view = super.getView(position, convertView, parent);
Log.d("Dialogo","enters getview");
Log.d("Dialogo",Integer.toString(position));
//check for odd or even to set alternate colors to the row background
if(position % 2 == 0){
Log.d("Dialogo","Even");
burbuja.setBackgroundResource(R.drawable.bubble_green);
wrapper.setGravity(Gravity.LEFT);
}
else {
Log.d("Dialogo","not even");
burbuja.setBackgroundResource(R.drawable.bubble_yellow);
wrapper.setGravity(Gravity.RIGHT);
}
return row;
}
}
The cursor adapter is called from this other class (just showing relevant part)
String[] from = new String[] { DialogoTable.TABLE_DIALOGO + "." + columna };
// Fields on the UI to which we map
final int[] to = new int[] { R.id.idiomaselec};
Log.d("Dialogo","entra en fillData2");
getLoaderManager().initLoader(0, null, this);
if (bot) {
Log.d("Dialogo","entra en fillData2.5");
getLoaderManager().restartLoader(0, null, this);
}
adapter2 = new DialogCursor(this, R.layout.dialogo_row, null, from, to, 0);
setListAdapter(adapter2);
And the output:
If I return row (last line of code)
I get the background resources in the right place but with no data
If I return view (last line of code)
I get the data but only the first item has the right background resources.
One last note:
I have followed this example
http://adilsoomro.blogspot.com/2012/12/android-listview-with-speech-bubble.html
but I dont want to create a class message since I wnat the data from my DB.
Thank you for your help :)
In a similar case I was able to have a custom cursorAdapter alternate resources based on the cursor position. I put the following code in my bindView where entryView is the passed in view. I am overriding getView at all.
if(cursor.getPosition()%2 == 1){
entryView.findViewById(R.id.title_relative).setBackgroundColor(getResources().getColor(R.color.orange));
}else{
entryView.findViewById(R.id.title_relative).setBackgroundColor(getResources().getColor(R.color.blue));
}
Related
I have a list of orders in SQLite which vary in status: assigned, loaded, delivered. I'd like for each of those orders, when displayed in the list, to have a different colored background. So far, I haven't found a good way to do this.
I've found plenty of discussions on how to change the background color of list items based on the position, but none based on data content. I've also found lots of discussions on how to change the color that's used to highlight an item that is selected. These don't help me.
The only methods I come up with for solving my problem involve running through the entire list, after it's been created by the adapter, and setting the background on each item. It's kludgy and wasteful. I'm hoping there's a more efficient method that would let the background be changed in the adapter as the list is being created from the cursor.
I'm sure there's a better way. I'm just too new to Android to know it.
I really appreciate the responses so far. I'm doing my best to incorporate them, but I'm still not having success. Here's what I've just tried, based on the answers I've gotten and the research I've done.
public class OrderListAdapter extends SimpleCursorAdapter {
private static final String TAG = "SimpleCursorAdapter";
Context _context = null;
int layoutResourceId = 0;
public OrderListAdapter(Context context, int layout, Cursor c,
String[] from, int[] to, int flags) {
super(context, layout, c, from, to, flags);
_context = context;
}
public View getView(int position, View convertView, ViewGroup parent) {
View view = super.getView(position, convertView, parent);
String tag = TAG + ".getView()";
Log.d(tag,"in getView()");
if (view == null) {
LayoutInflater inflater = ((Activity)_context).getLayoutInflater();
view = inflater.inflate(R.layout.fragment_order_list_row, null);
}
setRowColor(view);
return view;
}
private void setRowColor(View view) {
String tag = TAG + ".setRowColor()";
Cursor cursor = getCursor();
int col = cursor
.getColumnIndex(DBContract.DeliveryOrderTable.ENROUTE_FLAG);
String enroute_flag = cursor.getString(col);
Log.d(tag, "enroute_flag = [" + enroute_flag + "]");
col = cursor
.getColumnIndex(DBContract.DeliveryOrderTable.DELIVERED_DATETIME);
String deliveredDateStr = cursor.getString(col);
Log.d(tag, "deliveredDateStr = [" + deliveredDateStr + "]");
int bgColorId = 0;
if (!deliveredDateStr.equals("")) {
bgColorId = R.color.bg_status_delivered_color;
Log.d(tag, "Setting to delivered color");
} else if (enroute_flag.startsWith("T") || enroute_flag.startsWith("Y")) {
Log.d(tag, "Setting to enroute color");
bgColorId = R.color.bg_status_enroute_color;
} else {
Log.d(tag, "Setting to assigned color");
bgColorId = R.color.bg_status_assigned_color;
}
view.setBackgroundColor(_context.getResources().getColor(bgColorId));
}
}
Unfortunately, this doesn't work. If I don't make the call to super.getView(), I wind up with no data in the fields, obviously, since I don't explicitly make the transfers, but I figured I could just modify the returned view.
My traces show me that I am reading the data, but the background color is not changing.
It appears that the view I'm trying to change is the LinearLayout, but changing the background color doesn't seem to work.
Got it! Make sure to make the backgrounds of all the child views transparent.
if you are using any custom adapter for listview then, you will have a method getView(), in that just call a method before returning, and pass the view(which is returning) and data depending on you want to change the color of the row.
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View view = convertView;
if (view == null) {
LayoutInflater vi = (LayoutInflater) _c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
view = vi.inflate(R.layout.list_item_var, null);
}
TextView varView = (TextView) view.findViewById(R.id.var);
TextView valueView = (TextView) view.findViewById(R.id.value);
VarDetails var = _data.get(position);
setRowColor(view, var.getVar());
varView.setText(var.var);
valueView.setText("Value: " + var.value);
return view;
}
private void setRowColor(View view, String var) {
if("assigned".equalsIgnoreCase(var)){
view.setBackgroundColor(Color.rgb(255,0,0));
}else if("loaded".equalsIgnoreCase(var)){
view.setBackgroundColor(Color.rgb(0,255,0));
}else if("delivered".equalsIgnoreCase(var)){
view.setBackgroundColor(Color.rgb(0,0,255));
}
}
please change in method depending on you data type.
I would say try to extend CursorAdapter for binding your database with a ListView. And then you can override ListView.dispatchDraw() to customize your Paint object.
Or maybe it's helpful to check this: Customizing Android ListView Items with Custom ArrayAdapter
It uses different images based on weather status. Porting to your problem, you may use 9-patch or programmatically created Drawables as backgrounds, rather than changing stuff in Paint.
I'm trying to display information about an object, concretely a customer. I mean name, last name, telephone number, adress, .... that is the result by a query on my database.
I want to display this on a ListView, so better if my activity inherit of a ListActivity because ListView is self-contained by default.
I do the next: I have a class called Customer that saves all information by the query (Cursor) that only return one record(customer) and I don't know how to pass this object ,I don't find the most appropriate adapter.
Can I do this by this way or I must convert this object into an ArrayList that contains all information and, in fact, use ArrayAdapter for my adapter?
Anyone knows how to do this??
Thanks.
Once you have your cursor queried you should do something like this
String[] from = new String[]{"FirstName"};
int[] to = new int[]{R.id.row};
SimpleCursorAdapter sca = new SimpleCursorAdapter(this, R.layout.row_item, cur, from, to);
setListAdapter(sca);
check here the documentation for SimpleCursorAdapter.
If you need to do something more complicated with your views you should implement your own custom Cursor adapter:
public class ExampleCursorAdapter extends CursorAdapter {
public ExampleCursorAdapter(Context context, Cursor c) {
super(context, c);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
TextView summary = (TextView)view.findViewById(R.id.summary);
summary.setText(cursor.getString(
cursor.getColumnIndex(ExampleDB.KEY_EXAMPLE_SUMMARY)));
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(R.layout.item, parent, false);
bindView(v, context, cursor);
return v;
}
}
And finally, I can find the solution it would be. I'm going to tell you how I did.
I wanted to display details customer by this way:
row 1-> Name: name_value
row 2-> Last Name: lastname_value
row 3-> Phone: phone_value
...
N. n row -> n_field: n_value
Fields that describe what value is going to show, i mean "Name:", "Last Name:", "Phone:", ...
I put all this values on a string-array . I captured that by a String [] .
Values, show proper value next the correct field. I put all this inside a ArrayList
and next only have to write the proper adapter:
This Activity inherit ListActivity, ListView by default.
SimpleCursorAdapter is not proper because we have to pass the cursor as argument of this adapter but my Cursor only returns only one result, only one record .
And CursorAdapter is refused by the same reason. What can I do next? ArrayAdapter :
public class DetalleClienteAdapter extends ArrayAdapter<String>
{
private String[] campos_cliente;
private ArrayList<String> detalles_cliente;
public DetalleClienteAdapter(Context contexto, int layout, ArrayList<String> detalles)
{
super(contexto,layout,detalles);
//mInflater = LayoutInflater.from(contexto);
// LOS VALORES DE LOS CAMPOS:
detalles_cliente = detalles;
// CAMPOS DE DETALLES: nombre, apellidos, etc...
android.content.res.Resources res = getResources();
campos_cliente = res.getStringArray(R.array.array_detalle_cliente);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
// TODO Auto-generated method stub
//return super.getView(position, convertView, parent);
LayoutInflater inflater= getLayoutInflater();
View fila = inflater.inflate(R.layout.detalles_cliente, parent, false);
TextView campo =(TextView)fila.findViewById(R.id.detalles_campo);
TextView valor =(TextView)fila.findViewById(R.id.detalles_valor);
campo.setText(campos_cliente[position]);
valor.setText(detalles_cliente.get(position));
return fila;
}
}
Now I'm going to write a more efficient adapter, with ViewHolder. What is your opinion about that?
Thanks.
I have some extended cursor adapter, in witch I call super with the context and the resource layout for the item in the list, something like this.
call to super in my adapter:
super(activity, viewResource, c, false);
creation of my adapter:
new MyCursorAdapter(this, null, R.layout.my_list_item, null);
What I want to achieve is something like my stupid mock up made in paint.
Put into words I want to have different kinds of layout for the items, for example I want all even items to have layout1 and all odd to have the layout2. So far I am able to give only one layout in this case R.layout.my_list_item. Is it possible to dynamically change the layout ?
Is it possible to construct the adapter to have items with different layout ? My goal is to dynamically chose the layout of the item. I do not want to have just one layout for all of the items I want to have foe example two...
Thanks
Yes, you're going to have to do two things though. First, override the getItemViewType() method in your adapter so that you can be sure your bindView() only get's views that appropriate for a particular position in the list, like so:
public int getItemViewType(int position){
if(correspondsToViewType1(position)){
return VIEW_TYPE_1;
}
else(correspondsToViewType2(position)){
return VIEW_TYPE_2;
}
//and so on and so forth.
}
Once you do that, just have a simple test in your bindView() that checks to see what type of view it should have recieved and setup things accordingly like so:
public void bindView(View view, Context context, Cursor cursor){
if(correspondsToViewType1(cursor)){
//Now we know view is of a particular type and we can do the
//setup for it
}
else if(correspondsToViewType2(cursor){
//Now we know view is of a different type and we can do the
//setup for it
}
}
Note that you're going to have to have to different methods for correpondsToViewType, one that takes a cursor and one that takes an int (for a position). The implementation for these will vary depending on what you want to do.
Note that doing things this way will allow you to reuse potentially recycled views. If you don't do this, your app is going to take a huge performance hit. Scrolling will be super choppy.
I'm guessing your extending SimpleCursorAdapter from the name of your custom adapter. You will want to override the function getView in your adapter and depending on the object in the list inflate a different layout and return that view.
EX:
#Override
public View getView (int position, View convertView, ViewGroup parent)
{
Object myObject = myList.get(position);
if(convertView == null)
{
if( something to determine layout )
convertView = inflater.inflate(Layout File);
else
convertView = inflater.inflate(Some Other Layout File);
}
//Set up the view here, such as setting textview text and such
return convertView;
}
This is just an example and is somewhat sudo code so it will need some adjustments for your specific situation.
Just override the newView Method:
public class MyCursorAdapter extends CursorAdapter {
private final LayoutInflater inflater;
private ContentType type;
public MyCursorAdapter (Context context, Cursor c) {
super(context, c);
inflater = LayoutInflater.from(context);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
if( cursor.getString(cursor.getColumnIndex("type")).equals("type1") ) {
// get elements for type1
} else {
// get elements for type1
}
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
if( cursor.getString(cursor.getColumnIndex("type")).equals("type1") ) {
final View view = inflater.inflate(R.layout.item_type1, parent, false);
} else {
final View view = inflater.inflate(R.layout.item_type2, parent, false);
}
return view;
}
Hi I want to achieve the following result in my ListActivity:
First item, descritpion
Second item, description
...
where Name and description come from
Cursor c = db.rawQuery(...);
I used a SimpleCursorAdapter and I had no problems. Then I decided to make some modification to data that I have in cursor before displaying it. I wanted to have a internal counter that would increment every time cursor adapter dislays a new view. That's why I decided to implement my own CustomCursorAdapter extending SimpleCursorAdapter:
public class TrainingsListCursorAdapter extends SimpleCursorAdapter{
private Context context;
private int layout;
int list_item_order_number = 1;
public TrainingsListCursorAdapter(Context context, int layout, Cursor c,
String[] from, int[] to) {
super(context, layout, c, from, to);
this.context = context;
this.layout = layout;
}//contructor
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent){
Cursor c = getCursor();
final LayoutInflater inflater = LayoutInflater.from(context);
View v = inflater.inflate(layout, parent, false);
String durationCol = c.getString(c.getColumnIndex("duration"));
if(durationCol != null){
durationCol = minutesForSeconds(durationCol);
}
TextView textViewDuration = (TextView) v.findViewById(R.id.duration);
TextView order_number = (TextView) v.findViewById(R.id.list_item_num);
if(order_number != null){
order_number.setText(String.valueOf(list_item_order_number));
}
if(textViewDuration != null){
textViewDuration.setText(durationCol);
}
list_item_order_number++;
return v;
}
I have a member variable _list_item_order_number_ that is incremented by one every time a view/row is drawn (by calling newView() method). This number is displayed in every view/list item and it corresponds to list item order...
The problem I have is that when I'm rotating the screen this number doesn't correspond to list items anymore. For example my 3rd item has value 1 and so on...
I guess this has something to do with the order Android is drawing the views. If I scroll the list to, let's say, the middle and then rotate, these numbers are all mixed like this:
First Item
Second item
Third item
I feel a bit stupid as i can't find the answer to this question, which makes me think i'm actually asking the wrong question. However, here goes...
I have a list view, and a listviewitem defined in xml, with a couple of fields, nothing special. All set to visible.
Then I bind to my ListView using a custom ArrayAdapter, and want to hide one of my text views, on row 5. However, it seems to be hiding my TextView on item 0 and item 5. Which is a bit odd? I've simplified the code, to reproduce the problem and hopefully someone will be able to help me...
My Adapter
public class MenuScreenAdapter extends ArrayAdapter<String>
{
private List<String> _items;
private Context _context;
public MenuScreenAdapter(Context context, List<String> items)
{
super(context, R.layout.list_menu_item, items);
_context = context;
_items = items;
}
private MenuScreenAdapter(Context context, int textViewResourceId)
{
super(context, textViewResourceId);
}
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
if (v == null)
{
LayoutInflater vi = (LayoutInflater) _context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = vi.inflate(R.layout.list_menu_item, null);
}
String o = _items.get(position);
if (o != null)
{
TextView tt = (TextView) v.findViewById(R.id.list_menu_item_name);
if (tt != null)
tt.setText(o);
if (position == 5)
tt.setVisibility(View.GONE);
}
return v;
}
}
My Binding Code
// Load everything up that we need
List<String> items = new ArrayList<String>();
items.add("One");
items.add("Two");
items.add("Three");
items.add("Four");
items.add("Five");
items.add("Six");
items.add("Seven");
items.add("Eight");
items.add("Nine");
items.add("Ten");
// Get the ListView, and set it's adapter. The HomeScreenAdapter
// takes care of the rest
ListView homeScreenListView = (ListView) _mainActivity.findViewById(R.id.view_home_list);
homeScreenListView.setOnItemClickListener(ItemSelected);
homeScreenListView.setAdapter(new MenuScreenAdapter(_mainActivity.getBaseContext(), items));
Thanks in advance!
Since row views are reused by ArrayAdapter, once the View.GONE is set, it will cary on to the next row, where this view will be reused. In your case, you set View.GONE to textview in the fifth row, moved list a little and arrayadapter decided to reuse your fifth row layout to display the first row, since no changes were done to it, the textView still remains hidden.
Just do the:
if (position == 5) {
tt.setVisibility(View.GONE);
} else {
tt.setVisibility(View.VISIBLE);
}
P.S. If you still haven't, watch a presentation about ListViews from google. Tons of usefult info there. ListViews