Image resets to default after scrolling - Android Custom View - android

So I've been creating a custom view similar to GridView. It loads and scrolls through images just fine when they are resources inside the app but now that I'm using images coming in through an HTTP request, the images aren't loading correctly.
When the app starts: all images are set to the default (bad)
After scrolling past that cell and immediately scrolling back to it: image loads correctly (good)
After scrolling back to that same cell sometime later: image was set back to the default (bad)
Does anyone have any ideas of what could be causing this error? I assume it's some kind of recycling issue but I haven't been able to fix it.
Here is my xml file:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:paddingBottom="1dip"
android:background="#color/white"
android:id="#+id/highlight_counter_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:gravity="center" >
<ImageView
android:id="#+id/catalog_image"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<View
android:id="#+id/solid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#android:color/black" />
<View
android:id="#+id/text_gradient"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#id/solid"
android:background="#drawable/highlight_text_gradient" />
<TextView
android:id="#+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:padding="#dimen/highlight_text_padding"
android:ellipsize="end"
android:gravity="bottom"
android:maxLines="2"
android:textColor="#color/white"
android:textSize="#dimen/text_large" />
</RelativeLayout>
Here is an excerpt from my adapter (where I think the problem probably lies):
#Override
public View getView(int position, View convertView, ViewGroup parent) {
final LayoutParams lp;
int viewType = getItemViewType(position);
ImageView img;
PulseTextView title;
Resources res = getContext().getResources();
int height = (int) res.getDimension(R.dimen.icon_main_size);
if (convertView == null) {
LayoutInflater inflater = (LayoutInflater) mContext.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.element_item, parent, false)
LayoutParams layp = new LayoutParams(height);
convertView.setLayoutParams(layp);
}
img = ViewHolder.get(convertView,R.id.catalog_image);
title = ViewHolder.get(convertView,R.id.title);
final CatalogItem channel = getCatalogItem(position);
// find the url of the associated image then set image
String url = null;
try {
url = mCatalogHandler.getImageUrl(CatalogHandler.VALUE_ICON, channel.mPrimaryKey, 100, 100);
} catch (CatalogException e) {
e.printStackTrace();
}
if (url == null || TextUtils.isEmpty(url) || url.equals("null")) {
img.setImageBitmap(mDefaultPic);
} else {
// downloads the image to img
mImageDownloader.download(url, img, mDefaultPic, false);
}
title.setText(channel.mDomain);
img.setScaleType(ScaleType.FIT_XY);
img.setTag(RAMImageCache.KEY_URL, url);
// set the gradient behind the text
View grad = convertView.findViewById(R.id.text_gradient);
ViewUtils.setHeight(grad, height * 3 / 5);
grad.getBackground().setDither(true);
View solid = convertView.findViewById(R.id.solid);
ViewUtils.setHeight(solid, height / 5);
// set the padding based on the position on the screen
DisplayMetrics displaymetrics = new DisplayMetrics();
((Activity)getContext()).getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int width = displaymetrics.widthPixels;
if (convertView.getRight() == width && convertView.getLeft() == 0) {
convertView.setPadding(0, 0, 0, 1);
} else if (convertView.getRight() == width) {
//convertView.setPadding(1, 0, 0, 1);
convertView.setPadding(0, 0, 1, 1);
} else if (convertView.getLeft() == 0) {
//convertView.setPadding(0, 0, 1, 1);
convertView.setPadding(1, 0, 0, 1);
}
// set the onclicklistener to jump to the next fragment
convertView.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
Bundle bundle = new Bundle();
bundle.putString("channelitem", channel.getMetadata().toString());
ChannelFragment fragment = new ChannelFragment();
fragment.setArguments(bundle);
((PulseFragmentActivity)mContext.get()).openFragment(fragment);
}
});
return convertView;
}
static class ViewHolder {
ImageView img;
TextView title;
public ViewHolder(ImageView i, PulseTextView t) {
img = i;
title = t;
}
public static <T extends View> T get(View view, int id) {
SparseArray<View> viewHolder = (SparseArray<View>) view.getTag();
if (viewHolder == null) {
viewHolder = new SparseArray<View>();
view.setTag(viewHolder);
}
View childView = viewHolder.get(id);
if (childView == null) {
childView = view.findViewById(id);
viewHolder.put(id, childView);
}
return (T) childView;
}
}
Any point in the right direction would help greatly! Let me know if there are any other code snippets you would need to see.

