I checked on Stackoverflow, some people asked about changing the image, but non of the solutions helped me.
I have an ImageButton in listItem of list view. I want to change the image of ImageButton on clicking the ImageButton. It is displaying the toast but not changing the image.
Any help would be appreciated. Thanks in advance.
Below is my code...
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.fragment_contacts_item_list, null);
contactName = (TextView)rowView.findViewById(R.id.name_contact);
contactNumber = (TextView)rowView.findViewById(R.id.number_contact);
contactPic = (ImageView)rowView.findViewById(R.id.quickContactBadge_contact);
contactStatusButton = (ImageButton)rowView.findViewById(R.id.configuration_button);
convertView = rowView;
contactArray = new ArrayList<ContactModel>();
int status = contactList.get(position).getButtonStatus();
if(status == 0)
contactStatusButton.setImageResource(R.drawable.grey);
else if(status == 1) //toggle button is on
contactStatusButton.setImageResource(R.drawable.green);
else
contactStatusButton.setImageResource(R.drawable.red);
contactStatusButton.setVisibility(View.VISIBLE);
contactStatusButton.setBackgroundColor(Color.TRANSPARENT);
contactStatusButton.setTag(position);
contactStatusButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View view) {
int position = (Integer) view.getTag();
model.setName(contactList.get(position).getName());
model.setNumber(contactList.get(position).getNumber());
model.setPhoto(contactList.get(position).getPhoto());
ContactAdapter.contactArray.add(contactList.get(position));
contactStatusButton.setImageResource(R.drawable.green);
Toast.makeText(context, "contact Number: " + contactList.get(position).getNumber(), Toast.LENGTH_SHORT).show();
}
});
model = new ContactModel(contactList.get(position).getName(), contactList.get(position).getNumber(), 0);
model.setName(contactList.get(position).getName());
model.setNumber(contactList.get(position).getNumber());
model.setButtonStatus(contactList.get(position).getButtonStatus());
Bitmap pic = Utilities.getPhoto(contactList.get(position).getPhoto());
model.setPhoto(contactList.get(position).getPhoto());
contactName.setText(contactList.get(position).getName());
contactNumber.setText(contactList.get(position).getNumber());
contactPic.setImageBitmap(pic);
return convertView;
}
Your views all seem to be defined as members of the enclosing class. That means they are constantly getting reassigned to different views whenever getView is called.
Instead, you should assign your views locally to the method, and make them final so that they can be referenced inside your click handler:
final View rowView = inflater.inflate(R.layout.fragment_contacts_item_list, null);
final TextView contactName = (TextView)rowView.findViewById(R.id.name_contact);
final TextView contactNumber = (TextView)rowView.findViewById(R.id.number_contact);
final ImageView contactPic = (ImageView)rowView.findViewById(R.id.quickContactBadge_contact);
final ImageButton contactStatusButton = (ImageButton)rowView.findViewById(R.id.configuration_button);
You're also making the same mistake with model in the click handler. You need to make sure you're using the model associated with the row, not whatever model happens to be left in the class member.
Also, you should note that you're not actually recycling the rows. You're creating a new row every time. This is defeating one of the main purposes of ListView, which is to not have to create new views for each row. You might want to find one of the many ListView tutorials out there to help you do this correctly.
I think it will mostly solve you problem to invalidate the view after you have updated its image resource. Give this a try for your onClick():
#Override
public void onClick(View view) {
int position = (Integer) view.getTag();
model.setName(contactList.get(position).getName());
model.setNumber(contactList.get(position).getNumber());
model.setPhoto(contactList.get(position).getPhoto());
ContactAdapter.contactArray.add(contactList.get(position));
contactStatusButton.setImageResource(R.drawable.green);
contactStatusButton.invalidate(); //The View now needs to be redrawn
Toast.makeText(context, "contact Number: " + contactList.get(position).getNumber(), Toast.LENGTH_SHORT).show();
}
Related
Sorry for the long title.
I have an android application which shows a custom listView with 5 TextView columns. When the user clicks a row, I change the layout to have 3 TextViews and 2 EditTexts. I have different layout files for both of them. Everything worked fine initially, the row layout changes properly and I am able to click on the EditText and input values. However, I want either of the 2 EditText to automatically gain focus based on what is clicked. I already have a working code for this. My problem is that programatically requesting requestFocus() seems to block the part where I change the row layout with the new view.
Here is the code that changes my row layout, it works fine without the requestFocus() line:
private void changeLayout(final View view){
//get views from old row layout
TextView textViewQuantity = (TextView)view.findViewById(R.id.qtyInput);
TextView textViewDiscountReq = (TextView)view.findViewById(R.id.discInput);
TextView textViewName = (TextView)view.findViewById(R.id.dialogItemName);
TextView textViewPrice = (TextView)view.findViewById(R.id.price);
TextView textViewDiscount = (TextView)view.findViewById(R.id.discount);
//store values in strings
String itemName = textViewName.getText().toString();
String itemPrice = textViewPrice.getText().toString();
String itemDiscount = textViewDiscount.getText().toString();
String itemQty = textViewQuantity.getText().toString();
String itemDisc = textViewDiscountReq.getText().toString();
//set the view to gone
textViewQuantity.setVisibility(View.GONE);
textViewDiscountReq.setVisibility(View.GONE);
textViewName.setVisibility(View.GONE);
textViewPrice.setVisibility(View.GONE);
textViewDiscount.setVisibility(View.GONE);
//get the old layout
LinearLayout ll_inflate = (LinearLayout)view.findViewById(R.id.search_result_layout);
//get the inflate/new view
View child = getLayoutInflater().inflate(R.layout.search_result_inflate, null);
//get the views in the new view, populate them
TextView newName = (TextView)child.findViewById(R.id.dialogItemName);
newName.setText(itemName);
TextView newDiscount = (TextView)child.findViewById(R.id.discount);
newDiscount.setText(itemDiscount);
TextView newPrice = (TextView)child.findViewById(R.id.price);
newPrice.setText(itemPrice);
qtyIn = (EditText)child.findViewById(R.id.qtyInputSearchResult);
qtyIn.setText(itemQty);
qtyIn.setFilters(new InputFilter[] {filter});
discIn = (EditText)child.findViewById(R.id.discInputSearchResult);
discIn.setText(itemDisc);
//show new layout
ll_inflate.removeAllViews();
ll_inflate.removeAllViewsInLayout();
ll_inflate.addView(child);
//request focus here
if(focusTarget == 1){
Log.d("hello", "focus target is 1 " );
qtyIn.setFocusable(true);
qtyIn.setFocusableInTouchMode(true);
qtyIn.requestFocus();
}
else if(focusTarget == 2){
Log.d("hello", "focus target is 2 " );
discIn.requestFocus();
}
Log.d("hello", "focus state qtyIn = " + qtyIn.isFocused());
Log.d("hello", "focus state discIn = " + discIn.isFocused());
}
The interesting part is that the Log shows the proper values, it says the proper focus status according to what I want. However, the ll_inflate.addView(child); line does not work at all!
Does anyone know what happened here? I'm really confused as to why the layout didn't change but the lines after the .addView() line executed. Another weird thing is how requestFocus(); prevents the view from changing.
Any help is very much appreciated. Thanks.
You are doing this in wrong way. You should have two type of view in list, one for show data and one for edit it.
First set view type count
final int TYPE_EDIT =0,TYPE_VIEW =1;//EDIT 7/9/016 type need to be start from 0
public int getViewTypeCount (){
return 2;
}
public int getItemViewType (int position){
//return type as needed. simple if you add int for this in data model
}
And inside getView
public View getView (int position, View convertView, ViewGroup parent){
int type = getItemViewType ();
if(null==convertView){
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if(type ==TYPE_EDIT){
convertView = inflater.inflate(R.layout.rowEditlayout, parent, false);
}else{
convertView = inflater.inflate(R.layout.rowlayout, parent, false);
}
}
}
Do the changes in list data on edit button click and call notifydatasetchanged on adapter
After looking for some answers here, I find myself in a disturbing situation where my Listview is really getting on my nerve.
Here are the questions I looked for :
Maintain ListView Item State
How to save state of CheckBox while scrolling in ListView?
I'm using a custom adapter with a custom row as below.
My Listview is simple as it is displaying a custom row made of three elements :
1) an ImageView displaying contact picture cropped in a circle ;
2) a TextViewdisplaying the contact full name as plain text ;
3) and finally an ImageView that holds the purpouse of a CheckBox.
Please focus on the last element. The ImageView CheckBox-like will have its src changed upon click.
When the user click, the ImageView will switch between a check sign and an unchecked sign according to it's previous status. Possible status are : {checked | unchecked}
So far so good.
But as soon as I scroll the ListView, any aforementioned change will disappear as Android recycle unused view.
Here comes the so-called ViewHolder pattern. Unfortunately, this pattern is failling me on two issues :
First, when scrolling, my organized-in-an-alphabetical-order listview gets disorganized.
e.g. somehow, whitout any reason, the first displayed contact name gets displayed again later on the ListView as I scrolled. That can happen with any row ! So it would seem unused view are being wrongly re-used.
Second, and in accordance to the first issue, the checked status do seem to stay, but not always and if it does stay, it may very well stay on the wrong row ... and that can happen randomly, of course. Therefore ViewHoder is not a viable solution.
Before discouvering the ViewHolder pattern, I have been using a HashMap to store the item position upon click as followed :
ContactsListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public final void onItemClick(final AdapterView<?> adapterView, final View view,
final int position, final long id) {
final ImageView check = (ImageView) view.findViewById(R.id.checkImage);
final TextView name = (TextView) view.findViewById(R.id.contactName);
final Boolean isChecked = Boolean.valueOf(checkedContactsList.isChecked(position));
if (isChecked != null && isChecked.booleanValue() == true) {
check.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.unchecked_sign));
checkedContactsList.(position);
} else {
check.setImageDrawable(getActivity().getResources().getDrawable(R.drawable.checked_sign));
checkedContactsList.add(position, true);
}
}
});
I tried adding a different value instead of position.
I tried with ContactsListView.getPositionForView(view)
And I also tried with the View's ID, but still it doesn't work.
I wish I could use ContactsListView.getSelectedItemPosition() but it returns -1 as there is no selection event because I'm handling a touch/click event.
And this is how my Custom Adapter looks like :
public final View getView(final int position,
final View convertView, final ViewGroup parent) {
final LayoutInflater inflater = (LayoutInflater) this.context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View contactRowView = inflater.inflate(R.layout.contact_row, parent, false);
final ImageView contactPic = (ImageView) contactRowView.findViewById(R.id.contactPic);
final TextView contactName = (TextView) contactRowView.findViewById(R.id.contactName);
final ImageView checkImage = (ImageView) contactRowView.findViewById(R.id.checkImage);
// the list is the same as above and therefore contains the exact same entries
if (this.checkedContactsList.isChecked(position))
checkImage.setImageDrawable(this.context.getResources().getDrawable(R.drawable.checked_sign));
contactPic.setImageBitmap(cropePictureInCircle(this.contacts.get(position).getPicture()));
contactName.setText(this.contacts.get(position).getName());
return contactRowView;
}
Is there a good way to keep the checked row checked and the unchecked row unchecked in the given alphabetical order ?
Thanks !
For the list position change I know the solution but for the second problem I am still searching for a solution, anyway first make a viewHolder class;
public class ViewHolder{
//put all of your textviews and image views and
//all views here like this
TextView contactName;
ImageView checkImage;
ImageView contactImage;
}
Then edit your adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
final View contactRowView = convertView;
ViewHolder holder;
if (contactRowView == null) {
final LayoutInflater inflater = (LayoutInflater)
this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE
);
contactRowView =
inflater.inflate(R.layout.contact_row, parent,
false);
holder = new ViewHolder():
holder.contactPic = (ImageView)
contactRowView.findViewById(R.id.contactPic);
holder.contactName = (TextView)
contactRowView.findViewById(R.id.contactName);
holder.checkImage = (ImageView)
contactRowView.findViewById(R.id.checkImage);
contactRowView.setTag(holder);
} else {
holder = contactRowView.getTag();
// the list is the same as above and therefore contains the exact same entries
if (this.checkedContactsList.isChecked(position))
holder.checkImage.setImageDrawable(this.context.get.
Resources().getDrawable(R.drawable.checked_sign));
holder.contactPic.setImageBitmap(cropePictureInCircle(this.contacts.get(position).getPicture()));
holder.contactName.setText(this.contacts.get(position).getName());
return contactRowView;
}
}
Hope this helps and sorry because writing code from my phone is totally awkward.
holder.voteUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
final int position = myList.getPositionForView((View) v
.getParent());
final ListItem li = values.get(position);
li.voteUp_value = true;
//In getView(), I set different image to holder.voteUp ImageView depending on this value.
//Hence I call notifyDataSetChanged(); which takes care of it.
notifyDataSetChanged();
}
});
But the problem is, Since the whole list is notified, it flickers every time I vote.
I cannot changed the Image of the holder.voteUp directly, because there is a holder.voteDown which has to be changed too, depending on the li.voteUp_value. So, I went with notifyDataSetChanged(); which takes care of all this.
Is there a way I can get the Views from parent View and set the Images, instead of notifyDataSetChanged();?
like:
ViewParent vp = v.getParent();
ImageView im = vp.findViewById(R.id.voteUp);
im.setImageResource.....
But there is nothing like findViewById for ViewParent
You should be able to use the view passed to you from the onClick() listener. Just changing the image should be enough to make it redraw, but if not you can still tell it to manually.
#Override
public void onClick(View v) {
final int position = myList.getPositionForView((View) v
.getParent());
final ListItem li = values.get(position);
li.voteUp_value = true;
ImageView im = vp.findViewById(R.id.voteUp);
if ( im != null ) {
//Update the view here
}
}
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.
Apparently my thinking has some flaw as it's not working correctly. Basically, I'm trying to solve this problem: listview with checkbox
In the answer, people suggested to create a global data structure to hold the state, which made sense. However, I thought if I'm using ViewHolder pattern, I could use the tag as the structure to store state information?
cbox.setOnClickListener(new OnClickListener() {
public void onClick(View v)
{
P tag = (P) ((View) v.getParent()).getTag();
if(tag.cbox.isChecked())
tag.cbox.setChecked(true);
else
tag.cbox.setChecked(false);
//tag.cbox.toggle();
Log.d("YoYo", Boolean.toString(tag.cbox.isChecked()));
}
});
The code above did not toggle my checkbox in the rows. What did I do wrong?
Update: Rather then toggle, manual if statement seemed to work. Though, I'm running into another problem, where the checked state mess up after I scroll to different places. Why is that? If I set the checked state in the tag.cbox, isn't the check state unique to that object only?
Update2: I follow other's suggestion and got a working version, but I'm still wondering why setTag/getTag not working?
Working:
public View getView(final int position, View view, ViewGroup parent)
{
Plurker tag = getItem(position);
if (view == null)
view = adapterLayoutInflater.inflate(R.layout.adapter, null);
tag.avatar = (ImageView) view.findViewById(R.id.imgAvatar);
tag.cbox = (CheckBox) view.findViewById(R.id.cBox);
tag.cbox.setOnCheckedChangeListener(new OnCheckedChangeListener()
{
public void onCheckedChanged(CompoundButton btn, boolean isChecked)
{
int pos = position;
Plurker p = getItem(pos);
p.isChecked = isChecked;
Log.d("PLURK", "Listener:" + p.toString());
}
});
tag.update(getItem(position));
Log.d("PLURK", tag.cbox.getText() + ":" + Integer.toString(position));
return view;
}
Not working:
public View getView(final int position, View view, ViewGroup parent)
{
Plurker tag = getItem(position);
if (view == null)
{
view = adapterLayoutInflater.inflate(R.layout.adapter, null);
tag.avatar = (ImageView) view.findViewById(R.id.imgAvatar);
tag.cbox = (CheckBox) view.findViewById(R.id.cBox);
tag.cbox.setOnClickListener(new View.OnClickListener() {
public void onClick(View arg0)
{
int pos = position;
Plurker p = getItem(pos);
p.isChecked = !p.isChecked;
Log.d("PLURK", "Listener:" + p.toString());
}
});
view.setTag(tag);
}
else
tag = (Plurker) view.getTag();
tag.update(getItem(position));
Log.d("PLURK", tag.cbox.getText() + ":" + Integer.toString(position));
return view;
}
In the "not working" version, I need to use onClick instead of onCheckedChanged event, because when the view from hidden to reappear, it called the event listener, so it would falsely triggered.
Are you inflating a new view each time or making use of the convertView that is passed in?
Normally the Adapter tries to recycle views, only creating enough to provide smooth scrolling. The existing recycled views are passed in as convertView. You can either inflate and return a new view every time (expensive) or just re-setup the convertView (if it exists) based on position. If recycling you need to re-set all the view attributes, as there is no guarantee that the recycled view you get is the same one used for this position in the past.
It sounds like your bug is that you are not correctly re-setting all the attributes of the recycled view (convertView) to match the data for the current position.