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 !
Related
I am using RecyclerView with GridLayoutManager and The speed of RecyclerView Scroll is too much. I want it to slow down. I did try many other codes and approaches but, none worked for me.
Here is what I found
How to make RecyclerView scroll smoothly?
This post suggests that In onCreateViewHolder and onBindViewHolder there shouldn't be I/O operations and taking too much time to execute any of these method creates a lag. But, I have no I/O operations and not much code into these.
...
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.single_view, parent, false);
return new MyViewHolder(view,listner);
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, int position) {
GlideApp.with(context)
.load(arrayList.get(position).getUri())
.apply(RequestOptoins.overrideOf(180,180))
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.into(holder.img);
Log.d("FetchImages(): "," Glide Called");
}
...
How can I control the scrolling speed of recyclerView.smoothScrollToPosition(position)?
This post suggests to use a CustomLayoutManager Class. But, this didn't work for me either. May be because all answers are focused on LinearLayoutManager and may be it doesn't suupport GridLayoutManager.
https://github.com/Azoft/CarouselLayoutManager/issues/79
I tried this link as well but, I can't understan what's going in there.
4.https://programmer.group/android-recycler-view-slides-quickly-to-the-top.html
Same with this link. Can't understand a thing.
List is too long to post here. But, Ultimately I tried everything and everycode I understood. But, so far nothing seems to work for me.
Note
I am trying to show images from external storage. So, on first load glide will create thumbnails to cache and then on second load there will no larger size images. But, still there are more than 5000 images in my phone. And at a time recyclerview can show 40 images only. So, if I accidently scroll fast rest of the images will load in forground. So, I want to disable that fast scrolling so no matter how many photos are there recyclerview will only show loaded photos.
You can create CustomRecyclerView and slow it down by overriding fling() method. You can read the official documentation here.
#Override
public boolean fling(int velocityX, int velocityY) {
velocityY *= FLING_SPEED_FACTOR; // keep it less than 1.0 to slowdown
return super.fling(velocityX, velocityY);
}
This simply reduces the fling speed and even with same amount of fling your RecyclerView will scroll lesser.
As #Embydextrousu suggested and gave the accurate solution. I want to add few things.
You have to change your RecyclerView in Xml as well to your customRecyclerView Class.
...
<com.example.project.CustomRecyclerView
android:id="#+id/recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android" />
...
Then You need to create a CustomRecyclerView class.
...
public class CustomRecyclerView extends androidx.recyclerview.widget.RecyclerView {
public CustomRecyclerView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean fling(int velocityX, int velocityY) {
velocityY *= 0.35; // keep it less than 1.0 to slowdown
return super.fling(velocityX, velocityY);
}
}
...
Where change the velocityY according to your need.
and finally implement in your fragment or Activity like this.
...
CustomRecyclerView recyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = requireView().findViewById(R.id.recyclerview);
}
...
This will reduce the speed of recyclerView scrolling.
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.
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.
I'm using ViewPager as my main navigation in app. I'm using ActionBar, as well. What I want to achieve is that when user clicks search button in ActionBar I want to have blurred background. So my approach is to take a screenshot, blur it, set as ImageView and display as an overlay over the whole view. And it works. But problem is that actually when I first open search it displays proper screenshot. Then I turn overlay off, change page in ViewPager, click search again and ... I can see the previous screenshot- not new one with new page on it. Here are my snippets of code:
show and hide overlay on search click (I'm passing R.id.container view which is parent id of my content view, menuOverlay is my ImageView referenced from layout)
Here's my method that takes screenshot and makes it blurry
// Ad. 1
item.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
#Override
public boolean onMenuItemActionExpand(MenuItem menuItem) {
if(menuItem.getItemId() == R.id.action_search) {
menuOverlay.setImageBitmap(Utils.takeSnapshot(findViewById(R.id.container)));
menuOverlay.setVisibility(View.VISIBLE);
}
return true;
}
#Override
public boolean onMenuItemActionCollapse(MenuItem menuItem) {
if(menuItem.getItemId() == R.id.action_search) {
menuOverlay.setVisibility(View.GONE);
}
return true;
}
});
// Ad. 2
public static Bitmap takeSnapshot(View v) {
v.setDrawingCacheEnabled(true);
v.buildDrawingCache();
Bitmap bm = v.getDrawingCache();
Bitmap scaled = Bitmap.createScaledBitmap(bm, bm.getWidth()/5, bm.getHeight()/5, false);
return Utils.fastblur(scaled, 5);
}
I;m using fast blur algorithm found somewhere here on StackOverflow which is quite fast.
Where's the problem? Why it happens?
ok, I found the answer. if you want to always have "fresh" screenshot you need to destroy cache after all operations:
v.setDrawingCacheEnabled(false);
I have a LinearLayout, and this LinearLayout will hold dynamically placed views. I need to find out what the width of the children of LinearLayout, however this has to be done in onCreate method. From researching I've found out that you can't use getWidth from this method. So instead I'm using onWindowFocusChanged, which works for the parent LinearLayout (returning me the actual size), but it doesn't work with its children.
Another thing I noticed is that when the screen is fading away and the screen is locked, I can see at the logs the actual width of the children being returned (I think the activity is being paused).
I'm really stuck and this is needed because I need to dynamically place those views depending on the children width.
You might be able to get with the below. But as others pointed out, this probably isn't a great idea.
LinearLayout.measure(MeasureSpec.UNSPECIFIED, MeasureSpec.UNSPECIFIED);
LinearLayout.getMeasuredWidth();
inside the onCreate , views still can't know the state of the nearby views and the children ,etc... so only after all is prepared and the layout process is done , you can get the size of views .
here's a quick code for getting the size of the view just before it's being drawn:
private static void runJustBeforeBeingDrawn(final View view, final Runnable runnable)
{
final ViewTreeObserver vto = view.getViewTreeObserver();
final OnPreDrawListener preDrawListener = new OnPreDrawListener()
{
#Override
public boolean onPreDraw()
{
Log.d(App.APPLICATION_TAG, CLASS_TAG + "onpredraw");
runnable.run();
final ViewTreeObserver vto = view.getViewTreeObserver();
vto.removeOnPreDrawListener(this);
return true;
}
};
vto.addOnPreDrawListener(preDrawListener);
}
alternatively , you can use addOnGlobalLayoutListener instead of addOnPreDrawListener if you wish.
example of usage :
runJustBeforeBeingDrawn(view,new Runnable()
{
#Override
public void run()
{
int width=view.getWidth();
int height=view.getHeight();
}
});
another approach is to use onWindowFocusChanged (and check that hasFocus==true) , but that's not always the best way ( only use for simple views-creation, not for dynamic creations)
EDIT: Alternative to runJustBeforeBeingDrawn: https://stackoverflow.com/a/28136027/878126
https://stackoverflow.com/a/3594216/1397218
So you should somehow change your logic.