I suggest one way to debug is to try another image download library to find out if the error is in your code. I used https://github.com/koush/UrlImageViewHelper and it works well when cell is reused, and its API is similar to what you used now

The behaviour very much depends on what and how does your mImageDownloader handles the downloaded image.
In most cases LRU Cache implementation might be used to store your downloaded image and this cache has a maximum value of bytes assigned. Once your cached image exceeded this value, the old bitmaps will be discarded, hence why you are seeing default image and you need to re-download it.
My suggestion is after you have downloaded the image, scale it as small as you can, so that you can cache as much bitmaps as you could.
There is no way Android could cache up all your downloaded images due to memory restriction.

Related

Is TextView Ellipsize in recycleView

I have problem to check, if text view been ellipsized. I defined layout for item in recycle view and I have to check, if text was ellipsized and hide button if yes. I found solution, where can I can get layout from text view and check if it was ellipsized, but in bind method in recycle view it always return false. Do you have someone some idea, how I can do it?
Layout l = textview.getLayout();
if (l != null) {
int lines = l.getLineCount();
if (lines > 0)
if (l.getEllipsisCount(lines-1) > 0)
Log.d(TAG, "Text is ellipsized");
}
This code is not working for me.
try this in your adapter
holder.textView.post(new Runnable() {
#Override
public void run() {
if (holder.textView.getLayout() != null) {
if (widthText == 0) {
widthText = holder.textView.getWidth();
}
boolean isEllipsize = !holder.textView.getText().toString().equalsIgnoreCase(holder.textView.getLayout().getText().toString());
} else {
Paint paint = new Paint();
paint.setTextSize(holder.textView.getTextSize());
final float size = paint.measureText(holder.textView.getText().toString());
boolean isEllipsize = (int) (size / maxLine) > widthText;
}
}
});
if you want to read more if text too long you can use this libs:
https://github.com/bravoborja/ReadMoreTextView
As stated in another SO post:
This only works after the layout phase, otherwise the returned layout will be null, so call this at an appropriate place in your code.
Make sure this is called after the text had been laid out (After onCreate)!
Try this,
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:lines="1" />

Resizing ListView Rows Dynamically

