I have a simple cursor adapter set on a list in my application as follows:
private static final String fields[] = {"GenreLabel", "Colour", BaseColumns._ID};
datasource = new SimpleCursorAdapter(this, R.layout.row, data, fields, new int[]{R.id.genreBox, R.id.colourBox});
R.layout.row consists of two TextViews (genreBox and colourBox). Rather than setting the content of the TextView to the value of "Colour" , I would like to set its background colour to that value.
What would I need to do to achieve this?
Check out SimpleCursorAdapter.ViewBinder.
setViewValue is basically your chance to do whatever you wish with the data in your Cursor, including setting the background color of your views.
For example, something like:
SimpleCursorAdapter.ViewBinder binder = new SimpleCursorAdapter.ViewBinder() {
#Override
public boolean setViewValue(View view, Cursor cursor, int columnIndex) {
String name = cursor.getColumnName(columnIndex);
if ("Colour".equals(name)) {
int color = cursor.getInt(columnIndex);
view.setBackgroundColor(color);
return true;
}
return false;
}
}
datasource.setViewBinder(binder);
Update - if you're using a custom adapter (extending CursorAdaptor) then the code doesn't change a whole lot. You'd be overriding getView and bindView:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView != null) {
return convertView;
}
/* context is the outer activity or a context saved in the constructor */
return LayoutInflater.from(context).inflate(R.id.my_row);
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
int color = cursor.getInt(cursor.getColumnIndex("Colour"));
view.setBackgroundColor(color);
String label = cursor.getString(cursor.getColumnIndex("GenreLabel"));
TextView text = (TextView) findViewById(R.id.genre_label);
text.setText(label);
}
You're doing a bit more manually, but it's more or less the same idea. Note that in all of these examples you might save on performance by caching the column indices instead of looking them up via strings.
What you're looking for requires a custom cursor adapter. You can subclass SimpleCursorAdapter. This basically give access to the view as its created (although you'll be creating it yourself).
See this blog post on custom CursorAdapters for a complete example. Particularly, I think you'll need to override bindView.
Related
I need my application to apply a strike through to the contents of each item in my ListView based on whether or not the content of each item is marked as checked off or not in my SQLite database. Right now I obtain the text for each item in the list view from a database table, store it in a List, pass that into a ArrayAdapter, and then use that to set my ListView adapter. This is my code:
private ListView taskList = (ListView) view.findViewById(R.id.task_list);
// Get text for each TextView from database
db.getWritableDatabase();
tasks = db.readTasks((int) listId);
// Set up adapter for ListView
adapter = new ArrayAdapter<String>(
view.getContext(),
android.R.layout.simple_expandable_list_item_1,
tasks);
taskList.setAdapter(adapter);
My thought was to iterate through each item in the ListView after it has been set and check to see if the corresponding record in the database had it marked as checked or not, and then act accordingly on the item. I don't know how to iterate over the contents of a ListView however, nor am I certain this is the best way to do this. Does anyone know how to iterate over the contents of a ListView or is there an entirely better way to do this?
Firstly define a Task object with useful properties:
class Task {
public boolean isComplete() {...};
#override String toString() {...};
}
Then make your db method return a list of such objects:
ArrayList<Task> tasks = db.readTasks((int) listId);
Finally, subclass the ArrayAdapter:
private static class TaskAdapter extends ArrayAdapter<Task> {
private ArrayList<Task> mTasks;
TaskAdapter (Context c, ArrayList<Task> list) {
super(c, android.R.layout.simple_spinner_item, list);
mTasks = list;
}
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View v = super.getView(position, convertView, parent);
TextView txt = v.findViewById(android.R.id.text1);
int flags = txt.getPaintFlags();
if (mList.get(position).isComplete()) {
flags = flags | Paint.STRIKE_THRU_TEXT_FLAG;
} else {
flags = flags & ~Paint.STRIKE_THRU_TEXT_FLAG;
}
txt.setPaintFlags(flags);
}
}
You can also do a lot more when you subclass the adapter: eg. create your own view layout (eg. if you wanted a 'tick' graphic instead of a strikethough).
Google for examples of this kind of thing. There are many.
I have a list which lists EXPENSES and INCOMES. I want ListViewer to automatically change background to either green for Incomes or red for Expenses, would this be possible?
final DbCon database = new DbCon(this);
final ListView Viewer = (ListView)findViewById(R.id.historyViewer);
//date1, time1, desc1, val1, cat1, type1
final SimpleAdapter La = new SimpleAdapter(this, database.GetMonths(), R.layout.mviewer, new String[] {"month"}, new int[] {R.id.month_name});
Viewer.setAdapter(La);
Without seeing any code, its hard to give any "real" help but it is possible. You can change the background color with something like
rowView.setBackgroundColor(Color.GREEN);
assuming rowView is your current View in the List. You can also change styles and themes depending on what you want.
Yes just put the logic in the getView method of your Adapter, e.g.:
public View getView(int position, View convertView, ViewGroup parent) {
View myView;
if(convertView == null) {
myView = new MyView();
} else {
myView = (MyView) convertView;
}
if(myList.get(position).isExpense) {
myView.setBackgroundColor(Color.RED);
} else {
myView.setBackgroundColor(Color.GREEN);
}
return myView;
}
edit:
Yes, I see that you are using a SimpleAdapter. That will not be sufficient to do what you want. You need to create your own adapter by subclassing BaseAdapter (or something similar) and overriding the necessary methods, e.g. getView as I showed above.
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 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;
}
I'm trying to create a listview generated from information out of my SQLite DB. I currently have the code set up to just display each row accordingly and populate 3 textviews with data. I would like to be able to use some sort of If statement to check to see if one of values is "Room1" and then set the background of that row to a certain color.
This way each row in the list will have a different background based on what "room" the data is for.
I have tried extending SimpleCursorAdapter but I am a bit lost on how to get what I want out of it.
Here is what I have currently for the SimpleCursorAdapter:
Cursor notesCursor = myDbHelper.fetchDayPanelNotes(type, day);
startManagingCursor(notesCursor);
String[] from = new String[]{DataBaseHelper.KEY_NAME, DataBaseHelper.KEY_ROOM, DataBaseHelper.KEY_TIME};
int[] to = new int[]{R.id.text1, R.id.text2, R.id.text3};
SimpleCursorAdapter notes = new SimpleCursorAdapter(this, R.layout.main_row, notesCursor, from, to);
setListAdapter(notes);
You should override the getView method of your adapter:
SimpleCursorAdapter notes = new SimpleCursorAdapter(
this, R.layout.main_row, notesCursor, from, to)
{
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
final View row = super.getView(position, convertView, parent);
if (position % 2 == 0)
row.setBackgroundResource(android.R.color.darker_gray);
else
row.setBackgroundResource(android.R.color.background_light);
return row;
}
};
and set the background of the row based on the position (is it odd or even).
This should do it.
Thanks to this post, I had exact same problem but after going through this, problem solved as shown below,
SimpleCursorAdapter notes = new SimpleCursorAdapter(this, R.layout.notes_row, mNotesCursor, from, to)
{
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
cursor.moveToPosition(position);
String s = cursor.getString(mursor.getColumnIndex("roomnumber"));
final View row = super.getView(position, convertView, parent);
if (s.equalsIgnoreCase("room1"))
row.setBackgroundColor(Color.parseColor("#480000"));
else if (s.equalsIgnoreCase("room2"))
row.setBackgroundColor(Color.parseColor("#000040"));
else
row.setBackgroundColor(Color.parseColor("#004000"));
return row;
}
};
setListAdapter(notes);
I implement SimpleCursorAdapter.ViewBinder on my Activity then override setViewValue...
public class MyActivity extends Activity
implements SimpleCursorAdapter.ViewBinder {
SimpleCursorAdapter myAdapter = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState)
// Initialize myAdapter
myAdapter.setViewBinder(this);
}
#Override
public boolean setViewValue(View view, Cursor cursor, int column) {
// Put code to process row data here
}
}
I would recommend extending BaseAdapter, populating your list data manually and keeping a local copy of that data in your adapter class. Then when you implement the getView(...) method you can use the position or ID of the View you're generating to fetch the corresponding data to check against "Room1," and alter the View you return based on that comparison.
The answer by rekaszeru has exactly the right idea, but it sounds like you'll need more information than the position and ID, which is why I recommend going a level lower and implementing BaseAdapter directly.