Conditionally changing list adapter android - java

I'm working on an app for Android using the Facebook SDK. So far, the app scans a certain pages feed and puts all the posts into a GridView. I want it so that if the value of the imageArray at that current position is empty or null (A post without a picture), then it instead sets the ImageView visibility to GONE and makes the TextView take up the entire item. Here's my code:
public View getView(int position, View v, ViewGroup parent) {
MainListHolder mHolder;
if (v == null) {
mHolder = new MainListHolder();
v = inflator.inflate(R.layout.list_item, null);
mHolder.img1 = (ImageView) v.findViewById(R.id.preview);
mHolder.txt2 = (TextView) v.findViewById(R.id.message);
mHolder.txt3 = (TextView) v.findViewById(R.id.user_name);
mHolder.txt4 = (TextView) v.findViewById(R.id.user_id);
v.setTag(mHolder);
} else {
mHolder = (MainListHolder) v.getTag();
}
if (!imageArray.get(position).isEmpty()) {
manager.DisplayImage(imageArray.get(position), loader, mHolder.img1);
}
else {
manager.DisplayImage(null, loader, mHolder.img1);
mHolder.img1.setVisibility(ImageView.GONE);
}
mHolder.txt2.setText(messageArray.get(position));
mHolder.txt3.setText(nameArray.get(position));
mHolder.txt4.setText(idArray.get(position));
return v;
}
The problem is the GridView now changes its items while I'm scrolling. Even if the item has a picture, the ImageView disappears. How do I fix this?

Your problem is in this part of code:
if (!imageArray.get(position).isEmpty()) {
manager.DisplayImage(imageArray.get(position), loader, mHolder.img1);
}
else {
manager.DisplayImage(null, loader, mHolder.img1);
mHolder.img1.setVisibility(ImageView.GONE);
}
So if image for imageView doesn't exists you make img1 GONE. But if image exists img1 still remains GONE after scrolling, because you are not calling mHolder.img1.setVisibility(ImageView.VISIBLE). And this happens because when you are scrolling, your listItem views are recycling, so in adapter you have to treat ALWAYS both conditions.
So your code shold look like this:
if (!imageArray.get(position).isEmpty()) {
mHolder.img1.setVisibility(ImageView.VISIBLE);
manager.DisplayImage(imageArray.get(position), loader, mHolder.img1);
}
else {
manager.DisplayImage(null, loader, mHolder.img1);
mHolder.img1.setVisibility(ImageView.GONE);
}
Example for better understanding of problem:
Imagine this code in list adapter getView() mehod:
if(isEnabled) {
textView.setText("ENABLED");
}
In this case textView will always show text ENABLED after scrolling, because there is no else part where textview will show some other text.
So code should look like:
if(isEnabled)
textView.setText("ENABLED");
else
textView.setText("DISABLED");
Hope it will help you.

Related

Recyclerview : how to mix some post with image and no image? [duplicate]