Overview:
I have a ListView of different types of rows including images and text of varying heights. At runtime, I am trying to dynamically set the heights of the layout containing the image based on the width of the row. So the row's width is set to match_parent (because we want it to fill the width of the device), but the height must be calculated at runtime to get a specific aspect ratio for the frame.
Here's the code snippet from the Adapter that I'm using:
#Override
public View getView(View convertView) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.chat_row_image, null);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
ViewTreeObserver observer = holder.videoFrame.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
frameWidth = holder.container.getWidth();
int width = frameWidth;
int height = (int) (frameWidth / Utilities.MEDIA_CELL_ASPECT_RATIO);
Log.d(TAG, "Calculated : " + width + "," + height);
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(width, height);
holder.messageImage.setLayoutParams(params);
holder.videoFrame.getViewTreeObserver().removeGlobalOnLayoutListener(
this);
Log.d(TAG, "Imageview : " + holder.messageImage.getWidth() + "," + holder.messageImage.getHeight());
}
});
return convertView;
}
The problem: this code works well but during scrolling through the ListView, there are UI glitches that are readily noticeable. The main UI bug is that the rows have a pre-defined height from the XML file and are instantly transitioning to the new height we've calculated once the image finishes loading. This causes the entirety of contents below that row to suddenly shift as soon as the row appears on the screen. Also, we're scrolling from the bottom to the top (this is a chat thread).
Things I've tried:
We tried scrolling through the entire ListView via "setSelection" method (from ListView) to "preload" the contents before the user sees them. That didn't work and it doesn't seem like the images are loaded during the brief time we're scrolling past them.
We've tried setting a static XML file width and height, and that fixes the issue, but doesn't account for different screen sizes.
What I need help with: We want to keep the scrolling smooth even while resizing the layout dynamically. The final resort would be to create different XML files for each screen size category and hard code the height, but that would not keep our frame's aspect ratio as we desire.
Please let me know if there are any clarifications you would want. Thanks in advance!
The answer was to set the dimensions of the frame in the constructor of the Adapter instead of the getView() method. Based on Nannuo Lei's suggestion, we are not using the ViewTreeObserver.OnGlobalLayoutListener(), instead we are calculating the dimensions of the frame based on the display's width and padding/margin of other layout elements.
public ImageChatRow(Context context) {
this.mContext = context;
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
displayWidth = size.x - 40; //padding is 40 in our case
displayHeight = (int) (displayWidth / Utilities.MEDIA_CELL_ASPECT_RATIO);
}
Then in the getView() method, we just set the dimensions of the frame directly after inflating the view.
public View getView(View convertView) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.chat_row, null);
holder = new ViewHolder(convertView);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(displayWidth, displayHeight);
layoutParams.gravity = Gravity.CENTER;
holder.frame.setLayoutParams(layoutParams);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
This solves the problems of the glitches in loading rows and dynamically setting their heights. We haven't observed any jumpiness in the ScrollView since implementing this code!
You can add a LinearLayout outside in chat_row_image.xml, like this
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
android:id="#+id/layout_ll
android:layout_width="wrap_content"
android:layout_height="wrap_content">
[...]
</LinearLayout>
</RelativeLayout>
In Adapter.java
#Override
public View getView(View convertView) {
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(
R.layout.chat_row_image, null);
holder = new ViewHolder(convertView);
LinearLayout ll = (LinearLayout) view.findViewById(R.id.layout_ll);
LayoutParams linearParams = (LayoutParams) ll.getLayoutParams();
linearParams.width = width;
linearParams.height = height;
ll.setLayoutParams(linearParams);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
return convertView;
}

How to keep ViewHolder's width after view was recycled

I'm using RecyclerView as horizontal list to show my images.
If I scroll to the fifth picture, the first two or three are recycled and ViewHolder loses its width. If I scroll back to the first image, the images are loaded again and that leads to jumps while scrolling.
Here is R.layout.fragment_details_view_img_item
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/details_view_img_item"
android:background="#color/red"
android:adjustViewBounds="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ImageView>
My ViewHolder and Adapter:
private class ViewHolder extends RecyclerView.ViewHolder {
ImageView img;
public ViewHolder(ImageView imgV){
super(imgV);
img = imgV;
}
}
private class ImageListAdapter extends RecyclerView.Adapter<ViewHolder> {
[...]
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int position) {
View v = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_details_view_img_item, parent, false);
v.setOnClickListener(listener);
logDebug("onCreateViewHolder");
return new ViewHolder((ImageView) v);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
logDebug("onBindViewHolder");
ImageItem item = data.get(i);
if (item != null) {
ImageView imgView = viewHolder.img;
imgView.setTag(item);
String imgurl = ImageUtil.imgUrlForAvailableHeightInPX(item, parentHeight);
ImageLoader.instance().loadASYNC(imgurl, imgView);
}
}
#Override
public void onViewRecycled(ViewHolder holder) {
super.onViewRecycled(holder);
logDebug("onViewRecycled: " + holder.img.getTag());
}
}
So how can I keep ViewHolder's width?
Before I start to approach this problem, one thing needs to be clear. The ViewHolder doesn't have any width, the ImageView it "holds" does have width, and that's what you're trying to control.
Now, considering this, your issue is (after making certain assumptions from looking at your code) that you need to know the width of a certain image when it is at a certain given height and while maintaining aspect ratio - before it arrives from the server.
This is a tricky one.
One option would be to preload all your images. This, however, is very costly with memory and could lead to memory crashes.
A better option would be to load all the images' details, without actually downloading the images' pixels. Then, you'll need to remember the aspect ratio of all the images in some cache, and set the dimension of the image you're loading prior to actually downloading the image contents.
To download an image's dimensions without downloading the image itself, you should use something like this:
public float getImageAspectRatio(InputStream inputStreamFromServer)
{
BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
decodeOptions.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inputStreamFromServer, null, decodeOptions);
final float imageDesiredWidth = decodeOptions.outWidth;
final float imageDesiredHeight = decodeOptions.outHeight;
return imageDesiredWidth / imageDesiredHeight;
}
Once you have this method, you'll need to preload all your images using this function:
private float[] mAspectRatios;
public void decodeAllAspectRatios(List<String> imageUrls)
{
mAspectRatios = new float[imageUrls.size()];
InputStream inputStream;
int index = 0;
for (String url : imageUrls)
{
// Get the input stream from the image url using whatever method you use.
mAspectRatios[index] = getImageAspectRatio(inputStream);
index++;
}
}
Important: Your RecyclerView should not begin working until this method finished working.
Once you have preloaded all your aspect ratios, we go back to your ViewHolder:
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
logDebug("onBindViewHolder");
ImageItem item = data.get(i);
if (item != null) {
ImageView imgView = viewHolder.img;
// set the layout params of the image, making it fit the correct size prior to loading the bitmap.
imgView.setLayoutParams(new LayoutParams(parentHeight * mAspectRatios[i], parentHeight));
imgView.setTag(item);
String imgurl = ImageUtil.imgUrlForAvailableHeightInPX(item, parentHeight);
ImageLoader.instance().loadASYNC(imgurl, imgView);
}
}
So long as you want your RecyclerView to display varying width images depending on their aspect ratio, I believe this is your best option.

