Now that I can animate my FloatingActionButton, I have another issue.
When I click on the FAB, the rotation of the FAB starts and keeps going until an AsyncTask finishes its job.
Here are the parts of the code related:
#OnClick(R.id.floating_action_button) //Butterknife feature
public void refreshAccountInfo(){
APIHandler api = new APIHandler(databaseHelper, c, getActivity());
AccountSync accountSync = new AccountSync(api);
accountSync.execute();
}
public class AccountSync extends AsyncTask<Void,Void,Account> {
APIHandler apiHandler;
ObjectAnimator animation;
public AccountSync(APIHandler api){
apiHandler = api;
}
#Override
protected void onPreExecute(){
FloatingActionButton button = ButterKnife.findById(getActivity(), R.id.floating_action_button);
PropertyValuesHolder pvhR = PropertyValuesHolder.ofFloat(View.ROTATION, -360);
animation = ObjectAnimator.ofPropertyValuesHolder(button, pvhR);
animation.setRepeatCount(Animation.INFINITE);
animation.setDuration(1500);
animation.start();
}
#Override
protected Account doInBackground(Void... params) {
Account a;
try {
a = apiHandler.getAccountInfo();
return a;
} catch (final ResponseException e) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(apiHandler.getContext(), e.getMessage(), Toast.LENGTH_LONG).show();
}
});
return null;
}
}
#Override
protected void onPostExecute(Account result) {
animation.setRepeatCount(0);
if(result!=null){
a = result;
showInfo();
Toast.makeText(getActivity(),"Account synchronization done",Toast.LENGTH_SHORT).show();
}
}
}
My problem is, when I trigger the button the first time, the animation plays fine, but when I try to click more times, the AsyncTask will do its job, but the rotation animation will never play until I reload the fragment completely.
How can I fix that ?
You can for example end the animation in onPostExecute like this :
#Override
protected void onPostExecute(Account result) {
if(animation.isRunning())
animation.end();
...
}
}
That should be more efficient than setting repeatCount to 0.
Related
I am attempting to create a toggling animation for a custom menu. There are multiple menus that can be chosen by different buttons.
When no menu is open, tapping on a button should open that menu.
If another menu is open, the open one is closed and after that animation, the chosen one should be opened.
every closing/opening action is coupled with a ConstraintLayout transition.
As it didn't work properly, I created the following test procedure:
#Override
protected void onCreate(Bundle savedInstanceState) {
...
ConstraintLayout layout_main;
ConstraintSet l_mh0c = new ConstraintSet();
ConstraintSet l_mh1o = new ConstraintSet();
ConstraintSet l_mh1c = new ConstraintSet();
Button btn;
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toggleOne();
}
});
layout_main = (ConstraintLayout) findViewById(R.id.constraintMain);
l_mh1o.clone(this, R.layout.main_menue_header1_open);
l_mh1c.clone(this, R.layout.main_menue_header1_closed);
l_mh0c.clone(this, R.layout.activity_main);
...
}
int openedMenue = -1;
long animationTime = 1000;
private void toggleOne() {
TransitionManager.endTransitions(layout_main); //<- makes no difference when commented
if(openedMenue==1) {
System.out.println("sync closing");
Runnable r = () -> toggleOne();
closeMenue(1, animationTime, r);
} else {
System.out.println("sync opening");
startMenue(1, animationTime);
}
}
public void startMenue(Integer index, final Long maxtime) {
Transition t;
switch (index) {
case 1:
t = new ChangeBounds();
t.setDuration(0).addListener(new TransitionEndListener() {
#Override
public void onTransitionEnd(Transition transition) {
Transition t = new ChangeBounds();
t.setDuration(maxtime / 2).addListener(new TransitionEndListener() {
#Override
public void onTransitionEnd(Transition transition) {
Transition t = new ChangeBounds();
t.setDuration(maxtime / 2);
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh0o.applyTo(layout_main);
openedMenue = 1;
System.out.println("sync start finished");
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh1o.applyTo(layout_main);
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh1c.applyTo(layout_main);
// the following does not provoke any changes
Scene s = new Scene(layout_main);
TransitionManager.go(s);
break;
}
}
private void closeMenue(int index, final long maxtime, Runnable callback) {
System.out.println("sync closing menue " + openedMenue);
Transition t;
switch (index) {
case 1:
t = new ChangeBounds();
t.setDuration(maxtime/2).addListener(new TransitionEndListener() {
#Override
public void onTransitionEnd(Transition transition) {
Transition t = new ChangeBounds();
t.setDuration(maxtime/2).addListener(new TransitionEndListener() {
#Override
public void onTransitionEnd(Transition transition) {
Transition t = new ChangeBounds();
t.setDuration(0);
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh0c.applyTo(layout_main);
openedMenue = -1;
try {
callback.run();
} catch (Exception e) { e.printStackTrace(); }
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh1c.applyTo(layout_main);
openedMenue = 1;
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh1o.applyTo(layout_main);
break;
}
}
When I run it, I get the following output:
[Button Click]
sync opening
sync start finished
[Button Click]
sync closing
sync closing menue 1
sync opening
sync start finished // problem
However, the last line is never printed; It seems as if (after the closing action starts the opening action again) the first transition in startMenue() never does a callback to the onTransitionEnd().
Here is the TransitionEndListener (just a simple wrapper for the interface)
public abstract class TransitionEndListener implements Transition.TransitionListener {
#Override
public final void onTransitionStart(Transition transition) {}
#Override
public final void onTransitionCancel(Transition transition) {}
#Override
public final void onTransitionPause(Transition transition) {}
#Override
public final void onTransitionResume(Transition transition) {}
}
I already checked if the second starting Transition is cancelled by putting a print statement in the onTransitionCancel() which doesn't seem to be the case.
Can you please explain why this is happening?
UPDATE
I found this post on TransitionManager callbacks;
The Transition from mh0c t0 mh1c is a ConstraintLayout transition, because the constraints actually change; however, the transition is not visible on the UI because the width of the element in transition is 0. (This transition is a jump from one menu point to the other that should not be visible.)
Could this be a reason that the Transition does not do the callback?
And if so; how can I get around this?
UPDATE 2
I was reading the documentation and may have found a solution using TransitionManager.go(scene, transition).
--> Unfortunately this did not work; see code of startMenue() for changes
After more and more struggling and reviewing these posts:
Animate visibility of a view from gone to visible with animation
TransitionListener callbacks not called when there is nothing to change
Why does my Android transition ignore the TransitionListener?
I have found out what caused the trouble:
In the most inner part (the most inner callback) of closeMenue()
Transition t = new ChangeBounds();
t.setDuration(0);
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh0c.applyTo(layout_main);
openedMenue = -1;
try {
callback.run();
} catch (Exception e) { e.printStackTrace(); }
The sequence of commands is slightly wrong. It should look like this:
Transition t = new ChangeBounds();
t.setDuration(0).addListener(new TransitionEndListener() {
#Override
public void onTransitionEnd(Transition transition) {
try {
callback.run();
} catch (Exception e) { e.prtinStackTrace(); }
}
});
TransitionManager.beginDelayedTransition(layout_main, t);
l_mh0c.applyTo(layout_main);
openedMenue = -1;
This makes sure to first finish the closing transition and THEN starting the opening transition.
I have a method goToNextScreen() which does a check for 3 different asynchronous process, so when all process are done the validation will changes activity (Is kind of a Splash activity)
My example code access from 3 different result callbacks to the activity's method goToNextScreen() updating the flag value for each process and to validate other flags inside.
So far this approach works but i have the next questions:
Is this approach valid? Does it have a risk for some kind of deadlock? all threads/callbacks won't collide accessing the method at the same time causing wrong validations?
class LoadingActivity extends Activity{
public boolean isFetchDone, isAnimationDone, isServiceDone, watchDog;
Presenter presenter;
protected void onCreate(#Nullable Bundle savedInstanceState) {
presenter(this);
runAnimation();
presenter.runGetXService();
runFetchFromDB();
}
//do some generic isAnimationDone
private void runAnimation(){
//animator set and animation assumed to be correct...
//...
animatorSet.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
// do anything before animation start
}
#Override
public void onAnimationEnd(Animator animation) {
isAnimationDone = true;
goToNextScreen();
}
#Override
public void onAnimationCancel(Animator animation) {
// do something when animation is cancelled (by user/ developer)
}
#Override
public void onAnimationRepeat(Animator animation) {
// do something when animation is repeating
}
});
}
//Some example function to fetch data from x DB
private void runFetchFromDB() {
final Realm realm = RealmProvider.getInstance();
final ThingDB db = new ThingDBImpl(realm);
realm.beginTransaction();
db.getData(10L)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<XData>() {
#Override
public void onCompleted() {
isFetchDone = true;
goToNextScreen();
}
#Override
public void onError(Throwable e) {
//we dont care about the result
isFetchDone = true;
goToNextScreen();
}
#Override
public void onNext(XData dataSaved) {
//Should i update isFetchDone here?
});
realm.cancelTransaction();
}
private synchronized void goToNextScreen(){
if(!watchDog) return;
if(isFetchDone && isAnimationDone && isServiceDone){
changeActivityFaded(this, SomeOtherActivity);
finish();
watchDog = true;
}
}
}
class Presenter {
Activity activity;
Presenter(Activity activity){
this.activity = activity;
}
public void runGetXService(){
new Presenter.GetServiceAsyncTask().execute();
}
private class GetServiceAsyncTask extends AsyncTask<Void, Void, Response> {
#Override
protected void onPreExecute() {
super.onPreExecute();
//do preparation for service response, etc, asumme all correct
}
#Override
protected XResponse doInBackground(Void... voids) {
try {
return //Assume correct behaviour...
} catch (NetworkConnectionException e) {
return null;
}
}
#Override
protected void onPostExecute(XResponse xResponse) {
super.onPostExecute(xResponse);
((LoadingActivity)activity).isServiceDone = true;
((LoadingActivity)activity).goToNextScreen();
}
}
}
EDIT:
I have changed the method goToNextScreen to synchronized so it supposed to not allow access from others threads at the same time. Still have doubts if withs the execution will be right.
Yes, making the method synchronized means it cannot be executed multiple times simultaneously. If a second thread calls it while it is still executing, the second thread will block until the synchronization lock is released.
https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html
So far this approach by adding the synchronized to the method goToNextScreen member of the activity instance shared on the threads accessing it has worked so far. (View the question code for solution). Although i need to add a watch dog just in case some thread pass to execute code that its supposed to execute just once.
I want to replace my AsyncTask with RxJava in android. My current AsyncTask goes like this:
public class ProgressBarAsyncTask extends AsyncTask<Void,Void,Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
ringProgressDialog = ProgressDialog.show(context,"MyProgressBarTitle","Working please wait",true, false);
}
#Override
protected Void doInBackground(Void... void) {
//do work
myTask();
return null;
}
#Override
protected void onPostExecute(Void void) {
super.onPostExecute();
ringProgressDialog.dismiss();
}
}
Here's my RxJava replacement:
public static Observable<Void> getObservable(final Context context,final String... params) {
return Observable.defer(new Func0<Observable<Void>>() {
#Override
public Observable<Void> call() {
return Observable.just(myTask());
}
});
}
public static Subscriber<Void> getSubscriber() {
Subscriber<Void> subscriber = new Subscriber<Void>() {
#Override
public void onCompleted() {
ringProgressDialog.dismiss();
}
#Override
public void onError(Throwable e) {
Log.d(TAG,e.toString());
}
#Override
public void onNext(Void aVoid) {
manipulateData();
}
};
return subscriber;
}
My Activity:
public class MainActivity extends Activity {
private ProgressDialog ringProgressDialog;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GetNumberObservable.Observable()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()))
.subscribe(getSubscriber());
}
}
How do I mimic the onPreExecute() method in the AsyncTask where I kick off the progressDialog?
Here is how I would do it:
public final class ProgressOrResult<T> {
final int progress;
final T result;
public ProgressOrResult(int progress, T result) {
this.progress = progress;
this.result = result;
}
}
ProgressDialog ringProgressDialog = ProgressDialog.show(
context, "MyProgressBarTitle", "Working please wait", true, false);
Observable.fromEmitter((Emitter<ProgressOrResult> emitter) -> {
// generate "progress"
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
emitter.onNext(new ProgressOrResult(i, null));
Thread.sleep(1);
}
// generate "result"
emitter.onNext(new ProgressOrResult(100, sum));
emitter.onComplete();
}, BackpressureMode.BUFFER)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
if (pr.result == null) {
ringProgressDialog.setProgress(pr.progress);
} else {
ringProgressDialog.dismiss();
// process the result here
}
}, error -> {
// handle error here
})
In RxJava you have the do operators, that creates Observable
lifecycle events listeners, in your case you want to do something
(update the UI) before the task started, which mean you want the
doOnSubscribe event. (side note it is true with 'cold' Observables
that started thier work when subscrbied to - like your case) Just
beware to call .observeOn(AndroidSchedulers.mainThread())) before
the doOnSubscribe in order to get notified on the mainThread, as
you're updating the UI.
Instead of using both defer and just
return Observable.defer(new Func0<Observable<Void>>() {
#Override
public Observable<Void> call() {
return Observable.just(myTask());
}
});
you can use fromCallable:
Observable.fromCallable(new Callable<Object>() {
#Override
public Object call() throws Exception {
return myTask();
}
})
I am showing progressBar indoOnSubscribe() , hiding in doOnError() and in subscribe().
Please refer to this link for more details.
Shameless Promotion
I've created RxLoading library for that, it can do this and much more,
you can just do something like this:
networkCall().compose(RxLoading.<>create(loadingLayout)).subscribe(...);
it consists out of 2 classes, a custom view (loadingLayout) and RxLoading which is a transformer that glue it all together, you can choose to work with both or either of them.
if you want RxLoading with a simple progress bar you just need to implement an interface and you are done.
you can check more on the GitHub page.
To begin with I have tried a lot of ways to make a smooth animation in Android and probably my best option was to use AnimationDrawable. Everything was perfect until I got out of memory exception on older devices. The reason for that obviously is the number of frames, in my case 75. That is how I got to the point of using AsyncTask and Thread.sleep() to animate the frames. To avoid animation lag I used a Stack in which I preload the first 10 frames and then just pop the used one and push a new one until there are no more frames. Everything worked better than I expected, but the only problem is that at the end of the animation the last frame disappears and I am hitting my head whole day to understand why is that happening with no success obviously. Below is the code from the Activity in which I call the animation and the file where the animation code is.
SplashActivity.java
private void startAnimation() {
gifImageView = (LogoAnimImageView) findViewById(R.id.gifImageView);
gifImageView.setSplashActivityContext(this);
gifImageView.setBackgroundResource(R.drawable.logo_frame_0);
gifImageView.setAnimImageViewListener(new LogoAnimImageView.LogoAnimImageViewInterface() {
#Override
public void animationEnd() {
mAnimationFinished = true;
LoadNextActivity();
}
});
gifImageView.startLogoAnimation();
}
LogoAnimImageView.java
public class LogoAnimImageView extends ImageView {
public interface LogoAnimImageViewInterface {
void animationEnd();
}
final Handler mHandler = new Handler();
private Stack<Drawable> mImageStack;
private SplashActivity mSplashActivity;
private LogoAnimImageViewInterface mListener;
private int mFrameIndex;
private int[] mResources = {R.drawable.logo_frame_0,R.drawable.logo_frame_1,R.drawable.logo_frame_2,R.drawable.logo_frame_3,
R.drawable.logo_frame_4,R.drawable.logo_frame_5,R.drawable.logo_frame_6,
R.drawable.logo_frame_7,R.drawable.logo_frame_8,R.drawable.logo_frame_9,R.drawable.logo_frame_10,
R.drawable.logo_frame_11,R.drawable.logo_frame_12,R.drawable.logo_frame_13,R.drawable.logo_frame_14,
R.drawable.logo_frame_15,R.drawable.logo_frame_16,R.drawable.logo_frame_17,R.drawable.logo_frame_18,
R.drawable.logo_frame_19,R.drawable.logo_frame_20,R.drawable.logo_frame_21,R.drawable.logo_frame_22,
R.drawable.logo_frame_23,R.drawable.logo_frame_24,R.drawable.logo_frame_25,R.drawable.logo_frame_26,
R.drawable.logo_frame_27,R.drawable.logo_frame_28,R.drawable.logo_frame_29,R.drawable.logo_frame_30,
R.drawable.logo_frame_31,R.drawable.logo_frame_32,R.drawable.logo_frame_33,R.drawable.logo_frame_34,
R.drawable.logo_frame_35,R.drawable.logo_frame_36,R.drawable.logo_frame_37,R.drawable.logo_frame_38,
R.drawable.logo_frame_39,R.drawable.logo_frame_40,R.drawable.logo_frame_41,R.drawable.logo_frame_42,
R.drawable.logo_frame_43,R.drawable.logo_frame_44,R.drawable.logo_frame_45,R.drawable.logo_frame_46,
R.drawable.logo_frame_47,R.drawable.logo_frame_48,R.drawable.logo_frame_49,R.drawable.logo_frame_50,
R.drawable.logo_frame_51,R.drawable.logo_frame_52,R.drawable.logo_frame_53,R.drawable.logo_frame_54,
R.drawable.logo_frame_55,R.drawable.logo_frame_56,R.drawable.logo_frame_57,R.drawable.logo_frame_58,
R.drawable.logo_frame_59,R.drawable.logo_frame_60,R.drawable.logo_frame_61,R.drawable.logo_frame_62,
R.drawable.logo_frame_63,R.drawable.logo_frame_64,R.drawable.logo_frame_65,R.drawable.logo_frame_66,
R.drawable.logo_frame_67,R.drawable.logo_frame_68,R.drawable.logo_frame_69,R.drawable.logo_frame_70,
R.drawable.logo_frame_71,R.drawable.logo_frame_72,R.drawable.logo_frame_73,R.drawable.logo_frame_74
};
public LogoAnimImageView(Context context) {
super(context);
}
public LogoAnimImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LogoAnimImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void startLogoAnimation() {
mFrameIndex = 10;
mImageStack = new Stack<Drawable>();
for (int i=1;i<=mFrameIndex;i++) {
Drawable drawable = getDrawable(mResources[i]);
mImageStack.push(drawable);
}
mFrameIndex++;
mSplashActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
new LogoAnimOperation().execute((Object)null);
}
});
}
public void setSplashActivityContext(SplashActivity splashActivity) {
this.mSplashActivity = splashActivity;
}
public void setAnimImageViewListener(LogoAnimImageViewInterface listener) {
this.mListener = listener;
}
private Drawable getDrawable(int id) {
Drawable drawable;
if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP){
drawable = mSplashActivity.getDrawable(id);
} else {
drawable = mSplashActivity.getResources().getDrawable(id);
}
return drawable;
}
private class LogoAnimOperation extends AsyncTask<Object,Void,String> {
#Override
protected String doInBackground(Object... params) {
int number=1;
while (mImageStack.size() > 1) {
try {
Thread.sleep(40);
} catch (InterruptedException e) {
e.printStackTrace();
}
final Drawable drawable = mImageStack.pop();
mSplashActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
LogoAnimImageView.this.setBackground(drawable);
}
else {
LogoAnimImageView.this.setBackgroundDrawable(drawable);
}
if (mFrameIndex < mResources.length) {
Drawable newDrawable = getDrawable(mResources[mFrameIndex]);
mImageStack.push(newDrawable);
mFrameIndex++;
}
}
});
}
return "";
}
#Override
protected void onPostExecute(String s) {
mSplashActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
Drawable drawable = getDrawable(R.drawable.logo_frame_74);
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
LogoAnimImageView.this.setBackground(drawable);
}
else {
LogoAnimImageView.this.setBackgroundDrawable(drawable);
}
}
});
mListener.animationEnd();
super.onPostExecute(s);
}
}
}
...but the only problem is that at the end of the animation the last
frame disappears and I am hitting my head whole day to understand why
is that happening with no success obviously.
The problem may lie in your AsyncTask's onPostExecute(String):
#Override
protected void onPostExecute(String s) {
mSplashActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
Drawable drawable = getDrawable(R.drawable.logo_frame_74);
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
LogoAnimImageView.this.setBackground(drawable);
} else {
LogoAnimImageView.this.setBackgroundDrawable(drawable);
}
}
});
mListener.animationEnd();
super.onPostExecute(s);
}
onPostExecute(String) will always be called on the UI thread. So, mSplashActivity.runOnUiThread(....) is redundant.
By using runOnUiThread(Runnable), you are posting to the UI thread's event queue. So, the runnable is executed when its turn comes up. However, the code after the mSplashActivity.runOnUiThread(....) call may get executed before the runnable. So, mListener.animationEnd() may be getting called before your LogoAnimImageView has a chance to display R.drawable.logo_frame_74.
But, this should not happen in your case. If runOnUiThread(Runnable) is called from the UI thread (which, it is), the Runnable is not posted to the event queue, and executed immediately instead.
I suspect that the real issue here is that there isn't any delay between the last frame of your animation (R.drawable.logo_frame_74), and launch of next activity. Perhaps you could comment out the call to mListener.animationEnd(), to check whether the animation ends at the last or second-last frame.
Although this is an interesting approach, and one I haven't seen before, I have to say that you are meddling with more threads than you need to. If you're trying to load Drawables as and when they are needed, there is a simpler way:
public class LogoAnimImageView extends ImageView {
....
....
// flag to indicate whether `mNextFrameDrawable` should continue loading the next frame
private boolean mStopAnimating;
// loads the next frame, and calls back to activity when done
private Runnable mNextFrameRunnable = new Runnable() {
#Override
public void run() {
if (!mStopAnimating) {
if (isFinishedAnimating() && mListener != null) {
mListener.animationEnd();
} else { // Load next frame
setViewBg(getNextFrameDrawable());
// Will load the next frame in 40 ms
postDelayed(this, 40L);
}
}
}
};
// This method can be set `public static` and placed in a separate `Utils` class
private void setViewBg(Drawable d) {
if(android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
setBackground(drawable);
} else {
setBackgroundDrawable(drawable);
}
}
private Boolean isFinishedAnimating() {
return mFrameIndex >= mResources.length;
}
// returns the next frame's drawable and increments the `mFrameIndex` pointer
private Drawable getNextFrameDrawable() {
return getDrawable(mResources[mFrameIndex++]);
}
// start animating
public void startLogoAnimation() {
mFrameIndex = 0;
mStopAnimating = false;
post(mNextFrameRunnable);
}
// stop animating
public void stopLogoAnimation() {
mStopAnimating = true;
removeCallbacks(mNextFrameRunnable);
}
....
....
}
AsyncTask is neither needed, nor designed to handle such scenarios.
I want to show a fade effect when image is loading on Imageview. I am using picasso to cache image and display in image view. I have searched alot for this but couldnt find any solution.
I have used earlier before and i know in some version they had .fade(int Duration) method to fade image while loading but i couldnt find this method anymore.
Here is what i am doing now
Picasso.with(context)
.load(viewHolder.data.imageList.get(0).url)
.networkPolicy(NetworkPolicy.OFFLINE)
.placeholder(R.drawable.a_place_holder_list_view)
.error(R.drawable.a_place_holder_list_view)
.into(viewHolder.ivUser, context.loadImage(viewHolder.ivUser, viewHolder.data.imageList.get(0).url));
public Callback loadImage(RoundedImageView ivUser, String url) {
return new callback(ivUser, url);
}
public class callback implements Callback {
RoundedImageView imageView;
String url;
public callback(RoundedImageView imageView, String url) {
this.imageView = imageView;
this.url = url;
}
#Override
public void onSuccess() {
}
#Override
public void onError() {
Picasso.with(BaseActivity.this)
.load(url)
.placeholder(R.drawable.a_place_holder_list_view)
.error(R.drawable.a_place_holder_list_view)
.into(imageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
Log.v("Picasso", "Could not fetch image");
}
});
}
}
Please help me i have been stuck in this for quite long time.
Thanks in advance.
Quoting Jake Wharton's answer here:
If the image comes from anywhere except the memory cache the fade
should be automatically applied.
If you check the PicassoDrawable class
boolean fade = loadedFrom != MEMORY && !noFade;
if (fade) {
this.placeholder = placeholder;
animating = true;
startTimeMillis = SystemClock.uptimeMillis();
}
.
.
.
#Override public void draw(Canvas canvas) {
if (!animating) {
super.draw(canvas);
} else {
.
.
.
fade effect is already applied for images loaded from n/w and not memory/cache
and FADE_DURATION = 200f; //ms
To force fade, again quoting jake wharton's answer here:
You can specify noFade() and then always play an animation in the
image loaded callback. You can also rely on the callback being called
synchronously to determine if an animation needs played.
final AtomicBoolean playAnimation = new AtomicBoolean(true);
Picasso.with(context).load(..).into(imageView, new Callback() {
#Override public void onLoad() {
if (playAnimation.get()) {
//play fade
Animation fadeOut = new AlphaAnimation(0, 1);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(1000);
imageView.startAnimation(fadeOut);
Animation fadeOutPlaceholder = new AlphaAnimation(1, 0);
fadeOutPlaceholder.setInterpolator(new AccelerateInterpolator());
fadeOutPlaceholder.setDuration(1000);
placeHolderImageView.startAnimation(fadeOutPlaceholder);
}
}
//..
});
playAnimation.set(false);
You can simply do
Picasso.with(context).load(url).fetch(new Callback(){
#Override
public void onSuccess() {
imageView.setAlpha(0f);
Picasso.with(context).load(url).into(imageView);
imageView.animate().setDuration(300).alpha(1f).start();
}
#Override
public void onError() {
}
});
I do this:
Picasso.get().load(url).fit().noFade().centerInside().into(imageView, new Callback() {
#Override
public void onSuccess() {
imageView.setAlpha(0f);
imageView.animate().setDuration(200).alpha(1f).start();
}
#Override
public void onError(Exception e) {
}
});