ImageAdapter onClick goes wrong - java

I'm trying to set an onClick listener on my ImageView in the Adapter of my GridView. However, weird thing happens: The content of the onClick function affects also some other Views in my GridView.
There is a good reason that I don't do the click listener on my GridView, so I need a solution for this via the ImageAdapter.
The logcat is called only once I click, but for some reason, other ImageViews are affected by this function.
Here's relevant code:
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if(convertView == null) {
holder = new ViewHolder();
convertView = mInflater.inflate(R.layout.row_multiphoto_item, null);
holder.tickImageView = (ImageView) convertView.findViewById(R.id.tickImageView);
holder.imageView = (ImageView) convertView.findViewById(R.id.imageView1);
holder.imageViewLayout = (LinearLayout)convertView.findViewById(R.id.imageViewLayout);
convertView.setTag(holder);
}
else{
holder = (ViewHolder) convertView.getTag();
}
holder.imageView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
ImageView imageView = (ImageView)v;
int id = imageView.getId();
imageView.setVisibility(View.GONE);/*
if(!thumbnailsselection[id]){
Log.d(Global.TAG, "CLICK");
holder.tickImageView.setVisibility(View.VISIBLE);
holder.imageViewLayout.setBackgroundResource(R.drawable.imageview_selected);
thumbnailsselection[id] = true;
}
else{
holder.tickImageView.setVisibility(View.GONE);
holder.imageViewLayout.setBackgroundResource(R.drawable.imageview_unselected);
thumbnailsselection[id] = false;
}
*/
}
});
holder.imageView.setId(position);
holder.imageViewLayout.setId(position);
holder.tickImageView.setId(position);
holder.imageView.setImageBitmap(thumbnails[position]);
return convertView;
}
class ViewHolder {
ImageView imageView;
LinearLayout imageViewLayout;
ImageView tickImageView;
int id;
}

In baseAdapters, view are recycled. This means that if you set a view to invisible, you will add some other view invisible when you will scroll.
To avoid that, be sure to set again the visibility of yout view in the getView method:
holder.imageView.setVisibility(View.VISIBLE)
holder.imageView.setOnClickListener(new OnCl...
Also you will have to store each visibility state, in order to reassing to visible or invisible.

Related

Android: My listView onClick events are not working as intended (order messes up)

My program is supposed to show a list of Apps and their data usage. If you click that app, it starts a new activity page and gives more information about that app. Everything works fine on the initial page and all apps show accurate information, but after I scroll down it messes up - the "more info" page shows a different app than the one I clicked.
I'm pretty sure the problem is around the onclick event not being binded to the holder? However I can't figure out what to do there. It should be noted that the problem is fixed if i get rid of the if(convertView == null)/else condition, but I know that is bad practice since we don't want to keep re-generating the previous items
Here is the getView code in my customAdapter:
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
final DataUsageModel model;
model = getItem(position);
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(context).inflate(R.layout.customlayout, null);
convertView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intentMoreInfo = new Intent(view.getContext(),MoreInfoActivity.class);
intentMoreInfo.putExtra("wifiUsage",model.getWifiUsage());
intentMoreInfo.putExtra("mobileUsage",model.getMobileUsage());
intentMoreInfo.putExtra("appName",model.getName());
intentMoreInfo.putExtra("pname",model.getPname());
context.startActivity(intentMoreInfo);
}
});
holder.nameText = (TextView) convertView.findViewById(R.id.textViewWord);
holder.totalUsageText = (TextView) convertView.findViewById(R.id.textViewDescription);
holder.imageView = (ImageView) convertView.findViewById(R.id.iconImageView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.nameText.setText(model.getName());
long usageInMB = (model.getWifiUsage() + model.getMobileUsage())/(1024*1024);
holder.totalUsageText.setText(String.valueOf(usageInMB) + " MB");
holder.imageView.setImageDrawable(model.getImageRes());
return convertView;
}
public class ViewHolder {
TextView nameText;
TextView totalUsageText;
ImageView imageView;
}
Here is my code for the new activity started:
public class MoreInfoActivity extends AppCompatActivity {
TextView wifiUsage;
TextView mobileUsage;
ImageView icon;
TextView appName;
Button backButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.more_info_layout);
wifiUsage = findViewById(R.id.moreInfoWifi);
mobileUsage = findViewById(R.id.moreInfoMobile);
appName = findViewById(R.id.moreInfoAppName);
icon = findViewById(R.id.moreInfoIcon);
backButton = (Button)findViewById(R.id.moreInfoBack);
backButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
finish();
}
});
appName.setText(getIntent().getStringExtra("appName"));
wifiUsage.setText("Wifi Usage: " + String.valueOf(getIntent().getLongExtra("wifiUsage",0)/(1024*1024)) + " MB");
mobileUsage.setText("Mobile Usage: " + String.valueOf(getIntent().getLongExtra("mobileUsage",0)/(1024*1024)) + " MB");
try {
icon.setImageDrawable(getPackageManager().getApplicationIcon(getIntent().getStringExtra("pname")));
}catch(PackageManager.NameNotFoundException e){
e.printStackTrace();
}
You should set OnClickListener for every iteration of the getView method, not just when convertView == null.
In your case, the current data position is not synced with the current view (holder) position for every getView iteration
Furthermore, it is a very bad practice to start the activity from the adapter itself.
The adapter role is to bind view to data, and that's all.