Setting row height in a ListView

I'm trying to programatically set the height of rows in a ListView based off of some scaling factor. I've been looking around the internet for hours and cannot figure out why my code doesn't work.
UPDATE
Actually more things with the layout are having problems than just the height. The layout_weight in the XML file for all the TextViews doesn't do anything at all. Setting the textSize on the TextViews programatically doesn't work either, the text is bigger than I set it. But setting visibility on the TextViews works perfectly fine as well as setting the text. Another thing to mention is that the only thing I can think of that separates this from any normal situation in which this code would work is that I'm putting this ListView in a custom ViewGroup.
Here's my adapter code for getView() where I'm trying to set the height.
public View getView(int position, View convertView, ViewGroup parent) {
View layout = convertView;
if (layout == null) {
LayoutInflater inflater = activity.getLayoutInflater();
layout = inflater.inflate(R.layout.listview_row_parameter, parent, false);
}
int rowHeight = (int) (defaultRowHeight * scale);
layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, rowHeight));
layout.requestLayout();
Parameter p = getItem(position);
TextView tvName = (TextView) layout.findViewById(R.id.tvName);
TextView tvValue = (TextView) layout.findViewById(R.id.tvValue);
TextView tvMin = (TextView) layout.findViewById(R.id.tvMin);
TextView tvMax = (TextView) layout.findViewById(R.id.tvMax);
TextView tvAvg = (TextView) layout.findViewById(R.id.tvAvg);
if (showName) {
tvName.setVisibility(View.VISIBLE);
tvName.setTextSize(defaultTextSize * scale);
tvName.setText(p.getName());
} else {
tvName.setVisibility(View.GONE);
}
if (showValue) {
tvValue.setVisibility(View.VISIBLE);
tvValue.setTextSize(defaultTextSize * scale);
tvValue.setText(formatValue(p.getCurrentValue().value, p));
} else {
tvValue.setVisibility(View.GONE);
}
if (showMin) {
tvMin.setVisibility(View.VISIBLE);
tvMin.setTextSize(defaultTextSize * scale);
tvMin.setText(formatValue(p.getMinValue().value, p));
} else {
tvMin.setVisibility(View.GONE);
}
if (showMax) {
tvMax.setVisibility(View.VISIBLE);
tvMax.setTextSize(defaultTextSize * scale);
tvMax.setText(formatValue(p.getMaxValue().value, p));
} else {
tvMax.setVisibility(View.GONE);
}
if (showAvg) {
tvAvg.setVisibility(View.VISIBLE);
tvAvg.setTextSize(defaultTextSize * scale);
tvAvg.setText(formatValue(p.getAverage(), p));
} else {
tvAvg.setVisibility(View.GONE);
}
return layout;
}
And here's my XML
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<TextView
android:id="#+id/tvName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:textColor="#android:color/white"
android:singleLine="true" />
<!-- More TextViews here -->
The rows all show up just fine with the expected content, but the height isn't correct.

