I'm facing a Problem, that my GridView "clips"/"glitches" after I scroll to the 4th row or longer than 2 seconds.
I got 58 Items, which get loaded via a Adapter into the GridView. An Item consists of a filename and an Image of the Item (Thumbnail). Each Thumbnail has a width and height of 100dp and is loaded into a ImageButton via the Framework "Glide" without resizing, crop or anything else. Simple Glide.load(ressource).into(imageButton).
Please see the attached images to follow my further explanation.
After Scrolling I would expect, that my Items are Aligned like the first 15-19 Items before. Unfortunately it is scrolling only the "last Item" of the 4th row from the GridView. That mean's that at Point 2 (red digit within the Picture) all the other items appearing for a short period if I scroll through them.
After scrolling further the whole GridView and Scrollbar get's "destroyed" and only a small amount of Item's appear and lastly 1 or none item's appear. I can see, that the Scrollbar is decreasing very fast, after scrolling.
GridView xml Properties (within main_activity.xml):
android:columnWidth="100dp"
android:gravity="center"
android:horizontalSpacing="5dp"
android:numColumns="auto_fit"
android:verticalSpacing="20dp"
android:visibility="visible"
GridViewAdapter Code:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
_layoutInflater = (LayoutInflater) _context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
if (convertView == null) {
_view = new View(_context);
_view = _layoutInflater.inflate(R.layout.single_item, null);
TextView textView = _view.findViewById(R.id.textView);
final Item item = _items.get(position);
textView.setText(item.getName());
ImageButton imageButton = _view.findViewById(R.id.imageButton);
Glide.with(_context).load(item.getDrawableRessource()).into(imageButton);
imageButton.setOnClickListener(click -> {
_iOnItemClickListener.onClick(item);
});
}
return _view;
}
Thanks for any helpful advice.
Try this:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
_view = convertView;
if (convertView == null) {
_layoutInflater = (LayoutInflater) _context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
_view = _layoutInflater.inflate(R.layout.single_item, null);
}
TextView textView = _view.findViewById(R.id.textView);
final Item item = _items.get(position);
textView.setText(item.getName());
ImageButton imageButton = _view.findViewById(R.id.imageButton);
Glide.with(_context).load(item.getDrawableRessource()).into(imageButton);
imageButton.setOnClickListener(click -> {
_iOnItemClickListener.onClick(item);
});
return _view;
}
Related
I have a custom layout for a Listview, each row item contains a button that when clicked it shows a small imageview in the item, however the actions i perform in one item, repeats for another item down the list, for example, if i click the button in item 1, the imageview will show up in item 1 and item 10, if i click the button in item 2, the Imageview will show up on item 2 and item 11 and while i scroll it will keep repeating in different items, heres the code for my custom adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
mparent = parent;
if (convertView == null) {
convertView = mInflater.inflate(R.layout.places_item, parent, false);
holder = new ViewHolder();
holder.placeimage = (CircularImageView) convertView.findViewById(R.id.locationimage_pilayout);
holder.addbtn = (TextView) convertView.findViewById(R.id.addbtn_pilayout);
holder.delbtn = (TextView) convertView.findViewById(R.id.delbtn_pilayout);
holder.oribtn = (TextView) convertView.findViewById(R.id.oribtn_pilayout);
holder.placename = (TextView) convertView.findViewById(R.id.locationname_pilayout);
holder.selected = (ImageView) convertView.findViewById(R.id.selected_pilayout);
holder.origin = (ImageView) convertView.findViewById(R.id.origin_pilayout);
holder.swipeLayout = (SwipeRevealLayout) convertView.findViewById(R.id.swipe_pilayout);
holder.mainLayout = (LinearLayout) convertView.findViewById(R.id.main_pilayout);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
final Place item = getItem(position);
/*
my code assigning the button click listener and assigning the views
*/
return convertView;
}
Am i missing something? im sure this could be a simple fix, but i havent found it yet. any help would be kindly appreciated.
In ListView the individual views for rows, get reused. For example, let's say the window can show up to 10 rows inside the ListView. Now when you scroll down, the 1st view gets out of the window from the top, and a new 11th view comes into the window from the bottom. In order to save memory and CPU power, Android uses the same 1st view for the 11th view. That's the purpose of convertView and the if (convertView == null) {} else {} code.
So the reason why the image is being shown in the 1st item and also in the 11th item, is that they are exactly one view object. To tackle this issue, in the getView() method, you need to reset every attribute of every view and don't rely on the defaults.
So adding a line like the one below, will get rid of the mentioned problem:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// all your code ...
holder.placeImage.setImageResource(0); //<-- This clears any previously set image.
return convertView;
}
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.
I have listview with hundred of items. Every item had a couple of LinearLayouts but ONE of them is Visibility.GONE! Every item has textviews and an image. On Image Click i want to set the LinearLayout with visibility.Gone to View.VISIBLE. It works fine until you scroll down the listview, then every 4th item has the same layout set to VISIBLE but i only need the Clicked one! Here is the getView method:
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ImageView imgForClick;
View vi = convertView;
if (convertView == null)
vi = inflater.inflate(R.layout.custom_row, null);
final LinearLayout hiddenLayout = (LinearLayout)vi.findViewById(R.id.hiddenLayout);
imgForClick = (ImageView)vi.findViewById(R.id.imageView3);
imgForClick.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
hiddenLayout.setVisibility(View.VISIBLE);
}
});
return vi;
}
That's because you are recycling the views, so the layout gets changed on a click and then that layout is used in your other rows to save memory.
You need to remember the state of each of the rows to know whether or not the layout should be visible or not
Have something like this:
public View getView(final int position, View convertView, ViewGroup parent) {
...
if (shouldBeVisible.get(position)) {
hiddenLayout.setVisibility(View.VISIBLE);
} else {
hiddenLayout.setVisibility(View.GONE);
}
That way the layout will always be set one way or another.
shouldBeVisible is a List of something that lets you know which rows should have that layout visible or not.
EDIT--
An alternative is to remove view recycling, however this will dramatically hurt performance and should NOT be done, but I'm just explaining to list all your options.
You would remove the line
if (convertView == null)
Making Android always inflate a new view, instead of using the recycled one when possible.
I have a ListView in which each row is a TextView, and display a line of text. I'm getting a problem where occasionally an unwanted empty row appears. The empty row goes away once list scrolls past that particular area.
I've verified my list rows contain the correct information by using the following code after pausing the app in the debugger. Nothing in the output shows up empty or null, etc.
for (int i = 0; i<list.getChildCount(); i++) {
System.out.print((TextView) list.getChildAt(i)).getText());
}
This shows the information I expected.
I also checked the data backing my Adapter for empty entries, new lines, etc.
My getView() method inside the Adapter is as follows:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView t;
if (convertView == null) {
convertView = mInflator.inflate(R.layout.single_message_row, null);
t = (TextView) convertView;
t.setMovementMethod(LinkMovementMethod.getInstance());
t.setTextSize(mMsgSize);
}
else {
t = (TextView) convertView;
}
CharSequence text = get(position);
t.setText(text);
return t;
}
Below is an image demonstrating the problem (the area in red):
Try after changing getView method as:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
if(row==null){
LayoutInflater inflater=getLayoutInflater();
row=inflater.inflate(R.layout.single_message_row, parent, false);
}
TextView t=(TextView)row.findViewById(R.id.yourtextview);
t.setText("position "+position);
t.setMovementMethod(LinkMovementMethod.getInstance());
t.setTextSize(mMsgSize);
CharSequence text = get(position);
t.setText(text);
return row;
}
It seems the problem was caused by using match_parent for my TextView width in the ListView. Changing it to wrap_content seems to have fixed it.
For an unwanted empty list item occurring in the ListView, I tried this:
List<String> listofItems;
String strlist=listofItems.get(position);
if(strlist.isEmpty())
{
remove(strlist);
}
'position' is what i got as a parameter in View getView method, because i was implementing a custom adapter.
it worked fine for me!
Each item in my gallery is a custom view. One of the child's view is a gridView. When I'm scrolling the gallery everything works fine, but it wont scroll by touching the gridView. Its difficult to explain, I hope someone will understand me!
Touching and scrolling any part of the custom view suppose to trigger the scrolling? Or maybe only the imageView will trigger the scrolling?
public View getView(int position, View convertView, ViewGroup parent) {
final SubProduct subProduct=subProducts.get(position);
int quantity=subProduct.getQuantity();
int size=subProduct.getSizes().get(0).getWidth();
String productName=subProduct.getProductName();
int productPrice=subProduct.getSizes().get(0).getPrice();
int columnWidth = 0;
View view=convertView;
if (view==null){
holder=new SubProductHolder();
//The main container
holder.myLinearLayout= new LinearLayout(this.myContext);
holder.myLinearLayout.setOrientation(1);//vertical
//The custome button
LayoutInflater inflater = (LayoutInflater)
myContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v=(View)inflater.inflate(R.layout.custom_sub_product_layout, null);
LinearLayout btnLayout = (LinearLayout)v.findViewById(R.id.customSunProductButtonLayout);
LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(PRODUCT_BOTTUN_WIDTH,PRODUCT_BOTTUN_HEIGHT);
btnLayout.setLayoutParams(llp);
tvSubProductDescription=(TextView)v.findViewById(R.id.tvSubProductDescription);
tvSubProductPrice=(TextView) v.findViewById(R.id.tvSubProductPrice);
holder.btnProduct=btnLayout;
holder.btnProduct.setGravity(Gravity.CENTER);
holder.TvLinearLayout= new LinearLayout(this.myContext);
holder.TvLinearLayout.addView(holder.btnProduct);
//frame
holder.image = new ImageView(this.myContext);
holder.image.setBackgroundColor(Color.WHITE);
LinearLayout.LayoutParams llp2 = new LinearLayout.LayoutParams(FRAME_WIDTH,FRAME_WIDTH);
holder.image.setLayoutParams(llp2);
//grid
holder.grid=new GridView(myContext);
//the number of rows is the round number of quantity sqaure
int numberOfColomns=(int) Math.floor((int) Math.sqrt(quantity));
holder.grid.setNumColumns(numberOfColomns);
holder.grid.setLayoutParams(new RelativeLayout.LayoutParams(PRODUCT_PICTURE_WIDTH,PRODUCT_PICTURE_WIDTH));
}else{
holder=(SubProductHolder)view.getTag();
}
//set text in description tv
tvSubProductDescription.setText(quantity+" "+productName+" "+size+"X"+size);
tvSubProductPrice.setText("$"+productPrice);
//set the grid
holder.grid.setAdapter(new emptySquaresAdapter(quantity,myContext,columnWidth,columnHeight));
holder.relativeGridLayout=new RelativeLayout(myContext);
holder.relativeGridLayout.addView(holder.image);
holder.relativeGridLayout.addView(holder.grid);
//set button and picture to layout
holder.myLinearLayout.addView(holder.relativeGridLayout);
holder.myLinearLayout.addView(holder.TvLinearLayout);
return holder.myLinearLayout;
}
static class SubProductHolder
{
ImageView image;
GridView grid;
RelativeLayout relativeGridLayout;
LinearLayout btnProduct;
LinearLayout TvLinearLayout;
LinearLayout myLinearLayout;
}
You have to use TouchListeners for galleryView since on clicking on its child view will not trigger scroll for galleryView
You have to use TouchListeners. Also, you're better off going with a ViewPager and using fragments. To my knowledge, Gallery is deprecated.