ListView item background color update

I've created listview with dynamically items inside which contains two items: CheckBox and TextView. When user taps checkbox inside item it updates background color of textView. It works fine until this item is scrolled out of the visible for user space of list and I get error: java.lang.NullPointerException at com.viewactivities.AddNewPosition.changeTextColorWhenCheckBoxIsClicked(AddNewPosition.java:183).
I have also implemented interface with one method responsible for changing color. I have observed that method "changeTextColorWhenCheckBoxIsClicked" is fired also when item from listView is not visible anymore (Here it causes error). Can anyone help?
Code for class which contains listview and method to change color:
#Override
public void changeTextColorWhenCheckBoxIsClicked(int position) {
// TODO Auto-generated method stub
listView.getChildAt(position).findViewById(R.id.listViewItemText).setBackgroundColor(Color.BLUE);
}
And code from listView adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent)
{
ViewHolder viewHolder = null;
globalPosition = position;
if(convertView==null){
LayoutInflater inflater = ((AddNewPosition) mContext).getLayoutInflater();
convertView = inflater.inflate(layoutResourceId, parent, false);
viewHolder = new ViewHolder();
viewHolder.text = (TextView) convertView.findViewById(R.id.listViewItemText);
viewHolder.checkbox = (CheckBox) convertView.findViewById(R.id.listViewItemCheckBox);
viewHolder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
int getPosition = (Integer) buttonView.getTag(); // Here we get the position that we have set for the checkbox using setTag.
listOfObjects.get(getPosition).setCheckBoxEnabled(buttonView.isChecked()); // Set the value of checkbox to maintain its state. changeTextColorWhenCheckBoxIsClicked(getPosition);
}
});
convertView.setTag(viewHolder);
convertView.setTag(R.id.listViewItemText, viewHolder.text);
convertView.setTag(R.id.listViewItemCheckBox, viewHolder.checkbox);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.checkbox.setTag(position); // This line is important.
viewHolder.text.setText(listOfObjects.get(position).getTextFromListViewItemObject());
viewHolder.checkbox.setChecked(listOfObjects.get(position).getIsCheckedBoxEnabled());
//viewHolder.text.setBackgroundColor(Color.RED);
//ListViewItemObject tmpObject = listOfObjects.get(position);
//TextView listItemText = (TextView) convertView.findViewById(R.id.listViewItemText);
//CheckBox checkBox = (CheckBox) convertView.findViewById(R.id.listViewItemCheckBox);
//checkBox.setChecked(false);
//listItemText.setText(tmpObject.getTextFromListViewItemObject());
return convertView;
}
#Override
public void changeTextColorWhenCheckBoxIsClicked(int position) {
// TODO Auto-generated method stub
((AddNewPosition) mContext).changeTextColorWhenCheckBoxIsClicked(position);
}
ListView reuses its views so instead of using setTag() use a list to maintain the position.
viewHolder.checkbox.setTag(position); // This line is important.
Something like this -
Android Listview Scrolling challenge for row elements

Android checkbox Listview recycling is causing unexpected box checking

I am developing an app using a ListView with a simple custom adapter, each row containing a CheckBox object. However, due to ListView's recycling feature (that I don't plan on turning off), when any of the boxes are checked, others below or above in the ListView are also checked.
The following is my getView() in the adapter, along with the custom ViewHolder class:
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.populate_friends_row, null);
holder = new ViewHolder();
holder.nameCheckBox = (CheckBox) convertView.findViewById(R.id.isFriend);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.nameCheckBox.setText(data.get(position).contactLabel);
holder.nameCheckBox.setChecked(checked.get(position));
holder.nameCheckBox.setTag(String.valueOf(position)); // to properly track the actual position
holder.nameCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener(){
#Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
int pos = Integer.parseInt(buttonView.getTag().toString());
checked.set(pos, isChecked);
}
});
return convertView;
}
public static class ViewHolder {
public CheckBox nameCheckBox;
}
I am already holding the checked boxes in the ArrayList of booleans: checked.
Any help would be appreciated, thank you.
When you're calling holder.nameCheckBox.setChecked(checked.get(position)); to configure the view to be displayed for this view, the listener is called while the tag still has the position of the previous checkbox.
Try removing the listener (setting it to null) before calling setChecked

Issue with custom gridview Android

