I have a ListView wich use recycled views. I'm trying to prevent a view from recycle.
So I use setHasTransientState:
android.support.v4.view.ViewCompatJB.setHasTransientState(View view, boolean hasTransientState)
It works very well on Jellybean version but it doesn't do anything on Api < 16.
Is there a way to make it work or there is different approach for pre Jellybean ?
I found out how to set a RecyclerListener like #Daniel Chow suggested.
listView.setRecyclerListener(new RecyclerListener() {
#Override
public void onMovedToScrapHeap(View view) {
// Stop animation on this view
}
});
For pre Jellybean, I think you can just use setRecyclerListener on ListView and when RecyclerListener#onMovedToScrapHeap(View view) is called, clear the animation on the view who has been recycled and directly do the final job which was supposed to be done when animation ends.
The code inside onMovedToScrapHeap(View view) depends on how you implement the animation, e.g. you can call View#clearAnimation() if you previously used View#startAnimation to start animation.
Use android.support.v4.view.ViewCompat.setHasTransientState(View view, boolean hasTransientState) instead of android.support.v4.view.ViewCompatJB.setHasTransientState(View view, boolean hasTransientState)
Besides the animation problem Daniel talked about, another issue where knowing when your view is recycled has to do with memory management. If you are putting large, memory intensive bitmaps in your list items, it may be possible that you don't want your view recycled if its not likely to be re-used by other items. This hook gives you a chance to clear the bitmap you may have assigned to an ImageView. Hopefully, this is a rare problem.
Related
Am using an array list that holds 5 images which change after an interval of 5seconds on the viewpager2 using handler and runnable,the images change perfectly well from the 1st image to the 5th image and restarts the process again using slide transition, the problem i have is changing the animation as it restarts running through the item images it looks terrible because it has to slide back to the first time as it transits more faster through the items backwards , i want to create it in a disappear(last item) and reappear (1st item) fast transition just like when popup ads change images and restart the slide transition again to the last item in a fragment, below is the piece of code.
viewpager3.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageSelected(int position) {
super.onPageSelected(position);
sliderHandler.removeCallbacks(sliderRunnable);
sliderHandler.postDelayed(sliderRunnable, 5000);
}
});
sliderRunnable =new Runnable() {
#Override
public void run() {
if(viewpager3.getCurrentItem()<image2List.size()-1)
{ viewpager3.setCurrentItem(viewpager3.getCurrentItem()+1);
}else {
viewpager3.setPageTransformer();
viewpager3.setCurrentItem(0);
}
}
};
Not sure why you are using Viewpager2 for this and have to automate the changing of views yourself and you would really also need to implement an infinite View pager to get a more seamless transition (though at a small risk if running out of pages)
You could try setting viewpager3.setCurrentItem(0,false); to jump to the start without any animation.
But much better would be to use the more appropriate AdapterViewFlipper class.
This has automatic changing of the item based on a timer built in to it (no runnables needed).
It recyclers and is dynamic like viewpager2, the only downside is that page(view) is not as nicely encapsulated in a Fragment but as you are just showing images then that should not be a problem.
Example of how to use https://abhiandroid.com/ui/adapterviewflipper
You can add in and out animations to slide in and out to get a similar look to the Viewpager2 animations
If you look at another examples of the similar class for more static content it shows the slide in/out animations https://abhiandroid.com/ui/viewflipper
Hello i am writing a app which uses many customView(parent as FrameLayout holding activity's context).
In the app there is a recyclerView it's holder is having N(significantly large number) of view's(Custom).
As the number of View's are dynamically added (i am testing for 10k+ view's).
in recycler View i am not doing anything special but using coroutine's so that rendering of 10k view will not block the Main Thread
But due to large number's of View's in each RecyclerView Holder it's Filling up the heap memory of the app very fast as i scroll and give's OOM(Out of Memory) Crash.
To solve this issue i read and found I can use weak reference's to the
View.But i have not found any example or reference to this approach.So
i am not sure if using WeakReference on the Android View is a good
solution or not.
Code and Demo Project Reference Below.
RecyclerView Adapter :
override fun onBindViewHolder(holder: ViewPageHolder, position: Int) {
val slideViewMultipleCanvas = SlideViewMultipleCanvas(context)
holder.slideViewItemHolder.addView(slideViewMultipleCanvas)
holder.positionHolder = position
val childJob = launch {
//this is where 10k+ view's are created
slideViewMultipleCanvas.setNumOfObjects(numOfItemsInViewPage)
slideViewMultipleCanvas.startJob()
}
map[position] = childJob
}
CustomView :
...
...
suspend fun setNumOfObjects(numberOfObjects: Int) {
withContext(Dispatchers.Default) {
for (i in 0 until numberOfObjects) {
// To solve OOM as number of view here in heap are >10k
listOfObject.add(WeakReference(ShapeView(context)))
}
}
}
...
...
private fun addViewInScope(): Boolean {
for (shapeView in listOfObject) {
shapeView.get()?.let {
addView(it)
}
}
return true
}
....
....
After Using WeakReference from my testing i have not faced any issue(NO OOM's) till now but still i get Lag's whenever GC(Garbage Collector) kick's in and collect all the weak reference.
My Main concerns are
Is There any other way to handle creation of huge number of View without using WeakReference?
Is there any issue's if i use weak reference for Views?
how to decrease GC lag?(even using WeakReference i am not getting smooth scroll)
P.S. : I am already handling:
null cases if GC collect's the weak referenced View's
cancelling coroutine job's when view is recycled in RecyclerView.(can i release the weak Referred view also of this ViewHolder also???)
Look into RecyclerViewPools, you should be able to allocate a specific view (if the dynamically created ones are the same view) using the itemType and have the recyclerView reuse those views also
The main lag isn’t from Weakreferences, but purely the need to generate so many views so quickly, if you could keep a cached list of views to reuse (kind of like the recyclerViewPool) then that would reduce the inflating lag
Only issue if you’ve got so many views it causes OOM, is it possible to combine the views into a generic view with multiple functionality (less physical views, but more logic and complexity in each to cater for being used under multiple situations
This would allow you to reuse the same view more times, meaning less caching/inflating, you’ll just need to make sure the code is performing efficiently
The app I'm working on shows some sensitive information that must not be shown on the "Recent Tasks" screen when stopping the app by pressing the home button.
I'd like to blur the sensitive data in the screenshot or show the app logo instead.
I am aware of the following approaches but they don't fit my requirements:
Setting the actvitie's android:excludeFromRecents to true in the manifiest prevents the app from being shown at all in the recent tasks. This would disrupt the user experience.
Using FLAG_SECURE results in a blank card on the recents tasks screen. (How do I prevent Android taking a screenshot when my app goes to the background?) I don't like the blank screen. However, I'll stick to this solution if there is no workaround.
Overriding onCreateThumbnail seems like the ideal solution but, unfortunately, doesn't work as it's currently not invoked by the OS :( (https://code.google.com/p/android/issues/detail?id=29370)
And then there are some workarounds that I tried out but that didn't work as hoped:
Start a new activity that shows the app logo in onPause so that it's screenshot is shown instead of the actual activitie's one. But the new activity takes too long to open and it disrupts the user experience.
Set the activitie's content view to an image of the app logo in onPause. That seemed like a great solution to me. Unfortunately, the screenshot for the recent tasks screen is taken at an unspecified time. During testing the app logo quickly appears before the app is closed when pressing 'Home' but the resulting screenshot shows the activity a short time before that.
Removing the sensitive data from the widgets (e.g. textView.setText("")) has the same problem of screenshot timing just mentioned.
Any alternative ideas or solutions to the listed workarounds?
I looked into this a couple of months ago for the same purpose as you.
Unfortunately, I had to conclude that it is simply not possible. I dug through the android source code and confirmed it.
There is no callbacks or methods from android that allows you to customize it (that works anyway). Besides FLAG_SECURE, this part of the code does not accept any input or change.
OnPause and similar lifecycle methods are called too late (the screenshot is taken already). All lifecycle methods that would hint that you're about to go into the background runs too late.
The image you see in the recent tasks is an actual screenshot - and thus isn't affected by changes you do (too late) to your view. That means you can't modify your view just-in-time (like making it invisible, replacing with something else, adding SECURE_FLAG, or any other obstruction of the view). As an aside, these images can be found on an emulator at /data/system_ce/0/recent_images.
The only exception is using FLAG_SECURE, which will prevent the screenshot from being taken of your application. I experimented with setting this FLAG in onPause and removing it in onResume, however as mentioned already these lifecycle methods runs after the screenshot is taken already, and thus had absolutely no effect.
As discussed in How to change the snapshot shown by recent apps list? there used to be a callback that you could use to customize the thumbnail: onCreateThumbnail. However, this does not work and it is never called. To be clear, the callback is still there, it is simply never called by the OS. The fact that it stopped working is poorly documented, but apparently was silently deprecated/removed in 4.0.3
As for the thumbnail itself, it is a screenshot taken serverside. It is taken before onPause is called (or in fact before any callbacks indicating that your activity is about to go into the background is called).
When your app does go into the background, your actual view is animated (to get that zoom-out transition). That animation can be affected through changes you do in onPause (if you're fast enough that is) (I experimented with setting opacity to 0 on the window among other things). This will however only affect the animation. When the animation is finished, the view is replaced by the screenshot taken earlier.
Also see these questions that discuss this:
When does Android take its recent apps switcher screenshot?
Show custom application image in task manager on ICS or JB
Android never call method onCreateThumbnail
Currently (28/10/2020) is impossibile customizing app thumbnail in recent apps screen.
As explained by #Dellkan in the previous answer, the onCreateThumbnail method is not called anymore by the OS.
Unfortunately, also the suggestion to create a kind of launcher/splash screen without the FLAG_SECURE flag to let the app take a screenshot of that activity is not working, because the screenshot is taken on the activity you see and not at the launch of the app.
You cannot even customize the color of window background when using FLAG_SECURE as reported here.
How about implementing a layout overlay on top of your entire activity?
Make it transparent, it's click-through by default, so no negative impact on UX while in use.
In onPause() set a half-transparent, blurred image as the background of that layout, the data will be scrambled behind it. In onResume() change the background to fully transparent again. Voila.
It might be faster than other types of overlays. The positive side effect is, if you do the unblurring as a short animation effect when the user goes back (with a proper library that uses C++ instead of Java), it might even look cool and the users wouldnt even mind seeing it.
I haven't tried this myself, but it's something you haven't tried yet.
Since onPause is called to late, I use WindowFocusChangeListener to observe when the Fragment loses focus. At this moment we can hide all view which show sensitive data:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// hide sensitive data when window moves to background (before system screenshot is captured)
myViewWithSensitiveData.setVisibility(hasFocus ? View.VISIBLE : View.INVISIBLE);
}
});
There is a way to customize it. You need your Activities with sensitive data to FLAG_SECURE in onCreate before you setContentView. Then you need an empty Activity, which renders whatever you want to have as the customized thumbnail. This usually is some sort of splash screen. This new Activity needs to be the launcher and is the only Activity not FLAG_SECURE. This Activity is launched and in onResume starts your actual Activity with the sensitive data.
Android OS will take a screenshot of that new Activity at the beginning of your App. Unfortunately the users will also see this Activity for a short moment. Since every other Activity is FLAG_SECURE, Android OS will use the only available screenshot it made at the beginning.
Was looking for a solution and found some dirty things in case you don't want to use 'FLAG_SECURE'. It doesn't give a nice picture but protects data and doesn't prevent making screenshots for the user while they are in the app.
protected void onPause () {
this.getWindow().getDecorView().getRootView().setScaleX((float)200);
this.getWindow().getDecorView().getRootView().setScaleY((float)200);
super.onPause();
}
protected void onResume () {
this.getWindow().getDecorView().getRootView().setScaleX((float)1);
this.getWindow().getDecorView().getRootView().setScaleY((float)1);
super.onResume();
}
I think this can only achieve through BroadCastReceiver but there is no receiver present. So therefore you first disable default screenshot functionality in android and then implementing your own functionality to take screenshot and before taking screenshot you should blur your secure information.
I want to load images I have in my recyclerview after 350ms and I think I'm using wrong method for that. This is my code:
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.songView.setText(objects_.get(position).getAlbum());
holder.artistView.setText(objects_.get(position).getArtist());
holder.cover.setImageDrawable(context.getResources().getDrawable(R.drawable.song));
if(holder.r!=null){
handler.removeCallbacks(holder.r);
}
holder.r = new Runnable() {
#Override
public void run() {
Drawable img = Drawable.createFromPath(objects_.get(position).getCover());
if (img != null) {
holder.cover.setImageDrawable(img);
Bitmap bitmap = ((BitmapDrawable) img).getBitmap();
Palette palette = Palette.from(bitmap).generate();
Palette.Swatch p = palette.getVibrantSwatch();
if (p != null) {
holder.albumholder.setBackgroundColor(p.getRgb());
}
}
}
};
handler.postDelayed(holder.r,300);
}
But I have a problem with this. when I fast scroll recyclerview images of previous items loads at first then changes to new items picture. You can see result in GIF from this link:
http://8pic.ir/images/nkaaeqdvigqy4c6g2h5n.gif
what can I do to fix it?
I don't understand why do you need this 350ms delay but if you want to do it try some other approach:
Your problem is linked to the fact that RecyclerView recycles (suprise...) item views instead of creating new. That means that you will see previously load image, and if you have posted delayed task (handler.postDelayed(...)) it will be executed event if view was recycled, so wrong image can be loaded for particular list item.
General problem is that you're doing to much work in your onBindViewHolder. You should try to reduce computations here, or at least try to move them to some separate thread (handler is using this same thread it was created - in this case the UI thread).
Create handler inside view holder instead of inside your adapter.
Set some placeholder as an image
clear tasks (messages) currently waiting to be executed:
holder.handler.removeCallbacksAndMessages(null);
post load task (handler.postDelayed(...))
It's also possible that all you need is some nice image loading library like Picasso.
As we know recycler view reuse same view during scroll so it is displaying older images while you lazy load for some moments and after that it will update your imageview.
Solution is simply reset your imageview to default( ie white background or default image) state before lazyload .
You should avoid setimageresource() ,instead use setimagedrawable()
setImageResource Vs setDrawable
Instead of using the runnable for loading images, use AsyncTask. You'll need to execute a separate AsyncTask for each image. This AsyncTask will be saved as a WeakReference inside the drawable object which will be set in the respective ImageView.
WeakReferences are used for mapping purposes. The advantage of using WeakReferences is that the entries can be removed from the memory as soon as they are not required by your app. They will be removed by the Garbage Collector. We need to use the WeakReferences because there can be a large number of AsyncTasks getting executed (equal to the number of items present in the RecyclerView) at the same time and Android system or your app will not be able to identify which AsyncTask belongs to which ImageView without these references.
Now, as the drawable is set in the ImageView, it will contain the WeakReference to its respective AsyncTask. This AsyncTask will process the respective bitmap or drawable to be set on the ImageView and all this will be done off the UI thread.
In order to set the AsyncTask in the drawable object, you'll need to create a custom drawable class which will work as a Drawable object but will have the benefit of attaching an AsyncTask to it.
This Drawable object and AsyncTask will take care of loading the images.
The complete explanation and code for this concept has been provided on Android Developers website. Visit the link: http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
Hello i have a fully working code for my list adapter:
public View getView(int position, View convertView, ViewGroup parent) {
View vi=convertView;
if(convertView==null)
vi = inflater.inflate(R.layout.overview_item, null);
//getting id's
TextView name =(TextView)vi.findViewById(R.id.userUsername);
TextView date =(TextView)vi.findViewById(R.id.imageDate);
ImageView image=(ImageView)vi.findViewById(R.id.userImage);
ImageView avatar=(ImageView)vi.findViewById(R.id.userAvatar);
//setting text
name.setText(dataNames.get(position));
date.setText(dataDates.get(position));
//set image
Log.d("test: ", "Adapter wants to get picture");
imageLoader.DisplayImage(dataImage.get(position), image);
imageLoader.DisplayImage(dataAvatars.get(position), avatar);
return vi;
}
This code works perfect but the problem is this function runs everytime when you scroll throught the listview so whenever the lis item is getting in sight. And that's not what i want. i want it to do this function just once for every list item. This is because when your scrolling fast trought the list it has to load all images again so the loading image is showing and it keeps jumping because the loading image is another size then the image wich is getting loaded. I hope thay tou understand my question and can help me. Already thanks and if i'm not clear please ask my anything in the comments.
So short:
How do i run this code just once for every list-item and not everytime when it's getting in sight?
Your code might call findViewById() frequently during the scrolling of ListView, which can slow down performance. Even when the Adapter returns an inflated view for recycling, you still need to look up the elements and update them. A way around repeated use of findViewById() is to use the "view holder" design pattern.
Check this links:
1 - http://www.javacodegeeks.com/2013/09/android-viewholder-pattern-example.html
No, you should not. This is the way ListView works. Beside, you should use ViewHolder pattern for better performance.
If you still want to do this, you could remove check NULL with convertView. It will solve your problem, but lead to performance, I think.