This question already has answers here:
How to create RecyclerView with multiple view types
(23 answers)
Closed 3 years ago.
I want to display in same recycle-view which is some post have image and some post does not have images.
I can retrieve all the post with image and non-image,
but i want to change the size of the post when the user only post text(no image).
I expect the output like twitter feed..some post with image and without image have their own size.
Simple way to achieve this scenario is, All you have to do is create a view with both image and text, in recycler adapter check if image data is available make visibility of image visible else Image visibility gone.
Second Approach for this to make multiple view for RecyclerView.
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder called");
ContentItem item = mContentItems.get(position);
if(item.getName()!=null){
holder.textName.setVisibility(View.Visible);
holder.textName.setText(item.getName());
}else{
holder.textName.setVisibility(View.GONE);
}
if(item.getPreviewImageDefault()!=null){
holder.imageIcon.setVisibility(View.Visible)
Picasso.with(mContext).load("file://" + item.getPreviewImageDefault()).into(holder.imageIcon);
}else{
holder.imageIcon.setVisibility(View.GONE)
}
}
Another possible solution is create 2 xml layouts and use ViewType in your RecyclerView.
look this How to create RecyclerView with multiple view type?
If you want to hide the image when it is ic_launcher you could do that (suppposing that data.getImage() returns the id of the drawable as integer):
#Override
public void onBindViewHolder(ViewHolder viewHolder, int i) {
if(mItems!=null){
AdapterData data = mItems.get(i);
viewHolder.text.setText(data.getText());
viewHolder.image.setImageResource(data.getImage());
if(TextUtils.isEmpty(data.getText())){
viewHolder.text.setVisibility(View.GONE);
}else{
viewHolder.text.setVisibility(View.VISIBLE);
}
if(data.getImage()==R.drawable.ic_launcher){
viewHolder.image.setVisibility(View.GONE);
}else{
viewHolder.image.setVisibility(View.VISIBLE);
}
}
}
One possible solution, like some people have already said, is to hide/show the ImageView.
You could do that in the ViewHolder that you use for your RecyclerView.
class OptionalImageViewHolder extends RecyclerView.ViewHolder {
private ImageView image;
private TextView text;
// any other views you have
public OptionalImageViewHolder(View itemView) {
super(itemView);
image = itemView.findViewById(R.id.yourImageViewIdHere);
text = itemView.findViewById(R.id.yourTextViewIdHere);
// same for any other views you have
}
public void bindView(Tweet tweet) {
// This is where the magic happens
// Note: I make the assumption that you have a class called "Tweet"
// that has a field for "text", a field for "image" (that can be
// null if there's no image), and any other necessary fields.
text.setText(tweet.getTweetText());
if (tweet.hasImage() /* function that returns whether or not there is an image */) {
image.setVisibility(View.VISIBLE);
image.setImageBitmap(tweet.getImage()); // or however you are setting the image
} else {
// else just make the image invisible
image.setVisibility(View.GONE);
}
}
}
Hopefully this gives you an idea.
RecyclerView supports different viewTypes (layouts) which is the proper way in such scenario. E.g.,
class MyAdapter : RecyclerView.Adapter<MyViewHolder>() {
override fun getViewTypes(position:Int) =
if (mydata[position].hasImage) return R.layout.mylayout_with_image
else R.layout.mylayout_no_image;
override fun onCreateViewHolder(viewType:Int, parent:ViewGroup) : MyViewHolder =
// here viewType = layout id
MyViewHolder(layoutInflater.inflate(viewType, parent))
override fun onBindViewHolder(viewHolder:MyViewHolder, position:Int) {
// guaranteed viewHolder.itemView is the view you want for that position
}
}

Android GridView with Picasso display image wrongly [duplicate]

