Android GridView with Picasso display image wrongly [duplicate] - java

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.

Related

how to change content of an image in drawable folder then use it

I have an app that use .jpg files in drawable folder to show in viewPager.
I want to prevent simply steal my apk drawable files. so before loading them in drawable folder, I changed a unique word in .jpg file content name it Original
ےطےل Original II* ےى Ducky
to Fake.
ےطےل Fake II* ےى Ducky
this cause .Jpg file unreadable.then I load unreadable files to drawable folder.
I need a method that replace Fake to Original then send it to mResources. I have no idea how to do that please explain it step by step!!
class CustomPagerAdapter extends PagerAdapter {
int[] mResources = {
R.drawable.first,
R.drawable.second,
R.drawable.third,
R.drawable.fourth,
R.drawable.fifth,
R.drawable.sixth
};
Context mContext;
LayoutInflater mLayoutInflater;
public CustomPagerAdapter(Context context) {
mContext = context;
mLayoutInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mResources.length;
}
#Override
public boolean isViewFromObject(View view, Object object) {
return view == ((LinearLayout) object);
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
View itemView = mLayoutInflater.inflate(R.layout.pager_item, container, false);
ImageView imageView = (ImageView) itemView.findViewById(R.id.imageView);
imageView.setImageResource(mResources[position]);
container.addView(itemView);
return itemView;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((LinearLayout) object);
}
}
It's not the answer you want to hear, but...
I wouldn't spend much time trying to make your files unreadable. Even if you get it to work, you'll likely really slow down your app and/or make it less robust. And without a lot of extra hassle, you'll probably break all the automatic resource selection (for orientation, screen-density, etc.) that the OS does on you behalf.
Your altered drawables would probably still be readable by the more permissible graphics programs. And they will always be vulnerable to screen-grabs.
IMO, it would be better to spend the time making your app as good as you can and let your competitors try to play catch-up. Developing non-trivial Android apps can be challenging enough without attempting to override built-in behaviors.

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 !

Conditionally changing list adapter android

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.

ListView selection remains persistent after exiting choice mode

