Android Listview changes icon when scrolled - android

I have a ListView on my Android App. Each row has a text and an image. When the user clicks a row, the row's image is changed (this works fine). The problem is, when the user scrolls the ListView, at the moment the row crosses the top part of the ListView (when you stop seeing that row), the image changes back to the default one automatically. How can I prevent this? If the user clicks the row and the image is changed, I need that new image to stay there (since the new image is the indicator for the user that he already clicked the row).
Thank you for your help!
Here's my code:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent i;
ImageView imageView;
switch (position){
case 0:
imageView = (ImageView) view.findViewById(R.id.check_alimentacion);
if (frutas.equals("NO")){
imageView.setImageResource(R.drawable.checkbox);
frutas = "SI";
}else {
imageView.setImageResource(R.drawable.checkbox_inactivo);
frutas = "NO";
}
break;
case 1:
imageView = (ImageView) view.findViewById(R.id.check_alimentacion);
if (verdura.equals("NO")){
imageView.setImageResource(R.drawable.checkbox);
verdura = "SI";
}else {
imageView.setImageResource(R.drawable.checkbox_inactivo);
verdura = "NO";
}
break;
case 2:
imageView = (ImageView) view.findViewById(R.id.check_alimentacion);
if (carne.equals("NO")){
imageView.setImageResource(R.drawable.checkbox);
carne = "SI";
}else {
imageView.setImageResource(R.drawable.checkbox_inactivo);
carne = "NO";
}
break;
}
}
});

Well firstly, this is an interesting question.
The images are being replaced because when the list-item view exits the screen it is destroyed, and whenever it reappears it is newly created again. Entirely from scratch. A drawback of listview. A better way to do is to use a RecyclerView. It will create objects for each row using a ViewHolder and later instead of creating everything from scratch, it refers the already created ViewHolder objects.
However, the user selection for a row still needs to be handled all by ourselves here. I would suggest as follows.
Create a global boolean array of same length as your listview.
boolean[] selected=new boolean[listViewLength];
Now, inside onItemClickListener()
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//change value of boolean to true, to depict as selected
selected[position]=true; //default value of boolean is false.
//update the image
imageView = (ImageView) view.findViewById(R.id.check_alimentacion);
imageView.setImageResource(R.drawable.checkedDrawable);
}
});
And finally in your getView() check for the selection which will look something like this,
public View getView (int position, View convertView, ViewGroup parent){
//Initialize the item-view
LayoutInflater inflater=(LayoutInflater)getApplicationContext().getSystemService(LAYOUT_INFLATER_SERVICE);
convertView=inflater.inflate(R.layout.listItemLayout,null,false);
imageView = (ImageView) view.findViewById(R.id.check_alimentacion);
if(selected[position]){
imageView.setImageResource(R.drawable.checkedDrawable);
}
else{
imageView.setImageResource(R.drawable.defaultDrawable);
}
return convertView;
}

Views inside listview are recycled.
You should store each view's state in some kind of array;
For example in List<Integer> itemsImageRes, where itemsImageRes.get(i) will be cached imageResId of listView element at position i
And you should set this image resource to imageView on your adapters getView(int position, View convertView, ViewGroup parent)
And good idea is to use Picasso instead of imageView.setImageResource(R.drawable.drawableid);

Related

How to set gridview within listview item and click on each grid item with uniq posion and click evant

I want to foll list view with image grid date wise.First get Date list of image and then after get image path list of particular date wise image list.and finally I fill list and grid of particular date wise image(No of images and date not fixed),have a look screen shot.
Date wise images display in Grid View
And now my problem is, When I set click listener on image and add check mark image on click then image add in two position, Why I don't know.and second is when I scroll down list view then check mark visible gone.
I want to select multiple image and add check mark image and perform operation like "Delete image" and "Share image"
Like this
I call grid adapter of each list item , look my code tell me what's wrong
Custom List View adapter : get View method
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View row = convertView;
ViewHolder viewHolder = new ViewHolder();
DateSortedImageItem item = getItem(position);
if (row == null) {
row = mInflator.inflate(R.layout.row_date_sorted_image_list, parent, false);
viewHolder.tv_date = (TextView) row.findViewById(R.id.tv_row_date);
viewHolder.rowGridView = (GridView) row.findViewById(R.id.gv_row_item);
viewHolder.tv_date.setText(item.getDate());
row.setTag(viewHolder);
gridAdapter = new GridAdapter(context, R.layout.row_sorted_image_gridview_item,
item.getDateSortedImageList(), position);
viewHolder.rowGridView.setAdapter(gridAdapter);
} else {
row.getTag();
}
return row;
}
private class ViewHolder {
TextView tv_date;
GridView rowGridView;
}
and my grid adapter code is :
public View getView(final int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater mInflater = ((Activity) getContext()).getLayoutInflater();
convertView = mInflater.inflate(R.layout.row_sorted_image_gridview_item, parent, false);
}
view = convertView;
final SquareImageView imageView = (SquareImageView) view.findViewById(R.id.iv_test_row_grid_img);
final ImageView iv_checked = (ImageView) view.findViewById(R.id.iv_checked);
final ImageView iv_unChecked = (ImageView) view.findViewById(R.id.iv_unchecked);
view.setTag(arrList.get(position).toString());
File f = new File(arrList.get(position).toString());
System.out.println("arrList size >>" + arrList.size());
Picasso.with(context).load(Uri.fromFile(f)).resize(100, 100).centerCrop().placeholder(R.drawable.ic_launcher)
.error(R.drawable.ic_close_black).into(imageView);
I just set Grid View image. And now i set imageview click ivent and add check mark then add check mark event two places and scroll down then visible gone also.
What's wrong hear and how to set click event of image view unique.
Finally I want to design Look like Google "Photos" app.
Suggest me any example or library:
First you should create a POJO as below
public class AlbumItem {
private String headerStr;
private List<ChildItem> children;
//getter - setter....
public class ChildItem {
private String childName;
private boolean isSelected;
//getter - setter.....
}
}
Now use this POJO inside your main adapter.
and in the second adapter handle the selection logic means if isSelected is true then show iv_checked otherwise show iv_unchecked.
Please let me know if you need further explanation.
Hope it will help you.