I have developed an app in which I display data in Gridview. All data comes from local storage. I am able to display each data correctly and there are no issues with it. But when I have scroll the Gridview and goto the bottom and getback to Top,it changes position. And sometimes when I scroll down and getback to Top, a blank screen appears on screen;no data found at all!
So I thought that there is issue with getView(). I am unable to figure out the problem
Code of getView():
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
ViewHolder viewholder;
// LayoutInflater inflator = activit.getLayoutInflater();
if(convertView == null) {
// recycled view is null so create it.
viewholder = new ViewHolder();
convertView = inflator.inflate(R.layout.gridviewrow, null);
viewholder.imgvGridicon = (ImageView) convertView.findViewById(R.id.imgvGridicon);
viewholder.txtGridItemlabel = (TextView) convertView.findViewById(R.id.txtGridItemlabel);
convertView.setTag(viewholder);
} else {
viewholder = (ViewHolder) convertView.getTag();
}
if ((lstpinfo.get(position).appname.toString()) != null) {
viewholder.imgvGridicon.setImageDrawable((lstpinfo.get(position).icon));
viewholder.txtGridItemlabel.setText(lstpinfo.get(position).appname.toString());
}
return convertView;
}
Update::
Intitalize of inflater::
private LayoutInflater inflator;
private ArrayList<PInfo> lstpinfo = new ArrayList<PInfo>();
public GridViewAdapter(Context cntx, ArrayList<PInfo> lstpinfo) {
activit = cntx;
inflator = LayoutInflater.from(cntx);
this.lstpinfo = lstpinfo;
}
You have to set your view height with fixed value. There's a scrolling bug in android gridview with different heights. Please see this for reference.

GridView onItemClickListener never gets invoked

I have a GridView with custom View in it, which is a Button and a TextView. I defined the setOnItemClickListener but it looks like it never invoked, please see peaces of code below.
gridview = (GridView) findViewById(R.id.main_gridview);
gridview.setAdapter(new GridAdapter(this));
gridview.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(getApplicationContext(), "gadsfadsf",
Toast.LENGTH_SHORT).show();
Log.d("Main", "onItemClick");
}
});
The marked answer is kind of a hack. Instead of setting an onclicklistener to the button just ensure, that the ButtonView and the TextView has the following property:
android:clickable="false"
I had the same issue. While I've not yet figured out why it never gets invoked, I can propose a workaround.
Instead of setting the onClickListener on your GridView, set it on the Button itself inside your GridAdapter, inside your getView() method.
That worked for me!
It could be that some items in your GridView are stealing focus. Try adding these attributes to any elements you have inside the grid:
android:focusable="false"
android:focusableInTouchMode="false"
Instead of setting the onClickListener on your GridView,
set it on the Button itself inside your GridAdapter, inside your getView() method.
That worked for me!
I had the same problem, the event grid.itemClickListener was never launched.
In my case I had two listeners: grid.itemClickListener and another clickListener attached to a Button within the item's layout.
After fiddling with the layout for a while, I realized that if there was a widget, within the item's layout, with focusable=true, then itemClickListener was never launched. The clickListener attached to the Button worked well though.
Maybe that was your case. Anyway, I think this information might be useful to other users running into the same problem.
Thanx to CodingUser
what we were doing is directly accessing the Layout inside the GridView, so the onItemClickListener finds it confusing to access the item.
So the solution is to apply the onClickListener inside the Adapter (i.e. normally ArrayAdapter)
so what i m trying to say is:
public View getView(int position, View convertView, ViewGroup parent) {
//Here row is a view and we can set OnClickListener on this
final View row;
ViewHolder holder = null;
if (convertView == null) {
LayoutInflater inflater = ((Activity) context).getLayoutInflater();
//Here we inflate the layout to view (linear in my case)
row = inflater.inflate(layoutResourceId, parent, false);
holder = new ViewHolder();
holder.imageTitle = (TextView) row.findViewById(R.id.text);
holder.image = (ImageView) row.findViewById(R.id.image);
row.setTag(holder);
} else {
row = convertView;
holder = (ViewHolder) row.getTag();
}
ImageItem item = data.get(position);
holder.imageTitle.setText(item.getTitle());
holder.image.setImageBitmap(item.getImage());
//Now get the id or whatever needed
row.setId(position);
// Now set the onClickListener
row.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(context, "Clicked" + row.getId() + "!!",
Toast.LENGTH_SHORT).show();
}
});
return row;
}
You can set OnClick for view in Adapter of GridView .It work for me .
public View getView(final int position, View convertView, ViewGroup parent) {
ObjMenuVideo objListVideo = mListMenuVideo.get(position);
final ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
inflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.item_video_of_kind, null);
holder.tv_number_views = (TextView) convertView
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.tv_number_views.setText(String.valueOf(objListVideo.getViews()));
convertView.setId(position);
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Intent menuVideoIntent = new Intent(mContext,
ActivityDetailVideo.class);
mContext.startActivity(menuVideoIntent);
}
});
return convertView;
}

Categories