I have a ListView subclass that I allow selections on when the context action bar (CAB) is active. The CAB is set as a callback to the onItemLongClick event:
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// Inflate a menu resource providing context menu items
MenuInflater inflater = mode.getMenuInflater();
inflater.inflate(context_menu, menu);
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
return true;
}
This is fine, and the ListView works as expected, with the currently selected item staying highlighted when touched.
When I close the CAB, I want the ListView to return to normal (i.e. Touch mode). The problem is that the last selected item remains highlighted indefinitely, regardless of what methods I try to clear it:
public void onDestroyActionMode(ActionMode mode) {
//Unselect any rows
ListView lv = getListView();
lv.clearChoices(); // Has no effect
lv.setChoiceMode(ListView.CHOICE_MODE_NONE); // Has no effect on the highlighted item
lv.setFocusable(false); // Has no effect
lv.setSelection(0); // Has no effect
mActionMode = null;
}
Any suggestions?
The main reason for the problem is that once the ListView selection mode is switched to CHOICE_MODE_NONE, the framework optimizes out the clear operation as it is no longer supporting 'selections'. I have improved the above workarounds a bit by clearing the selection state manually and then setting the mode in a delayed manner so the framework will have its turn to clear the state before turning the mode to CHOICE_MODE_NONE.
final ListView lv = getListView();
lv.clearChoices();
for (int i = 0; i < lv.getCount(); i++)
lv.setItemChecked(i, false);
lv.post(new Runnable() {
#Override
public void run() {
lv.setChoiceMode(ListView.CHOICE_MODE_NONE);
}
});
I faced the same issue and since requesting layout doesn't solve the problem for me either I implemented a little hack which works for me. Maybe this is the same issue because I'm switching between CHOICE_MODE_SINGLE and CHOICE_MODE_NONE.
When the action mode ends I'm calling this code snippet. clearChoices makes sure that all items are not checked anymore (internally). The iteration over the views makes sure that all currently visible views are reset and not checked anymore.
mListView.clearChoices();
for (int i = 0; i < mListView.getChildCount(); i++) {
((Checkable) mListView.getChildAt(i)).setChecked(false);
}
mListView.setChoiceMode(ListView.CHOICE_MODE_NONE);
Looking at the ListView sourcecode, the only way to work around this is to set the ListView to CHOICE_MODE_NONE, then re-assign the ListAdapter (which clears the internal selection list regardless of choice mode)
i.e. in a ListFragment/ListActivity
getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
getListView().setAdapter(getListAdapter())
I was having this issue in API Level 17 and solved it by doing:
listView.clearChoices();
listView.invalidateViews();
For me, it seems the accepted answer is not working for invisible items, and it's no need to call
for (int i = 0; i < lv.getCount(); i++)
lv.setItemChecked(i, false);
instead, just call
lv.requestLayout();
To completely solve my issue, I call
lv.clearChoices();
lv.requestLayout();
in onDestroyActionMode()
and call
lv.setItemChecked(position, false)
in onItemClick() when it's not in ActionMode
However, I did not confirm whether call setItemChecked() will result some performance issues
This has been logged as an AOSP bug, but marked as obsolete for whatever reason.
Normally you would expect this to work:
getListView().clearChoices();
getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
Unfortunately it does not. Deferring setting choice mode to none in the next layout pass would work:
getListView().clearChoices();
getListView().post(new Runnable() {
#Override
public void run() {
getListView().setChoiceMode(ListView.CHOICE_MODE_NONE);
}
});
I had tried all the approaches discussed above but none of them work for me. Finally, I decide to apply the following workaround. The key idea is that,
During multimode, instead of reusing the "cached" view, we will create a completely new view. Not efficient, but at least "partially" solve my problem.
Here is the code of my customized ArrayAdapter
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// Key to solve this problem. When we are in multimode, we will not reusing the cached view.
View rowView = this.multimode ? null : convertView;
if (rowView == null) {
LayoutInflater inflater = activity.getLayoutInflater();
rowView = inflater.inflate(R.layout.watchlist_row_layout, null);
ViewHolder viewHolder = new ViewHolder();
viewHolder.textView0 = (TextView) rowView.findViewById(R.id.text_view_0);
viewHolder.textView1 = (TextView) rowView.findViewById(R.id.text_view_1);
viewHolder.textView2 = (TextView) rowView.findViewById(R.id.text_view_2);
rowView.setTag(viewHolder);
}
Also, I feel safer to have the following code in ActionMode.Callback, although I'm not sure how much it helps.
#Override
public void onDestroyActionMode(ActionMode mode) {
MyFragment.this.myArrayAdapter.setMultimode(false);
// http://stackoverflow.com/questions/9754170/listview-selection-remains-persistent-after-exiting-choice-mode
// Using View.post is the key to solve the problem.
final ListView listView = MyFragment.this.getListView();
listView.clearChoices();
for (int i = 0, ei = listView.getChildCount(); i < ei; i++) {
listView.setItemChecked(i, false);
}
listView.post(new Runnable() {
#Override
public void run() {
listView.setChoiceMode(ListView.CHOICE_MODE_NONE);
}
});
actionMode = null;
}
Side Note
Using MultiChoiceModeListener couple with CHOICE_MODE_MULTIPLE_MODAL will make this bug gone. However, for device below API level 11 will not able to use this solution.
I know this has been answered, but above answers still gave me problems with the cached/recycled views that ListView maintains, that didn't update it's state when scrolled back into view.
So, the above solution changes slightly to:
lv.clearChoices();
ArrayList<View> list = new ArrayList<View>();
lv.reclaimViews(list);
for (View view : list) {
((Checkable) view).setChecked(false);
}
lv.setChoiceMode(lv.CHOICE_MODE_NONE);
This is better than using getChildAt(i) because that method jusg gives you the currently visble views and does not account for the internal cached views, that are not visible.
I have found that the only two methods that work here (API 19) are:
Resetting the list adapter, which is undesirable because it goes back to the top of the list;
Setting the choice mode to CHOICE_MODE_NONE in a new Runnable
If the choice mode is changed without using listView.post(new Runnable()), it doesn't work. Can anyone explain to me why this is?
Apologies for not commenting; I have no reputation.
Thanks.
Not sure if this is too late just wanted to share. I created an intent to the same page so that once the clicked data is captured it recreates a fresh page without any clicked persistence.
Is not a bug. That behavior is required to support multiple HID for Android.
So to show the selection state you only need set the choice mode of the listview and a background to support the selected state for the "list item layout", like:
android:background="?android:attr/activatedBackgroundIndicator"
FYI: http://android-developers.blogspot.mx/2008/12/touch-mode.html

Categories