Android: Determine which View clicked within ListView row

I have a ListView with lots of icons which may be clicked to change the state of their respective row items. I realise that I can create onClick handlers for all of them but I would like to a generic means of identifying which icon (View) has been clicked. (e.g. determine View at touch coords x,y?
Any idea how I can do that?
I am getting row clicks with the below handler:
lvClickListener = new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick( AdapterView<?> arg0, View arg1, int position, long arg3 )
...
On each row I have 5 ImageViews.
I assume all rows are being inflated with the same layout and you are using the holder pattern (http://developer.android.com/training/improving-layouts/smooth-scrolling.html).
This way, you can have a single OnClickListener to "listen" for any of your ImageView (or whatever they are). You only need to set the listener in the momento you actually inflate the view (not when you reuse it), and then set for all ImageView in the same row the row position as the Tag, so, then in the onClick method, you can chech to which row they belong to (by the tag) and which ImageView is it (by the id)
class MyAdapter extends BaseAdapter implements OnClickListener
[...]
#Override
public View getView (int position, View convertView, ViewGroup parent)
{
MyHolder holder = null;
if(convertView != null)
{
holder = new MyHolder();
convertView = inflater.inflate(...)
holder.imageView1 = (ImageView)convertView.findViewById(R.id.[...] )
holder.imageView1.setOnClickListener(this);
holder.imageView2 = (ImageView)convertView.findViewById(R.id.[...] )
holder.imageView2.setOnClickListener(this);
[...]
convertView.setTag(holder);
}
else
{
holder = (HyHolder)convertView.getTag();
[...]
}
holder.imageView1.setTag(position);
holder.imageView2.setTag(position);
[...]
return convertView;
}
#Override
public void onClick(View v)
{
int id = v.getId();
Integer position = (Integer)v.getTag();
[...]
}
Of course you can then optimize this, put the views in an array or whatever or put the OnClickListener outside the Adapter (in the activity, or an instance variable), but this is the basic idea.
As a note, you should check what happens with the onItemClickListener, I'm not pretty sure, but it may cause problems intercepting touches (or it may no, I don't know), but if you're not going to use the whole row click, then you can remove it
You can do this in multiple ways, You can tag each ImageView to your AsyncTask(or whatever) you use. Or, refer https://github.com/square/picasso

How to save and maintain ListView items states after scroll?

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.

ListView Odd/Even Rows Refresh Automatically When Scrolling Down Or Selecting AnotherItem

I use even and odd rows to set backgrond to my listview rows. In my efficientAdapter I set the row background as follows:
public View getView(int position, View convertView, ViewGroup parent) {
vi = convertView;
if (convertView == null) {
vi = inflater.inflate(R.layout.ecran_multiple_row, null);
holder = new ViewHolder();
holder.txIndex = (TextView) vi.findViewById(R.id.txIndex);
holder.txSTitle = (TextView) vi.findViewById(R.id.txSTitle);
holder.btOnOFF = (ImageView) vi.findViewById(R.id.btOnOFF);
vi.setTag(holder);
} else
holder = (ViewHolder) vi.getTag();
/*
* CHANGE ROW COLOR 0 WHITE 1 GRAY
*/
if ( position % 2 == 0) //0 even 1 odd..
vi.setBackgroundResource(R.drawable.listview_selector_odd);
else
vi.setBackgroundResource(R.drawable.listview_selector_even);
/*
* ONE ITEM IN ARRAY
*/
if (data.toArray().length==1){
holder.btOnOFF.setBackgroundResource(R.drawable.air_radio_button_rouge);
}else {
holder.btOnOFF.setBackgroundResource(R.drawable.air_deezer_check);
}
return vi;
}
and in my MainActivity.Class. I select an item using on itemclicklistener() as shown below:
**lvRMultiple.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
imgview = (ImageView) view.findViewById(R.id.btOnOFF);
//And change its background here
imgview.setBackgroundResource(R.drawable.air_radio_button_rouge);
}
});**
When i clicked on an item btnOff image change successfully but when i scroll down it change to default background. Secondly when i click on one item after the other both becomes the new image but i want only the row clicked by the user to change to new image and the previous image are set to default.
All row view of a ListView created by the getView() method of BaseAdpter class. When ever we scroll the ListView all, new viable row create by getView() using recycle. So getView() called again and again when new row is viable on scroll.
There are two solution of your question:-\
You can save the status of ListView
// Save ListView state
Parcelable state = listView.onSaveInstanceState();
// Set new items
listView.setAdapter(adapter);
// Restore previous state (including selected item index and scroll position)
listView.onRestoreInstanceState(state)
And other solution is create RowView at runtime and add it on a Parent Layout by using addView() method.
LayoutInflater inflater = LayoutInflater.from(context);
// You should use the LinerLayout instead of the listview, and parent Layout should be inside of the ScrollView
parentView = (LinerLayout)this.findViewById(R.id.parentView);
for(int i = 0; i<=numberOfRow;i++){
LinearLayout rowView = (LinerLayout)inflater.inflate(R.layout.rowView);
ImageView rowImageView = (ImageView)rowView.findViewById(R.id.rowImage);
rowImageView.setOnClickListener(new View.onClickListListener(){
#Override
public void onClick(){
rowImageView.setImageBitmap(onClickBitmapImage);
}
});
parentView.addView(rowView);
}
Please check this answer Maintain/Save/Restore scroll position when returning to a ListView
More Reference
http://developer.android.com/reference/android/widget/Adapter.html#getView(int,android.view.View, android.view.ViewGroup)
The item changes back to the default background because the view gets recycled. This is the same problems of checkboxes losing their checked state
Check out this answer too see how to handle it:
CheckBox gets unchecked on scroll in a custom listview
As for your second problem, I believe it's already answered here:
highlighting the selected item in the listview in android
Hope it helps

