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.
Related
I'm trying to load image and name from Firebase database to my app in RecyclerView but when I run it it is not showing the image and name with no error, here is my code.
//Load menu
recycler_menu=(RecyclerView)findViewById(R.id.recycler_menu);
recycler_menu.setHasFixedSize(true);
layoutManager= new LinearLayoutManager(this);
recycler_menu.setLayoutManager(layoutManager);
loadMenu();
}
private void loadMenu(){
FirebaseRecyclerOptions<Category> options = new FirebaseRecyclerOptions.Builder<Category>().setQuery(category, Category.class).build();
FirebaseRecyclerAdapter<Category, MenuViewHolder> adapter= new FirebaseRecyclerAdapter<Category, MenuViewHolder>(options){
#Override
public MenuViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.menu_item, parent, false);
MenuViewHolder viewHolder = new MenuViewHolder(view);
return viewHolder;
}
#Override
protected void onBindViewHolder(MenuViewHolder holder, int position, Category model) {
holder.txtMenuName.setText(model.getName());
Picasso.with(getBaseContext()).load(model.getImage()).into(holder.imageView);
}
};
recycler_menu.setAdapter(adapter);
}
My code is running fine but image and name is not showing, blank activity is displaying. Please help.
Just add adapter.startListening() in your OnStart to start the data listener and adapter.stopListening() in your onStop to stop it by this way :
#Override
protected void onStart() {
super.onStart();
adapter.startListening();
}
#Override
protected void onStop() {
super.onStop();
adapter.stopListening();
}
This is one of the main problems you usually face when you are using Firebase as your database. I could not see where you retrieve your data but the problem is, most probably, Firebase event listeners working asynchronous. In another way, your method returns before you retrieve the data. In my project, I solved this issue by retrieving data on onCreateView or onCreate and then use those values inside other methods. If it is not the case I just simply do the work inside the event listener, if I have to (It will make your code look extremely complicated and unreadable). It would be more helpful if you do more research about Firebase and its asynchronous working style.
If you somehow find a better way to solve this, please comment below so I can use it too. I am still seeking a better way.
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.
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 !
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.