How to refresh a GridView?

I have a GridView which is pretty similar to the Google tutorial, except that I want to add the ImageViews on runtime (via a subactivity). The results are okay, but the layout of the View is messed up: The GridView doesn't fill the content of its parent, what do I have to do to design it properly?
Here the code of adding the children:
public void initializeWorkbench(GridView gv, Vector<String> items) {
Prototype.workbench.setDimension(screenWidth, divider.height()+workbenchArea.height());
Prototype.workbench.activateWorkbench();
// this measures the workbench correctly
Log.d(Prototype.TAG, "workbench width: "+Prototype.workbench.getMeasuredWidth());
// 320
Log.d(Prototype.TAG, "workbench height: "+Prototype.workbench.getMeasuredHeight());
// 30
ImageAdapter imgAdapter = new ImageAdapter(this.getContext(), items);
gv.setAdapter(imgAdapter);
gv.measure(screenWidth, screenHeight);
gv.requestLayout();
gv.forceLayout();
Log.d(Prototype.TAG, "gv width: "+gv.getMeasuredWidth());
// 22
Log.d(Prototype.TAG, "gv height: "+gv.getMeasuredHeight());
// 119
Prototype.workbench.setDimension(screenWidth, divider.height()+workbenchArea.height());
}
}
activateWorkbench, setDimension and measure in the workbench (LinearLayout above the GridView):
public void activateWorkbench() {
if(this.equals(Prototype.workbench)) {
this.setOrientation(VERTICAL);
show = true;
measure();
}
}
public void setDimension(int w, int h) {
width = w;
height = h;
this.setLayoutParams(new LinearLayout.LayoutParams(width, height));
this.invalidate();
}
private void measure() {
if (this.getOrientation() == LinearLayout.VERTICAL) {
int h = 0;
int w = 0;
this.measureChildren(0, 0);
for (int i = 0; i < this.getChildCount(); i++) {
View v = this.getChildAt(i);
h += v.getMeasuredHeight();
w = (w < v.getMeasuredWidth()) ? v.getMeasuredWidth() : w;
}
if (this.equals(Prototype.tagarea))
height = (h < height) ? height : h;
if (this.equals(Prototype.tagarea))
width = (w < width) ? width : w;
}
this.setMeasuredDimension(width, height);
}
The ImageAdapter constructor:
public ImageAdapter(Context c, Vector<String> items) {
mContext = c;
boolean mExternalStorageAvailable = false;
boolean mExternalStorageWriteable = false;
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.equals(state)) {
// We can read and write the media
mExternalStorageAvailable = mExternalStorageWriteable = true;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
// We can only read the media
mExternalStorageAvailable = true;
mExternalStorageWriteable = false;
} else {
// Something else is wrong. It may be one of many other states, but
// all we need
// to know is we can neither read nor write
mExternalStorageAvailable = mExternalStorageWriteable = false;
}
if (mExternalStorageAvailable && mExternalStorageWriteable) {
for (String item : items) {
File f = new File(item);
if (f.exists()) {
try {
FileInputStream fis = new FileInputStream(f);
Bitmap b = BitmapFactory.decodeStream(fis);
bitmaps.add(b);
files.add(f);
} catch (FileNotFoundException e) {
Log.e(Prototype.TAG, "", e);
}
}
}
}
}
And the xml layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="bottom"
android:paddingLeft="0px"
android:paddingTop="0px"
android:paddingRight="0px">
<com.unimelb.pt3.ui.TransparentPanel
android:id="#+id/workbench"
android:layout_width="fill_parent"
android:layout_height="10px"
android:paddingTop="0px"
android:paddingLeft="0px"
android:paddingBottom="0px"
android:paddingRight="0px">
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center" />
</com.unimelb.pt3.ui.TransparentPanel>
</LinearLayout>
the GridView has an invalidateViews() method.
when you call this method: "all the views to be rebuilt and redrawn."
http://developer.android.com/reference/android/widget/GridView.html
i think this is what you need:)
You must, first tell the adapter to notify that the data has changed and set the adapter again to the grid
adapter.notifyDataChanged();
grid.setAdapter(adapter);
This may be helpful.
I refresh a gridview of book image thumbnails after a delete is executed on an item.
Using adapter.notifyDataChanged(); as mentioned above didn't work for me as it's called in my adapter.
//this is a call that retrieves cached data.
//your constructor can be designed and used without it.
final Object data = getLastNonConfigurationInstance();
I essentially just reload the adapter and bind it to the same view.
//reload the adapter
adapter = new BooksAdapter(MyBooks.this, MyBooks.this, data, show_collection );
grid.invalidateViews();
grid.setAdapter(adapter);
#flyerz #snagnever
Together you guys have got it. It should be:
adapter.notifyDataChanged();
grid.invalidateViews();
This will flag the adapter that its data has changed, which will then be propagated to the grid whenever after the invalidateViews() method is called.
Glad I found this question because I could not figure out how to add items to the grid after its been rendered.
None of these answers actually worked for me and I had to mash all of them together. To actually get the GridView to update, you need to do this:
adapter.notifyDataChanged();
grid.invalidateViews();
grid.setAdapter(adapter);
Hope this helps anyone who couldn't get the other solutions to work.
You should not call invalidateViews and setAdapter to refresh your grid view. This is not a good idea to keep your grid view updated, if you update in that way it would cost a lot of time and memory.
If you have a chance to look at getView method you will see that convertView is created just once. When you call notifyDataChanged, it will update this view. Whereas if you call invalidateViews, previously created views will be recreated. This is not a good solution.
Your getView method is called when you call notifyDataChanged. So your getView method should look something like the code below.
public List list;
public class SimpleItemViewHolder extends Object
{
public TextView textView;
public ImageView imageView;
}
public View getView(int position, View convertView, ViewGroup parent){
View itemView = convertView;
SimpleItemViewHolder viewHolder;
if(convertView==null)
{
viewHolder = (SimpleItemViewHolder)itemView.getTag();
}else{
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
itemView = inflater.inflate(R.layout.layout_name, null);
TextView labelField = (TextView) itemView.findViewById(R.id.label_field);
labelField.setText(list.get(position).Name);
//Typeface boldFont = Typeface.createFromAsset(context.getAssets(), "fonts/Font-Bold.ttf");
//labelField.setTypeface(boldFont);
ImageView imageView = (ImageView) itemView.findViewById(R.id.image_view);
//Bitmap bitmap = init your bitmap here;
//imageView.setImageBitmap(bitmap);
viewHolder = new SimpleItemViewHolder();
viewHolder.imageView = imageView;
viewHolder.textView = labelField;
itemView.setTag(viewHolder);
}
//don't create new views, instead use previous ones and update them.
viewHolder.textView.setText(list.get(position).Name);
//viewHolder.imageView.setImageBitmap(your_bitmap);
return itemView;
}
adapter.notifyDataChanged();
may not works just because data for the adapter is stale (if Activity or fragment was not destroyed and have been sitting in the back stack for instance).
So, if firstly refresh data, then after it will be working.
You are placing the GridView inside com.unimelb.pt3.ui.TransparentPanel which is 10px tall.
You shouldn't use px, you should use dp.
Change com.unimelb.pt3.ui.TransparentPanel's android:layout_height="10px" to android:layout_height="fill_parent"
In your adapter :
class MAdapter extends BaseAdapter{
List<Objects> mObjects;
...
public void clearAdapter(){
mObjects.clear();
}
public void addNewValues(List<Objects> mObjects){
this.mObjects = mObjects;
}
...
}
adapter.clearAdapter(); // clear old values
adapter.addNewValues(MObjects);
adapter.notifyDataChanged();

Categories

Resources