How to change the image resource of a item clicked in a list view

I have a custom listview which has an ImageView on the left, followed by a EditText and on the right I have another ImageView which, at first, has not any source.
Then I have an onItemClickListener on this View. I would like to change the source of the ImageView on the right (the one that initially hasn't any source) on the item click.
For this I have implemented this code:
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
ImageView icone2 = (ImageView) view.findViewById(R.id.icone2_lugar);
icone2.setImageResource(R.drawable.flag_origem);
}
The ImageView is succefully inserted on the item clicked. However, when I scroll down the list, I realized that all the other elements which were at the same position of the one clicked before I scroll the list, also changed the image resource.
Why is it happening? I guess Android loads new views as scroll the list instead of loading all elements views at the same moment, so I guess I can't change the resource of a element in a list by taking the View parameter of onItemClick.
So how could I change the resource of the clicked element in a list view?
EDIT:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View linha = convertView;
ArmazenadorLugar armazenador = null;
if (linha == null) {
LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
linha = inflater.inflate(R.layout.linha_lugar, parent, false);
armazenador = new ArmazenadorLugar(linha);
linha.setTag(armazenador);
} else {
armazenador = (ArmazenadorLugar) linha.getTag();
}
armazenador.popularFormulario(mListaLugares.get(position), mContext);
return linha;
}
public LugarAndroid getItem(int position) {
return mListaLugares.get(position);
}
I ended up doing it in a different way. Don't know if it is the better but it works.
I created a variable (imgPosition) in my adapter which will hold the element position which has to set the image resource. Then, whenever I click onto an element, the listener calls
adapter.setImgPosition(position);
adapter.getView(position, view, parent);
I called the getView method in the listener to refresh the list, because getView calls a method (I'll explain it next) to update the list.
In the getView method of the adapter class I have the following logic (the position element comes from getView parameter):
holder.setValues(mListaLugares.get(position), mContext, imgPosition == position);
In the holder:
static class Holder{
private TextView txt1= null;
private ImageView img1 = null;
private ImageView img2 = null;
Holder(View linha) {
txt1= (TextView) linha.findViewById(R.id.txt1);
img1 = (ImageView) linha.findViewById(R.id.img1 );
img2 = (ImageView) linha.findViewById(R.id.img2);
}
void setValues(LugarAndroid lugar, Context mContext, boolean setImg) {
img2.setImageResource(0);
if(setImg) img2.setImageResource(R.drawable.flag);
// Logic to fill up the others views
}
This way I will only set the image resource of the element clicked when the boolean passed is true
If you are using an adapter with a List of Object, you can use :
getItem(position) and then change the image of the imageview of this specific object

Categories

Resources