I have a ListView and a simple adapter class with two element. I just want to change one element by clciking the other one but whenever I scroll down or up the change is disappear. I have studied this two links
Why do items disappear when I scroll the listView?
Clickable element in custom ListView row
I know that listView has recycling feature which is the reason for this but I want a simple solution without refresh the whole list and when I will change on multiple rows, it should be there after getView called I mean after scroll up or down.
here is my adapter class code
public class Pha extends BaseAdapter {
private List<PhaMessages> pList = new ArrayList<PhaMessages>();
private Activity context;
ViewHolder viewHolder = null;
int fCounted = 0;
public Pha(Activity cont, ArrayList<PhaMessages> posts){
this.context = cont;
this.pList = posts;
Log.d("pha","called constructor..");
}
static class ViewHolder {
protected TextView fCount;
protected ImageView fIcon;
}
#Override
public int getCount() {
return pList.size();
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater inflator = context.getLayoutInflater();
convertView = inflator.inflate(R.layout.pha_list_row, null);
viewHolder = new ViewHolder();
viewHolder.fIcon = (ImageView)convertView.findViewById(R.id.f_counter_img);
viewHolder.fCount = (TextView)convertView.findViewById(R.id.f_plus);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
final ViewHolder hold = viewHolder;
viewHolder.fCount.setText("" + pList.get(position).NumFrnd);
viewHolder.fIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!t){
fCounted++;
hold.fCount.setText(""+fCounted);
// by doing this at list I can see an immediate change that the number is changing
//
//but is i do viewHolder.fCount.setText(""+fCounted); I see another row changing
}else {
Toast.makeText(context,"You have already clcik this.",Toast.LENGTH_SHORT).show();
}
}
});
return convertView;
}
}
thanks in advance.
When you incrementfCounted value increment that value in your collection ie pListas well
in your getview method put position as tag in fCount component
viewHolder.fIcon.setTag(position); // this is to get exact position for which we will change/increment value
Now in fIcon's OnClickListener method
viewHolder.fIcon.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(!t){
fCounted++;
hold.fCount.setText(""+fCounted);
int position = (int)v.getTag();
int oldValue = pList.get(position).NumFrnd;
pList.get(position).NumFrnd = ++oldValue;
}
}
};
Related
My ListView item consists of the following components - TextView and then under it there is one ImageView - like.
So when I click on like I want to be able to change the like ImageView from grey to blue.
At the moment when I click on like the like ImageView does change from grey to blue. But not only for the corresponding ListView item but for every 3rd item in the list - so if I have 10 items in my list and I click on the like of the first item in the list then 4th, 7th and 10th items like ImageView change from grey to blue.
In my post_list_item.xml in the root element of the file I specified the following android:descendantFocusability="blocksDescendants" but it doesn't help either.
My question is - what do I have to do so that when I click on like I would be able to change the like ImageView from grey to blue without affecting other list items?
Here is my code:
public class CustomListAdapter extends BaseAdapter {
LayoutInflater inflater;
ArrayList<Post> list;
public CustomListAdapter(PostActivity postActivity, ArrayList<Post> list) {
inflater = LayoutInflater.from(postActivity);
this.list = list;
}
#Override
public int getCount() {
return list.size();
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.post_list_item, parent, false);
holder = new ViewHolder();
holder.tvPost = (TextView) convertView.findViewById(R.id.tvPost);
holder.ivLike = (ImageView) convertView.findViewById(R.id.ivLike);
holder.tvLikes = (TextView) convertView.findViewById(R.id.tvLikeCount);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Post post = list.get(position);
holder.tvPost.setText(post.getPost());
holder.tvLikes.setText(post.getLiked());
holder.ivLike.setOnClickListener(mClickListener);
holder.ivLike.setTag(position);
return convertView;
}
private View.OnClickListener mClickListener = new View.OnClickListener() {
public void onClick(View v) {
int pos = (Integer) v.getTag();
Post post = (Post) list.get(pos);
post.setIvLike(v);
CustomListAdapter.this.notifyDataSetChanged();
}
};
static class ViewHolder
{
TextView tvPost ;
TextView tvLikes;
ImageView ivLike;
}
}
Post.java
public class Post {
String post;
String liked;
public void setIvLike(View view){
if(view.isActivated())
view.setActivated(false);
else
view.setActivated(true);
}
public String getPost() {
return post;
}
public void setPost(String post) {
this.post= post;
}
public String getLiked() {
return liked;
}
public void setLiked(String liked) {
this.liked = liked;
}
}
You're not "resetting" the view in getView when a view is reused. You need to set the recycled view back to the correct state. This is done after the if-else block where you create or reuse a ViewHolder.
setIvLike will need to modify that Post object in one way or another such that when it is scrolled off screen the view can be recreated exactly as it appeared even if it is using a recycled view. For that reason it is important to know which properties of the view you change and consistently set every one of those properties in getView after reuse/creation.
You need to have a property in your Post Class to track that post has been liked
Updated Post.Java
public class Post {
String post;
String liked;
public bool isLiked; // Add this
public void setIvLike(View view){
if(!isLiked)
view.setActivated(false);
else
view.setActivated(true);
}
public String getPost() {
return post;
}
public void setPost(String post) {
this.post= post;
}
public String getLiked() {
return liked;
}
public void setLiked(String liked) {
this.liked = liked;
}
}
And in getView method of Adapter
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = inflater.inflate(R.layout.post_list_item, parent, false);
holder = new ViewHolder();
holder.tvPost = (TextView) convertView.findViewById(R.id.tvPost);
holder.ivLike = (ImageView) convertView.findViewById(R.id.ivLike);
holder.tvLikes = (TextView) convertView.findViewById(R.id.tvLikeCount);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
Post post = list.get(position);
holder.tvPost.setText(post.getPost());
holder.tvLikes.setText(post.getLiked());
holder.ivLike.setOnClickListener(mClickListener);
holder.ivLike.setTag(position);
holder.setIvLike(ivLike); // Add this
return convertView;
}
private View.OnClickListener mClickListener = new View.OnClickListener() {
public void onClick(View v) {
int pos = (Integer) v.getTag();
list.get(pos).isLiked = true;
// post.setIvLike(v); // No need to update here
CustomListAdapter.this.notifyDataSetChanged();
}
};
My rows contain a button that has its own click listener set in my adapter's
When I click on a button it sets the button text "yes' and again click than change it to "No" properly, my problem is as I scroll through the list its setting it for different rows as well. I assume theirs an issue somewhere with views recycling. and when i scroll list then changed text button needs double click to again change button text. but i want it on single click.
what should i have to do?
Here's my code:
public class ListAdapter extends ArrayAdapter<Model> {
customButtonListener customListner;
public interface customButtonListener {
public void onButtonClickListner(int position, Model model);
}
public void setCustomButtonListner(customButtonListener listener) {
this.customListner = listener;
}
private Context context;
private ArrayList<Model> data = new ArrayList<Model>();
public ListAdapter(Context context, ArrayList<Model> dataItem) {
super(context, R.layout.row, dataItem);
this.data = dataItem;
this.context = context;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder viewHolder;
if (convertView == null) {
LayoutInflater inflater = LayoutInflater.from(context);
convertView = inflater.inflate(R.layout.row, null);
viewHolder = new ViewHolder();
viewHolder.text = (TextView) convertView
.findViewById(R.id.childTextView);
viewHolder.text1 = (TextView) convertView
.findViewById(R.id.childTextView1);
viewHolder.button = (Button) convertView
.findViewById(R.id.childButton);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
final Model model = getItem(position);
viewHolder.text.setText(model.getNames());
viewHolder.button.setTag(1);
viewHolder.button.setText(model.getYes());
viewHolder.button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
int status = (Integer) v.getTag();
if (status == 1) {
model.setYes("Yes");
viewHolder.button.setText("Yes");
v.setTag(0);
} else {
model.setYes("No");
viewHolder.button.setText("No");
v.setTag(1);
}
}
});
return convertView;
}
public class ViewHolder {
ViewHolder viewHolder;
TextView text, text1;
Button button;
}
}
you need to remove viewHolder.button.setTag(1); from code. This code actually changes the tag previously set to 0 to 1.So 2 click is required to change it again to 1 then change is reflected in ui.
This is a common problem in listview.You can overcome it with getViewTypeCount() and getItemViewType(int position) methods.
Add this two methods in your adapter like:
#Override
public int getViewTypeCount() {
// TODO Auto-generated method stub
return 2;//The number of conditions that may occur in listview
}
#Override
public int getItemViewType(int position) {
// TODO Auto-generated method stub
if (status == 1) {
return 1;
} else {
return 0;
}
}
Then in getView() method:
int type=getItemViewType(position);
if(type==1)
{
model.setYes("Yes");
viewHolder.button.setText("Yes");
v.setTag(0);
}else{
model.setYes("No");
viewHolder.button.setText("No");
v.setTag(1);
}
To know more about this two methods you can see this and also this.
And tutorials: here and here.
This problem will not occur again if you correctly implement this 2 methods.
I am getting one problem while adding ListView in layout. I have implemented one ListView in one page where we get list of items, in that when we click on some ListMember it change its color and again clicking on it will change it to previous color.Now imagine because of Item height one screen can hold maximum 5 List items,for next member to see you need to scroll down.
Now imagine List members are
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Among these use can only see 5 items at a time, now when I click on 'Item 1'(first member of first five members) its color is changing(say WHITE TO GREEN) but when I scroll down I see 'Item 6'(first member of first five members) is also changed its color(to GREEN),and when I click on 'Item 6' ,this time setOnItemClickListener for that member is getting actually triggered and trying changing its color to what it already changed.
this is code for setOnItemClickListener :
productList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> a, View v, int position, long id) {
Log.i("imIn","Item Clicked");
v.animate();
if(listClicked[position]==0)
{
Log.i("***After*** ","Cyan Set ON");
v.setBackgroundColor(Color.parseColor("GREEN"));
listClicked[position]=1;
}
else if(listClicked[position]==1){
Log.i("***After*** ","Cyan Set OFF");
v.setBackgroundColor(Color.parseColor("WHITE"));
listClicked[position]=0;
}
}
});
AfterEdit:: this is my adapter
public class ProductListBaseAdapter extends BaseAdapter {
SharedPreferences sharedpreferences;
private static ArrayList<Product> searchArrayList;
private LayoutInflater mInflater;
ArrayList<TotalSelectedProduct> selectedProducts=new ArrayList<>();
final int[] listClicked;
public ProductListBaseAdapter(Context context, ArrayList<Product> totalProducts, int[] ClickedList) {
searchArrayList =totalProducts;
mInflater = LayoutInflater.from(context);
listClicked=ClickedList;
}
public int getCount() {
return searchArrayList.size();
}
public Object getItem(int position) {
return searchArrayList.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.item_list, null);
holder = new ViewHolder();
holder.txtItem = (TextView) convertView.findViewById(R.id.item_name);
holder.edit=(Button)convertView.findViewById(R.id.edit);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
/** I have tried inserting onClickListener in adapter also .but resulting same
*
holder.txtItem.setText(searchArrayList.get(position).getItemName());
final View.OnClickListener makeListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
v.animate();
if(listClicked[position]==0)
{
Log.i("***After*** ","Cyan Set ON");
v.setBackgroundColor(Color.parseColor("#ff2dbeff"));
listClicked[position]=1;
}
else if(listClicked[position]==1){
Log.i("***After*** ","Cyan Set OFF");
v.setBackgroundColor(Color.parseColor("#009933"));
listClicked[position]=0;
}
}
};
holder.txtItem.setOnClickListener(makeListener); */
return convertView;
}
static class ViewHolder {
TextView txtItem;
Button edit;
}
}
Why this is happening ?
to do what you want, you have to add an adapter to your listview and there control the on click method for each item.
UPDATE WITH EXAMPLE
public class ProductAdapter extends ArrayAdapter<Product> {
public PProductAdapter(Activity activity,
ArrayList<Product> products) {
super(activity, R.layout.item_product, products);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final Product p = getItem(position);
ViewHolder viewHolder;
if (convertView == null) {
viewHolder = new ViewHolder();
LayoutInflater inflater = LayoutInflater.from(getContext());
convertView = inflater.inflate(R.layout.item_product, parent,
false);
viewHolder.name = (TextView) convertView.findViewById(R.id.tvName);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
p.checked = !p.checked;
if (p.checked)
v.setBackgroundColor(Color.parseColor("#ff2dbeff"));
else
v.setBackgroundColor(Color.parseColor("#009933"));
}
});
viewHolder.name.setText(p.name);
return convertView;
}
private class ViewHolder {
TextView name;
}
}
public class Product{
public String name;
public boolean checked;
Product() {
name = "dummy name";
checked = false;
}
}
Do as following
1 Handle click event in your adapter not Activity
2 View for your click can be parent layout in item_list.xml
3 Don't use final int[] listClicked instead have boolean variable in Product class for example isChecked.
4 Set and unset isChecked on click and set background color
i use view holder to hold views in list view, and her my code
public class MyAdapter extends BaseAdapter{
Context context;
private LayoutInflater mInflater;
ViewHolder holder;
public MyAdapter(Context context/*,List<Music> list*//*,ListView listView*/){
this.context = context;
}
#Override
public int getCount() {
return 15;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
int p = 0;
View view = null;
if(convertView == null){
holder= new ViewHolder();
convertView = View.inflate(context, R.layout.item, null);
holder.set = (TextView)convertView.findViewById(R.id.set);
holder.button = (Button)convertView.findViewById(R.id.button1);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
holder.button.setOnClickListener(new lis(position,holder));
return convertView;
}
class lis implements OnClickListener{
private int p;
private ViewHolder holder;
public lis(int p,ViewHolder holder){
this.p = p;
this.holder = holder;
}
int counter=0;
#Override
public void onClick(View v) {
p++;
counter++;
Log.e("counter ",String.valueOf(counter));
holder.set.setText("" + counter);
}
}
static class ViewHolder{
Button button = null;
TextView set = null;
}
}
but when i click on an item in the list for example i click on item 1 to increase its value by +1, but in this code it also increase other two items with +1 as will, where is the problem in my code?
It is due to the fact, that you're data is stored in the click listener. It shouldn't.
The ListView component doesn't generate view for each item in the list. So, there won't be 15 of them in your case. There will be just as many as can fit on the screen. When you scroll the list, the old items, which are no longer visible, are recycled. The getView is then called with convertView != null and the adapter is giving you an opportunity to update this recycled item view. Since you don't update the view, and only assign a new click listener, the item seems to have a count of 1, but it's just the old item view being recycled.
Moreover, updating recycled view won't help in your case, since item data is declared in your click listener. Move it outside, for example in the adapter:
private int[] counters = new int[15];
Then, in the click listener, access count of the item in question by its position:
#Override
public void onClick(View v) {
counters[p]++;
int count = counters[p];
holder.set.setText(String.valueOf(count));
}
Finally, in getView(), always update item view to reflect current count:
holder.button.setOnClickListener(new lis(position,holder));
holder.set.setText(String.valueOf(counters[position]));
I want to know about how to delete child view at some position in list view and added the same at the top of the list. I tried using the following methods butit will generate the unsupported exception. How do that please can anybody help me.
lvAddedContacts.removeViewAt(AddUserView,nAddUserPosition );//Here i want to remove this view from the list
lvAddedContacts.addView(AddUserView, 0); //Add the same at the top
lvAddedContacts.invalidate();//list is refreshed
contactsAdapter.notifyDataSetChanged();
private class ContactsListViewAdapter extends BaseAdapter
{
private LayoutInflater mInflater;
public ContactsListViewAdapter(Context context) {
// Cache the LayoutInflate to avoid asking for a new one each time.
mInflater = LayoutInflater.from(context);
}
public int getCount()
{
int nListSize = DH_Constant.AddedContactsList_obj.response.size();
if(nListSize > 0)
{
return nListSize;
}
else
{
return 0;
}
}
public Object getItem(int position)
{
return position;
}
public long getItemId(int position) {
return position;
}
public View getView(final int position, View convertView, final ViewGroup parent)
{
ViewHolder holder;
convertView = mInflater.inflate(R.layout.added_contacts_list, null);
holder = new ViewHolder();
//getting the id's
holder.tvName = (TextView) convertView.findViewById(R.id.xrays_Name_tv);
holder.btnRemove = (Button) convertView.findViewById(R.id.xrays_removebtn);
//Name
String strName = DH_Constant.AddedContactsList_obj.response.get(position).Name;
holder.tvName.setText(strName);
//Change the color for differentiate the dicom and non dicom users
if(DH_Constant.AddedContactsList_obj.response.get(position).IsDicomUser)
{
holder.tvName.setTextColor(Color.rgb(0, 135, 137));
}
else
{
holder.tvName.setTextColor(Color.BLUE);
}
//Remove button Listener
holder.btnRemove.setBackgroundResource(R.layout.xrays_contact_removebtn_widget);
holder.btnRemove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v)
{
lnIcontactId = DH_Constant.AddedContactsList_obj.response.get(position).ImportedContactsID;
nDicomUser = DH_Constant.AddedContactsList_obj.response.get(position).IsDicomUser?1:0;
//Alert for remove the contact
showDialog(DIALOG_removebtnalert);
}
});
//Copy the view and position if the user is added
if(DH_Constant.blnAddUserStatus)
{
System.out.println("IContactID(xrays):"+DH_Constant.lnAddUserID);
if(DH_Constant.AddedContactsList_obj.response.get(position).ImportedContactsID == DH_Constant.lnAddUserID)
{
nAddUserPosition = position;
AddUserView = convertView;
}
}
return convertView;
}
class ViewHolder
{
TextView tvName;
Button btnRemove;
}
}
Don't remove Views backed by an Adapter yourself, as it may result in weired behavior!! Your Implementation of BaseAdapter looks strange to me, too. Ie:
public Object getItem(int position)
{
return position;
}
public long getItemId(int position) {
return position;
}
doesn't seem to make any Sense!
You should use an ArrayAdapter passing a Model to it and just implement getView(int, View, ViewGroup) accordingly. If you then want to move an Item inside on Top, all you have to do is just:
ArrayAdapter adapter = //initialize with your Model Objects and set it as ListAdapter
Object someItemInsideList = //some Item
adapter.remove(someItemInsideList);
adapter.insert(someItemInsideList, 0);
adapter.notifyDataSetChanged();