My vision is to create a color coded listView, where I want to have a slim colored bar in each item's layout. This bar should be a certain color based on an int I pass to the activity. How do I create such a bar(essentially a filled rectangle) and set its color. Currently, I am using a custom layout for my list and using a SimpleAdapter with an ArrayList.
I know I will have to use
if (integerForColor == someNumber)
//set the color of the shape, bar
I am sure this is a simple thing that I simply cannot think of at the moment, or must be missing a fundamental. Thank you in advance for your effort.
EDIT 1:
Trying to decode some of the answers that were here, I realized that you guys need my code on how I am building my list:
public class AddScreen extends Activity implements OnClickListener,
OnItemClickListener, OnItemLongClickListener {
SimpleAdapter adapter;
List<HashMap<String, String>> painItems = new ArrayList<HashMap<String, String>>();
ListView listthings;
public void onCreate(Bundle savedInstanceState) {
from = new String[] { "row_1", "row_2" };
to = new int[] { R.id.row1, R.id.row2 };
adapter = new SimpleAdapter(this, painItems, R.layout.mylistlayout,
from, to);
listthings.setAdapter(adapter);
}
I have added the getView() Method:
public View getView (int position, View convertView, ViewGroup parent){
convertView.setBackgroundColor(R.drawable.red);
return convertView;
//I have no inflater as of now...
}
EDIT 2: As per #huntsfromshadow 's suggestion, I have created a new activity and overridden the getView() method there.
//Imports
public class Adapter extends SimpleAdapter{
public Adapter(Context context, List<? extends Map<String, ?>> data,
int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
// TODO Auto-generated constructor stub
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
View row = convertView;
row.setBackgroundColor(0xFF0000FF);
return row;
}
}
Unfortunately, it still does not work.
Assuming you have everything else setup,
it's probably easiest to define your colored backgrounds in xml like so (red.xml):
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="#DD0000"
android:endColor="#EE0000"
android:angle="270" />
</shape>
And then use it like this:
context.getResources().getColor(R.drawable.red).
You'll have to pass in and save a context object from the parent Activity.
Look at the answer I gave on how to create a ListView (yes it's parsing data from JSON - ignore that) here
Create a custom adapter and override getView. That is the point where you can do any complicated layout decisions based on values and logic.
A google search for custom adapter will give you a lot of resources.
As #CaspNZ said, use .xml shape files.
In your code extend a 'ListAdapter' (probably either BaseAdapter or ArrayAdapter). Override the getView function like this:
public View getView (int position, View convertView, ViewGroup parent){
if (convertView == null){
//assuming you already have inflater initilized somewhere
convertView = inflater.inflate(you_layout_file.xml, parent, false);
}
//some logic to determine what color the background should be
convertView.setBackgroundDrawable(R.drawable.the_file_you_want_to_use.xml)
}
I hacked together something very similar to color code the text of each item differently. You could adapt it pretty easily to change the color of a bar instead.
Since each element in the list has a different color, you have to remember which color is associated with which location in the array. I just used a simple integer array as a member of my ColorItemsAdapter.
private class ColorItemsAdapter extends ArrayAdapter<String>
{
/*
* For each line to be added to the log, the text is stored in "items"
* and the text color is stored in "colors"
*/
ArrayList<String> items = new ArrayList<String>();
ArrayList<Integer> colors = new ArrayList<Integer>();
int length = 0;
public ColorItemsAdapter(Context context, int textViewResourceId, List<String> stringObj)
{
super(context, textViewResourceId, stringObj);
if(stringObj != null)
{
length = stringObj.size();
}
}
/**
* Adds an item to the adapter, and specifies what color the item should be
* #param item
* The text to be added
* #param color
* The color of the text to be added
*/
public void add(String item, Integer color)
{
items.add(item);
colors.add(color);
length++;
super.add(item);
}
/**
* Adds an item to the adapter at position <i>index</i>
* #param item
* The text to be inserted
* #param index
* The index at which to insert the text
* #param color
* The color of the text to be inserted
*/
public void insert(String item, int index, Integer color)
{
items.add(index, item);
colors.add(index, color);
length++;
super.insert(item, index);
}
/*
* Changes the color of the TextView appropriately before calling the super
*/
public View getView(int position, View convertView, ViewGroup parent)
{
TextView v = (TextView) convertView;
if (v == null)
{
LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
v = (TextView) vi.inflate(R.layout.log_list_item, null);
}
/*
* Here I am setting the text color
* but you could set some other attribute instead.
*/
v.setTextColor(colors.get(position));
return super.getView(position, v, parent);
}
}
Then, to add an item to your list (in this case a red one), you could just use
myColorItemsAdapter.insert(message, 0, Color.RED);
Related
I'm using listview custom adapter which with row click i'm changing row color. But when i'm scrolling bot and up again it doesnt have the right position.
It changes color in other rows...
public override View GetView(int position, View convertView, ViewGroup parent)
{
DataViewHolder holder = null;
if (convertView == null)
{
convertView = LayoutInflater.From(mContext).Inflate(Resource.Layout.TableItems, null, false);
holder = new DataViewHolder();
holder.txtDescription = convertView.FindViewById<TextView>(Resource.Id.txtDescription);
holder.txtDescription.Click += delegate
{
holder.txtDescription.SetBackgroundColor(Color.Red);
};
convertView.Tag = holder;
}
else
{
holder = convertView.Tag as DataViewHolder;
}
holder.txtDescription.Text = mitems[position].Description;
return convertView;
}
public class DataViewHolder : Java.Lang.Object
{
public TextView txtDescription { get; set; }
}
It looks like it doesnt keep in memory specific row situation.
Don't change the color in the click handler directly, instead change the data from which the adapter draws from and use that to change the color when GetView is called again.
ListView recycles the views it uses to optimize scrolling, instead it just expects the view to represent the data. If you change a color of one view directly, the view then gets recycled and you'll see "another view" (another part of the data) with a different background color.
So in summary: give each data point a color attribute and use that to set the color of each view in GetView, change the data and notify the adapter about the changes to the data.
Edit
I've never used Xamarin but maybe something like this would work
public override View GetView(int position, View convertView, ViewGroup parent)
{
DataViewHolder holder = null;
if (convertView == null)
{
convertView = LayoutInflater.From(mContext).Inflate(Resource.Layout.TableItems, null, false);
holder = new DataViewHolder();
holder.txtDescription = convertView.FindViewById<TextView>(Resource.Id.txtDescription);
holder.txtDescription.Click += delegate
{
// instead of setting the color directly here, just modify the data
(holder.txtDescription.Tag as ItemType).ItemColor = Color.Red
notifyDataSetChanged();
};
convertView.Tag = holder;
}
else
{
holder = convertView.Tag as DataViewHolder;
}
holder.txtDescription.Text = mitems[position].Description;
holder.txtDescription.Tag = mitems[position]; // this so that the click handler knows which item to modify
holder.txtDescription.SetBackgroundColor(mitems[position].ItemColor);
return convertView;
}
public class DataViewHolder : Java.Lang.Object
{
public TextView txtDescription { get; set; }
}
ListView will reuse the item layout, you can use List and View.Tag to avoid the problem caused by reusing.
I have posted my demo on github.
I am using Yuri Kanivets' WheelView in my project.
For translation purposes, I have made a custom TextView with custom Font. The WheelView uses TextView to display text. I would like to use the custom Textview in Wheelview so that it displays the text in custom font. How do I do that?
I don't want to change the original source of wheelview, in case I have to use it for other apps.
Code of the wheelview adapter:
/**
* Adapter for string based wheel. Highlights the current value.
*/
private class DateArrayAdapter extends ArrayWheelAdapter<String> {
// Index of current item
int currentItem;
// Index of item to be highlighted
int currentValue;
/**
* Constructor
*/
public DateArrayAdapter(Context context, String[] items, int current) {
super(context, items);
this.currentValue = current;
setTextSize(16);
}
#Override
protected void configureTextView(TextView view) {
super.configureTextView(view);
if (currentItem == currentValue) {
view.setTextColor(getResources().getColor(R.color.holo_blue));
}
view.setTypeface(Typeface.SANS_SERIF);
view.setTextSize(18);
}
#Override
public View getItem(int index, View cachedView, ViewGroup parent) {
currentItem = index;
return super.getItem(index, cachedView, parent);
}
}
Create a layout xml with your own view. Let us say it is layout_item.xml. Set the id of your view (the custom textview) to text using android:id="#+id/text"
Use this on your adapter:
adapter.setItemResource(R.layout.layout_item);
adapter.setItemTextResource(R.id.text);
I guess now it should work
I want to add a footer view for GridView.
I find in the documentation GridView has 2 inherited addView(View child) method.
From class android.widgetAdapterView
void addView(View child)
This method is not supported and throws an UnsupportedOperationException when called.
and
From class android.view.ViewGroup
void addView(View child)
Adds a child view.
It seems I should use the latter. But how can I call this particular inherited method?
You don't. It overwrites the original with a UnsupportedOperationException because it's.. well.. not supported.
You should be editing the adapter instead. Depending on your implementation, this will look different. But, you just have to add more data to the adapter, and call .notifyDataSetChanged() on the adapter, and your GridView will add the view by itself.
A footer view should either be a separate View after your GridView, or you will have to maintain its position in the adapter's list to always be last whenever you add new items.
Providing an example of Erics solution, the adapter could maintain two extra members for tracking the
position of the "footer", and its event handler:
class ImageViewGridAdapter : ArrayAdapter<int>
{
private readonly List<int> images;
public int EventHandlerPosition { get; set; }
public EventHandler AddNewImageEventHandler { get; set; }
public ImageViewGridAdapter(Context context, int textViewResourceId, List<int> images)
: base(context, textViewResourceId, images)
{
this.images = images;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
ImageView v = (ImageView)convertView;
if (v == null)
{
LayoutInflater li = (LayoutInflater)this.Context.GetSystemService(Context.LayoutInflaterService);
v = (ImageView)li.Inflate(Resource.Layout.GridItem_Image, null);
// ** Need to assign event handler in here, since GetView
// is called an arbitrary # of times, and the += incrementor
// will result in multiple event fires
// Technique 1 - More flexisble, more maintenance ////////////////////
if (position == EventHandlerPosition)
v.Click += AddNewImageEventHandler;
// Technique 2 - less flexible, less maintenance /////////////////////
if (position == images.Count)
v.Click += AddNewImageEventHandler;
}
if (images[position] != null)
{
v.SetBackgroundResource(images[position]);
}
return v;
}
}
Then, before assigning the adapter to the grid view, just assign those values (position doesn't have to be at end, but for a footer it should be):
List<int> images = new List<int> {
Resource.Drawable.image1, Resource.Drawable.image2, Resource.Drawable.image_footer
};
ImageViewGridAdapter recordAttachmentsAdapter = new ImageViewGridAdapter(Activity, 0, images);
recordAttachmentsAdapter.EventHandlerPosition = images.Count;
recordAttachmentsAdapter.AddNewImageEventHandler += NewAttachmentClickHandler;
_recordAttachmentsGrid.Adapter = recordAttachmentsAdapter;
I have a ViewPager within a Fragment in my application. Within the ViewPager I have 6 list views which pull their data from a central data source (a Schedule, which each list view then pulls Lists of days from). Now I'm trying to update this schedule object by changing the schedule that is stored in my PagerAdapter class, and then updating the ArrayAdapter that serves as the adapter to all the list views. When one list view is switched to, a new array adapter is created with the correct data.
Unfortunately this doesn't work at all. I cannot see any data at any point in the application lifecycle. So I'm assuming I'm doing something fundamentally wrong in connecting my data to my ViewPager...
I've been all over the net, read up on all of the fragment stuff, a LOT of the view pager stuff...
Does anyone know the correct way to do this?
Heres the code for my DayAdapter (ViewPagerADapter)
public final class DayAdapter extends PagerAdapter {
//~Data Fields----------------------------------------//
/**
* The list adapter that the current list is being handed to.
*/
private Schedule schedule;
/**
* The current index of the day that is to be displayed.
*/
private int currentIndex;
/**
* ArrayAdpter to be used to connect data to all of the list views.
*/
private ArrayAdapter<Course> adapter;
//~Constructors---------------------------------------//
/**
* Constructor for the DayAdapter implementation of PagerAdapter. Takes in a
* Schedule object which is to be the data backing of the views displayed.
*
* #param theSChedule the Schedule object that is the backing for the ListViews.
*/
public DayAdapter(Schedule theSchedule) {
schedule = theSchedule;
currentIndex = schedule.getTodayIndex();
}
//~Methods--------------------------------------------//
public void setSchedule(Schedule theSchedule) {
schedule = theSchedule;
adapter.clear();
adapter.add(schedule.getDay(currentIndex).getList().get(0));
adapter.notifyDataSetChanged();
}
#Override
public Object instantiateItem(ViewGroup container, int index) {
LayoutInflater inflater = (LayoutInflater) container.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = inflater.inflate(R.layout.schedule_list_view, null);
ListView view = new ListView(container.getContext());
adapter = new ArrayAdapter<Course>(container.getContext(),
R.layout.list_view_child, schedule.getDay(index).getList());
view.setAdapter(adapter);
//TEST CODE!!!! This does not yield any sort data items in the list views!?
adapter.add(new Course("test", "test", "test", "test", "test", "test", "test"));
currentIndex = index;
((ViewPager) container).addView(layout);
return layout;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
ViewPager pager = (ViewPager) container;
View view = (View) object;
pager.removeView(view);
}
/**
* Gets the pageTitle, which is the name of the day that is at position.
*
* #param position the index of the day selected.
* #return the name of the day the index refers to.
*/
#Override
public CharSequence getPageTitle(int position) {
return schedule.getDay(position).getThisDay();
}
#Override
public int getCount() {
return 6;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
}
If that is the full code for the instantiateItem method then it's normal that you don't see any data in the lists. You inflate the layout file named R.layout.schedule_list_view(in which you, most likely, have a ListView) and in the same instantiateItem method you create an independent ListView widget on which you set the data. As the data is binded to the ListView which is not in the layout file, you don't see anything as the original ListView remains empty.
The solution is to either set the data on the ListView that is in the inflated layout(R.layout.schedule_list_view) if you have it there, or add the created ListView with data to the inflate layout(layout(this is a View, so you would want to cast it to a ViewGroup or a subclass of ViewGroup)).
Instead of creating a new adapter with new data, try to change the data in the existing adapter.
Get the collection associated with the adapter.
Modify the collection as you need (should not create new instance of collection. either you can clear the collection and add the new contents or update the collection.)
call the notifyDataSetChanged() of the adapter.
Hope this will work...:)
Blatant n00b question: I have several directories of pictures and wish to display randomly pictures from only one, which I select by a set of radio buttons. How do I specify the directory when using :
//"ha" is ha.png, which I would like to be at drawable/1/ha.png
image.setImageResource(R.drawable.ha);
Can I use setImageResource for this? If so how? If not, what should I use and how?
The object of the exercise is a flashcard program with different lessons (hence the dividing up of images) selectable at the first activity.
You cannot have subfolders under res/drawable, if you are referring to the drawables folder in your apk.
If you are referring to a random folder on your sdcard, then it's fine to use subfolders, but then you cannot use R.drawable.* for that approach to refer to the image.
In that case you need to load the image using
Bitmap bmp = BitmapFactory.decodeFile("/sdcard/drawable/1/ha.png");
which returns a bitmap, which you can use like
image.setImageBitmap(bmp)
see http://developer.android.com/reference/android/widget/ImageView.html#setImageBitmap(android.graphics.Bitmap)
In order to react on changes made to the radion button, see
How to set On click listener on the Radio Button in android
You can use a GridView to show the images from a directory selected from a radio button (as your requirement says). After creating a GridView, associate a adapter to it. Please refer below for a n example adapter :
public class ImageAdapter extends BaseAdapter {
/** LayoutInflater. */
private LayoutInflater mInflater;
/** The i. */
private ImageView i;
/**
* Instantiates a new image adapter.
*
* #param c
* the c
*/
public ImageAdapter(Context c) {
mInflater = LayoutInflater.from(c);
}
public int getCount() {
// scaled pictures will have the list of
// which you have from the directory
return scaledPictures.size();
}
public Bitmap getItem(int position) {
return scaledPictures.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.image, parent, false);
} else {
i = (ImageView) convertView;
}
Bitmap bitmap = getItem(position);
i = (ImageView) convertView.findViewById(R.id.galleryimage);
i.setImageBitmap(bitmap);
bitmap = null;
return i;
}
}