I am having a problem loading images in ListView from a server with Picasso.
I have a BaseAdapter that is used to fill my ListView. In this ListView some items have an image and some not.
in this method:
public View getView(final int position, View convertView, ViewGroup parent) {
I do:
...
//context = Activity context;
//context1 = Context context1;
context1 = context.getApplicationContext();
if (!photo[position].equals("")) {
String stringurl = "http://www.blablabla.it/img/"+photo[position]+".jpg";
Picasso.with(context1)
.load(stringurl)
.placeholder(R.drawable.white)
.into(holder.imageD);
}
else {
holder.imageD.setImageBitmap(null);
}
This code work, but too often I see that an image is in a different place than where it belongs!
as you can imagine this is very annoying for users .. Thanks everybody
You had faced this problem becuase ListView recycle items view + Picasso calls are asynchronous ... How it can appears?
You start loading with Picasso
View is reused (convertView != null)
You are setting holder.imageD.setImageBitmap(null);
Asynchronous from point 1. is finnished
Thats why you are having wrong image loaded ...
To avoid such behaviour you need to inform Picasso loader to cancel previous request.
So instead just setting image bitmap to null you have to set it via Picasso library (in else statment use):
Picasso.with(context1).load(null).placeholder(R.drawable.white).into(holder.imageD);
edit: following #Budius comments: even better solution will be cancel and set instead like:
{
Picasso.with(context1).cancelRequest(holder.imageD);
//holder.imageD.setImageBitmap(null); //or
holder.imageD.setImageResource(R.drawable.white); //depends on your needs
}
This should be more efficent way as it should creates less internal objects on every getView calls.

BaseAdapter and Picasso issue

I am having a problem loading images in ListView from a server with Picasso.
I have a BaseAdapter that is used to fill my ListView. In this ListView some items have an image and some not.
in this method:
public View getView(final int position, View convertView, ViewGroup parent) {
I do:
...
//context = Activity context;
//context1 = Context context1;
context1 = context.getApplicationContext();
if (!photo[position].equals("")) {
String stringurl = "http://www.blablabla.it/img/"+photo[position]+".jpg";
Picasso.with(context1)
.load(stringurl)
.placeholder(R.drawable.white)
.into(holder.imageD);
}
else {
holder.imageD.setImageBitmap(null);
}
This code work, but too often I see that an image is in a different place than where it belongs!
as you can imagine this is very annoying for users .. Thanks everybody
You had faced this problem becuase ListView recycle items view + Picasso calls are asynchronous ... How it can appears?
You start loading with Picasso
View is reused (convertView != null)
You are setting holder.imageD.setImageBitmap(null);
Asynchronous from point 1. is finnished
Thats why you are having wrong image loaded ...
To avoid such behaviour you need to inform Picasso loader to cancel previous request.
So instead just setting image bitmap to null you have to set it via Picasso library (in else statment use):
Picasso.with(context1).load(null).placeholder(R.drawable.white).into(holder.imageD);
edit: following #Budius comments: even better solution will be cancel and set instead like:
{
Picasso.with(context1).cancelRequest(holder.imageD);
//holder.imageD.setImageBitmap(null); //or
holder.imageD.setImageResource(R.drawable.white); //depends on your needs
}
This should be more efficent way as it should creates less internal objects on every getView calls.

Using Async task for gridview image loading

Ive been searching for a solution for hours now, hoping someone can help?
Ive got a swipe tab pages ui and each page has a gridview that loads images, works as expected but its very slow, even on a high end device. Can i set image resources using Async task? My adapter for my gridview is below:
public class PcAdapter extends BaseAdapter {
private Context context;
private Integer[] imageIds = {
R.drawable.pcserioussam, R.drawable.pc_trinetwo,
R.drawable.pc_leftfordead, R.drawable.pc_dungeondefenders,
R.drawable.pc_portaltwo, R.drawable.pc_spaz,
R.drawable.pc_laracroftattoo, R.drawable.pc_goatsim,
R.drawable.pc_deadblock
};
public PcAdapter(Context c) {
context = c;
}
public int getCount() {
return imageIds.length;
}
public Object getItem(int position) {
return imageIds[position];
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View view, ViewGroup parent) {
ImageView iview;
if (view == null) {
iview = new ImageView(context);
iview.setLayoutParams(new GridView.LayoutParams(230,300));
// iview.setScaleType(ImageView.ScaleType.FIT_CENTER);
iview.setPadding(5, 5, 5, 5);
} else {
iview = (ImageView) view;
}
iview.setImageResource(imageIds[position]);
return iview;
}
}
#mmlooloo's answer is totally relevant and I totally agree with him.
In complement, and to give a pist of solution, I would suggest you to use the library Picasso which is really easy to use and really powerful.
In your Adapter you can load your Images into the ImageView like this:
// Trigger the download of the URL asynchronously into the image view.
Picasso.with(context)
.load(url) // url of your image
.placeholder(R.drawable.placeholder) // drawable to display while downloading the image
.error(R.drawable.error) // drawable in case of failure
.into(imageView); // ImageView where you want to load the picture.
Picasso will take care of the caching and memory issues for you.
To learn more about Picasso and start using it, take a look here.
Your problem arises because of these several thing:
you are using async task with asyncTask.execute that is run one async task at a time so I recommend you this link:
Running multiple AsyncTasks at the same time -- not possible?
you are using viewpager and if your gridview is inside fragment viewpager when loads your current tab page also loads two tabs beside it to make user experience well when clicking other tabs and not wait to view inflated and so on ... so you must call async task inside tab select not inside page select and cancel it on onTabOnselected (you can not make viewpager to set.limitOffScreenPage(0); that wont work and the 0 is ignored. I highly recommend you use libraries to download images because this task requires lot of tips and tricks to have a smooth user experience and by itself it is an another project(do not reinvent wheel, if others invents for you)
you are decoding images on UI thread and decoding one at a time
your internet connection is slow
your image sizes are large
hope help you !

ViewPager and AsyncTask for loading the Items

I've a ViewPager with an PageAdapter loading ImageViews.
The images displayed come from the internet and I use an AsyncTask for loading the Bitmap and then (if the Page hasn't been destroyed) assign it to the ImageView.
#Override
public Object instantiateItem(ViewGroup container, int position) {
ImageView img = new ImageView(container.getContext());
new LoadImageTask(img,Data.getImageUrL(position)).execute();
container.addView(img );
return img;
}
So the ImageView has two states: loading or loaded. I need to track if the user waits for the image. So I implement this at the LoadImageTask onPostExecute:
if (loadListener != null) {
loadListener.onImageLoaded(position);
}
And in the listener:
public void onImageLoaded(int position) {
if (position == mViewPager.getCurrentItem()) {
// Save record ..
}
}
Since I only need to count the images displayed at the "Current Item of the ViewPager" this method is not enought because if the user moves to the item 5 for example, the ViewPager loads in cache the item 6. If the user waits enought time for this load (beeing the item 5 the current) the callback to onImageLoaded is lost (because the item 6 is not the currentItem at the moment, and then when the user moves to the item 6 I've no way of knowing if the image is loaded or not.
I was thinking in adding something like [[PSEUDOCODE]]:
mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
if (mViewPager.isLoaded(posistion)){
// Save record ...
}
}
But I dont know how to retrieve if the corresponding view loaded the image or not at this moment. How can I do it?

Categories