I want to animate a movement of images.
I have two different implementations, the first involves two methods and runs smoothly, the other only needs one method and lags. I'd rather use the second one but can't figure out what causes the lags.
I don't think it's the code that calculates the new positions since it's very simple and in both methods almost the same (I removed it for better readability)
Here is the first:
public void animateCircleMovement(final long duration) {
// ...
post(new Runnable() {
#Override
public void run() {
animateStep();
}
});
}
public void animateStep() {
// ...
invalidate();
if(curTime<endTime) {
post(new Runnable() {
#Override
public void run() {
animateStep();
}
});
}
}
Here is the second
// ...
new Thread(new Runnable() {
#Override
public void run() {
while(currTime<endTime){
// ...
postInvalidate();
}
}).start();
Why does the second implementation cause lags?
Edited the postInvalidate() method
The animation runs smoothly if Thread.Sleep(10) is put into the while-loop.
Related
I have run into a very strange problem. There is a layout I have with a RecyclerView and a button that switches on animation. The point is if I click the button right after the app executes - say 10 seconds, everything is fine. But if I leave the app running idle for some seconds, without touching anything, then when I click the button again, the animation simply doesn't start at all. And yet if after that I slide RecyclerView about the screen with my finger (in any place!) then the animation triggers all of a sudden. As if it was somehow frozen and "woke up" only after I interacted with the RecyclerView. As if RecyclerView after some seconds puts its animation-related objects to sleep somehow and they "wake-up" only after the RecyclerView is slided?.. Apparently the listener "goes to bed"... Is there a way to keep the animation-related objects "awake" at all times?
Here is the Animation function:
private void recyclerViewAnimate(final RecyclerView recyclerView, LayoutAnimationController controller) {
if(isAnimating){
return;
}
controller = AnimationUtils.loadLayoutAnimation(recyclerView.getContext(), R.anim.layout_recycleview_disappear);
recyclerView.setLayoutAnimation(controller);
recyclerView.setLayoutAnimationListener(new Animation.AnimationListener() {
public void onAnimationStart(Animation animation) {
isAnimating = true;
bt_add.animate().rotationXBy(180).setDuration(400).start();
addButtonLayout.animate().setDuration(400).alpha(0).withEndAction(new Runnable() {
#Override
public void run() {
addButtonLayout.setVisibility(View.INVISIBLE);
}
});
}
public void onAnimationRepeat(Animation animation) {}
public void onAnimationEnd(Animation animation) {
recyclerView.setVisibility(View.GONE);
layoutSettings.setVisibility(View.VISIBLE);
layoutSettings.animate().scaleX(0.95f).setDuration(0).withEndAction(new Runnable() {
#Override
public void run() {
layoutSettings.animate().alpha(1)
.scaleY(1.01f).scaleX(1f).setDuration(400).withEndAction(new Runnable() {
#Override
public void run() {
settingsText.animate().setDuration(300).alpha(1).start();
isAnimating = false;
recyclerView.setLayoutAnimationListener(null);
}
}).start();
}
}).start();
}
});
recyclerView.scheduleLayoutAnimation();
}
Instead of setting scheduleLayoutAnimation(); I needed to use startLayoutAnimation();
What is the difference between
textView.setText("hello")
and
textView.post(new Runnable() {
#Override
public void run() {
mTxtTimer.setText(time);
}
});
If i have to set text in a loop which is beneficial for me.
textView.post(new Runnable() {
#Override
public void run() {
mTxtTimer.setText(time);
}
});
This is creating new child thread of working in UI(parent thread)
This will run in the UI thread, no matter who calls it.
My question is very similar to this question, except that I need it to work with fragments instead of Views.
As explained in that thread, there seems to be a bug(?) in Android when an AnimationListener is attached to an Animation, which results in the onAnimationEnd() callback getting called before the animation has really completely ended.
I'm using fullscreen fragments which are animated in the FragmentTransaction (v4 support library) to have a horizontal "roll-in" effect. This works smoothly everywhere except in the fragment where I start a camera preview. Therefore I want to only start the camera preview once the animation is finished.
I tried using this method in my Fragment:
#Override
public Animation onCreateAnimation(final int transit,final boolean enter,final int nextAnim)
{
Animation anim=super.onCreateAnimation(transit, enter, nextAnim);
if (anim==null && nextAnim!=0)
anim=AnimationUtils.loadAnimation(getActivity(), nextAnim);
if(anim!=null)
{
anim.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation animation)
{
}
#Override
public void onAnimationRepeat(Animation animation)
{
}
#Override
public void onAnimationEnd(Animation animation)
{
scannerView.startCameraInHandlerThread();
}
});
}
Unfortunately starting the camera is quite an expensive operation and so my animation lags every time right before the animation is over. It looks ugly and my client is complaining about it.
I also thought about a cheap solution like waiting/sleeping on the camera thread, but the main thread actually needs to wait for the camera starting thread for synchronization otherwise it can deadlock in some cases, so sleeping actually makes the issue worse (the lag becomes longer).
Does anyone know how to solve this?
edit: someone asked for more code...
public void startCameraInHandlerThread()
{
if (mThread == null)
mThread = new CameraHandlerThread();
synchronized (mThread) {
mThread.openCamera();
}
}
private class CameraHandlerThread extends HandlerThread
{
Handler mHandler = null;
CameraHandlerThread()
{
super("CameraHandlerThread");
start();
mHandler = new Handler(getLooper());
}
synchronized void notifyCameraActionCompleted() {
notify();
}
void openCamera()
{
mHandler.post(new Runnable() {
#Override
public void run() {
startCamera();
notifyCameraActionCompleted();
}
});
try
{
wait();
}
catch (InterruptedException e)
{
Thread.currentThread().interrupt();
LOG.warn("Waiting for camera opening was interrupted");
}
}
}
I was able to come up with a workaround: Just delay the start of my method for a very short time to give the animation time to run to completion. On the devices that my client is going to use this works fine but I don't feel that it's a general enough solution, therefore I'm not going to mark this as the accepted answer.
#Override
public void onAnimationEnd(Animation animation)
{
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
scannerView.startCameraInHandlerThread();
}
}, 10); // will start after 10ms
}
I'm trying this:
public void onClick(View view){
tv.animate().x(600).y(100).scaleX(3).scaleY(3);
tv.animate().x(400).y(1400).scaleX(1).scaleY(1);
}
but it skips the the first line of animation.
How can I make it chain them so first it will do the first line and then the next?
You can try this
Runnable endAction = new Runnable() {
public void run() {
tv.animate().x(400).y(1400).scaleX(1).scaleY(1);
}
};
tv.animate().x(600).y(100).scaleX(3).scaleY(3).withEndAction(endAction);
as suggested in documentation.
I want to make a view disappear (to be gone) when the user pushes a button.
I can make it inside the onCreate() method (main UI thread) by doing:
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
However, I want to be able to do the same thing inside another thread (out of the main UI thread). I tried to put the above live in my thread and it didn't work.
Thank you in advance!
## EDIT ####
To make myself more clear, I want to do something like this:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.teste_aba_3);
botao_tab_musica.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
...
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
}
});
}
However, that DOESN'T work! How can I fix this?
theres a neat method called runOnUiThread
runOnUiThread(new Runnable() {
#Override
public void run() {
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
}
});
edit: with your code
botao_tab_musica.setOnClickListener( new View.OnClickListener() {
#Override
public void onClick(View v) {
runOnUiThread(new Runnable() {
#Override
public void run() {
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
}
});
}
});
I'd suggest either using View.post(Runnable), View.postDelayed(Runnable, long), a Handler, or an AsyncTask to do this for you.
There are some good examples on how to post to the UI thread in these scenarios:
http://developer.android.com/resources/articles/painless-threading.html
I think one possibility is to pass a reference to your activity to that other thread so that your thread can access the findViewById method.
try this
findViewById(R.id.llLoadingGallery).post(new Runnable()
{
#Override
public void run()
{
findViewById(R.id.llLoadingGallery).setVisibility(View.GONE);
}
});
or create AsyncTask