I would like to create a "Flash" class to display a white flash screen (200ms) as easily as a toast, like this:
Flash.create(context).show();
This layer should appear instantly then gradually disappear (alpha transition)
It must not catch events
It must adapt to the screen rotation
It can be created from any activity
I looked for a solution via WindowManager but I am having some difficulties.
What solution do you recommend?
There are some issues with that, as you'll have to pass the Activity and not just a Context into the "Flash" screen. Therefore the handling of screen rotation will be tricky.
I've created and open-sourced a small library called Crouton that can help you.
Currently it still struggles with the issue of orientation changes while a Crouton is being displayed.
So I give my own solution if it can help anybody...
(I changed the call to flash method since my first post)
public class Flasher {
private final static int DURATION_OFFSET = 300;
private final static int DURATION_FADEIN = 50;
private final static int DURATION_FADEOUT = 200;
private Animation fadein;
private Animation fadeout;
private RelativeLayout flash;
private RelativeLayout view;
private Toast toast;
private int count;
public Flasher(Context context) {
fadein = new AlphaAnimation(0, 1);
fadein.setStartOffset(DURATION_OFFSET);
fadein.setDuration(DURATION_FADEIN);
fadein.setAnimationListener(new AnimationListener() {
#Override public void onAnimationStart(Animation anim) {}
#Override public void onAnimationRepeat(Animation anim) {}
#Override public void onAnimationEnd(Animation anim) {
flash.startAnimation(fadeout);
}
});
fadeout = new AlphaAnimation(1, 0);
fadeout.setDuration(DURATION_FADEOUT);
fadeout.setAnimationListener(new AnimationListener() {
#Override public void onAnimationStart(Animation anim) {}
#Override public void onAnimationRepeat(Animation anim) {}
#Override public void onAnimationEnd(Animation anim) {
if(count > 1) {
flash(count - 1);
} else {
cancel();
}
}
});
LayoutParams params = new LayoutParams(-1, -1);
flash = new RelativeLayout(context);
flash.setLayoutParams(params);
flash.setBackgroundColor(0xffffffff);
flash.setVisibility(View.INVISIBLE);
view = new RelativeLayout(context);
view.setLayoutParams(params);
view.addView(flash);
toast = new Toast(context);
toast.setView(view);
toast.setGravity(Gravity.FILL, 0, 0);
}
public final void flash(int count) {
toast.show();
this.count = count;
flash.startAnimation(fadein);
}
public final void cancel() {
toast.cancel();
}
}
MainActivity for test it:
public class MainActivity extends Activity {
private static Flasher flasher;
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
View layout = getLayoutInflater().inflate(R.layout.activity_main, null);
setContentView(layout);
if(flasher == null) {
flasher = new Flasher(this);
}
layout.setOnClickListener(new OnClickListener() {
#Override public void onClick(View v) {
flasher.flash(1);
}
});
}
#Override protected void onDestroy() {
if(isFinishing()) {
flasher = null;
}
super.onDestroy();
}
}
Thank you to Keyboardsurfer for the help!
Edit: a constraint is not satisfied:
The Toast does not dispatch the touch event:
flasher.flash(10);
MainActivity is not touchable during flashes effect
If anybody have a solution to resolve the problem...
After searching for many hours, it seems impossible to delegate events from one window to another. So the final functional solution I've found is to extend an activity so that it achieves this flash effect. For those who are interested in it, here's my subclass:
public class FlmActivity extends Activity {
private final static int DURATION_FADEIN = 50;
private final static int DURATION_FADEOUT = 200;
private final static int DURATION_INTERVAL = 200;
private View view;
private AnimationSet anim;
private int count;
public FlmActivity() {
super();
}
#Override protected void onPostCreate(Bundle state) {
super.onPostCreate(state);
view = new View(this) {{
setBackgroundColor(0xffffffff);
setVisibility(View.INVISIBLE);
}};
anim = new AnimationSet(false) {{
addAnimation(new AlphaAnimation(0, 1) {{
setDuration(DURATION_FADEIN);
}});
addAnimation(new AlphaAnimation(1, 0) {{
setStartOffset(DURATION_FADEIN);
setDuration(DURATION_FADEOUT);
}});
addAnimation(new Animation() {{
setStartOffset(DURATION_FADEIN + DURATION_FADEOUT);
setDuration(DURATION_INTERVAL);
setAnimationListener(new AnimationListener() {
#Override public void onAnimationStart(Animation anim) {}
#Override public void onAnimationRepeat(Animation anim) {}
#Override public void onAnimationEnd(Animation anim) {
flash(count - 1);
}
});
}});
}};
addContentView(view, getWindow().getAttributes());
}
#Override protected void onDestroy() {
((ViewGroup) view.getParent()).removeView(view);
super.onDestroy();
}
public final void flash(int count) {
if((this.count = count) > 0) {
view.startAnimation(anim);
}
}
}
Code to call flash effect in an extended activity:
flash(10);
With this implementation, the events pass through the layer
Related
i want to make a toss app ..it is spining all time and will not stop to show a head or tail..i want that when i click on the coin then it should spin for 2 3 seconds and then stops to show head or tail at random selection...but it is continuously spinning.
Here is the code...
public class MainActivity extends AppCompatActivity {
ImageView Coin;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Coin=(ImageView) findViewById(R.id.ImgViewcoin);
Coin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Animation animation = new AlphaAnimation(1, 0);
animation.setInterpolator(new DecelerateInterpolator());
animation.setDuration(3000);
Coin.startAnimation(animation);
animation.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
final int[] photos = {R.drawable.heads, R.drawable.tails};
final ImageView image = (ImageView) findViewById(R.id.ImgViewcoin);
final Random ran = new Random();
int i = ran.nextInt(photos.length);
image.setImageResource(photos[i]);
image.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
int k = ran.nextInt(photos.length);
image.setImageResource(photos[k]);
}
}
);
}
#Override
public void onAnimationEnd(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
Coin.startAnimation(animation);
}
});
}
}
Remove the findviewbyid from onclick and use with Coin instance you have.
because animator set doesn't provide REPEAT function,
i am implementing my own repetition logic.
but i have a problem on it.
first, let me show you my sample code like below.
My MainActivity ->
public class MainActivity extends AppCompatActivity {
private ImageView imageView;
private Button button;
private AnimatorSet animatorSet;
private Repetition repetition;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image_view);
button = (Button) findViewById(R.id.button);
animatorSet = new AnimatorSet();
repetition = new Repetition();
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startAnimation();
}
});
}
private void startAnimation() {
imageView.post(new Runnable() {
#Override
public void run() {
final ObjectAnimator moveY = ObjectAnimator.ofFloat(imageView, "y", imageView.getY(), imageView.getY() - 200F);
moveY.setDuration(1000);
final ObjectAnimator rotation = ObjectAnimator.ofFloat(imageView, "rotation", 0F, 360F);
rotation.setDuration(1000);
repetition.repeat(animatorSet, 10);
List<Animator> list = new ArrayList<>();
list.add(moveY);
list.add(rotation);
// problem is here, if i run a single animator, repetition class can not get onAnmationEnd callback.
animatorSet.play(moveY);
// if i run playSequentially with multiple animators it works properly;
// animatorSet.playSequentially(list);
animatorSet.start();
}
});
}
}
My support class for REPETITION ->
it's listening animator's lifecycle and if animation ends it compare the desire animation repeat count with current repeat count in onAnimationEnd callback. if i run a single animator.. onAnimationEnd not get called...
just first time of onAnimationEnd is called..
public class Repetition implements Animator.AnimatorListener {
private AnimatorSet animatorSet;
private int repeatCount;
private int currentRepeatCount;
public void repeat(AnimatorSet animatorSet, int repeatCount) {
this.animatorSet = animatorSet;
this.repeatCount = repeatCount;
animatorSet.removeAllListeners();
animatorSet.addListener(this);
}
#Override
public void onAnimationStart(Animator animation) {
Log.d("RepeatitionLog", "onAnimationStart");
}
#Override
public void onAnimationEnd(Animator animation) {
if(currentRepeatCount < repeatCount) {
animatorSet.start();
currentRepeatCount++;
Log.d("RepeatitionLog", "onAnimationRepeat by repetition");
Log.d("RepeatitionLog", "currentRepeatCount : " + currentRepeatCount);
} else {
Log.d("RepeatitionLog", "onAnimationEnd");
}
}
#Override
public void onAnimationCancel(Animator animation) {
Log.d("RepeatitionLog", "onAnimationCancel");
}
#Override
public void onAnimationRepeat(Animator animation) {
Log.d("RepeatitionLog", "onAnimationRepeat");
}
}
I'd appreciate any help!
Thanks.
I have a custom pager that uses two animations
final Animation fadeOut = AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
final Animation fadeIn = AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
For some reason, the animations are not starting up. For example, I'm logging
Log.v("TAG", "L1 start animation"); //prints "L1 start animation"
But I'm not logging anything in onAnimationEnd
Log.v("TAG", "L1 animation end"); //nothing is printing out
Here is setAnimation() method of CustomPager class
public void setAnimation(){
final Animation fadeOut = AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
final Animation fadeIn = AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
l1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.v("TAG", "L1 clicked");
if (clicked) {
//do nothing
} else {
clicked = true;
fadeOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
Log.v("TAG", "L1 animation start");
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
Log.v("TAG", "L1 animation end");
l1.setVisibility(View.GONE);
l2.bringToFront();
clicked = false;
}
});
Log.v("TAG", "L1 start animation");
l1.startAnimation(fadeOut);
l2.setVisibility(View.VISIBLE);
l2.startAnimation(fadeIn);
}
}
});
//etc
}
Here is CustomPager class
public class CustomPager extends PagerAdapter {
private Context mContext;
private View l1;
private View l2;
private LayoutInflater inflater;
private ViewGroup layout;
private Bitmap pic;
private Bitmap pic_blurred;
private boolean clicked = false;
public CustomPager(Context context) {
this.mContext = context;
}
#Override
public Object instantiateItem(ViewGroup collection, int position) {
inflater = LayoutInflater.from(mContext);
layout = (ViewGroup) inflater.inflate(R.layout.pager_play_panel, collection, false);
collection.addView(layout);
l1 = layout.findViewById(R.id.l1);
l2 = layout.findViewById(R.id.l2);
pic = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.pic);
picture_blurred = blur(pic, 1, 100);
BitmapDrawable bkg1 = new BitmapDrawable(pic);
l1.setBackgroundDrawable(bkg1);
BitmapDrawable bkg2 = new BitmapDrawable(picture_blurred);
l2.setBackgroundDrawable(bkg2);
setAnimation();
return layout;
}
public void setAnimation(){
final Animation fadeOut = AnimationUtils.loadAnimation(mContext, R.anim.fade_out);
final Animation fadeIn = AnimationUtils.loadAnimation(mContext, R.anim.fade_in);
l1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.v("TAG", "L1 clicked");
if (clicked) {
//do nothing
} else {
clicked = true;
fadeOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
Log.v("TAG", "L1 animation start");
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
Log.v("TAG", "L1 animation end");
l1.setVisibility(View.GONE);
l2.bringToFront();
clicked = false;
}
});
Log.v("TAG", "L1 start animation");
l1.startAnimation(fadeOut);
l2.setVisibility(View.VISIBLE);
l2.startAnimation(fadeIn);
}
}
});
l2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.v("TAG", "L2 clicked");
if (clicked) {
//do nothing
} else {
clicked = true;
fadeOut.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
}
#Override
public void onAnimationRepeat(Animation animation) {
}
#Override
public void onAnimationEnd(Animation animation) {
l2.setVisibility(View.GONE);
l1.bringToFront();
Log.v("TAG", "L2 visibility set to gone");
clicked = false;
}
});
l2.startAnimation(fadeOut);
l1.setVisibility(View.VISIBLE);
l1.startAnimation(fadeIn);
}
}
});
}
//Etc.
}
Looks like the problem was I had to change the scope of my view variables
public void setAnimation(View l1, View l2){
//etc
}
Rather than using them globally
public void setAnimation(){
//etc
}
Okay a little help here, so I have two images loading in my splash screen. The first image opens (starting the splash screen) then the second image opens, once the second image closes the mainactivity starts. Now my question is how do I make my first image fade out, then fade in with my second image?
-Oh yes, and no cross fading
-Just a complete fade out and in transition
-Thanks in advance
-The splash.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:id="#+id/lin_lay"
android:gravity="center" >
<ImageView
android:contentDescription="#string/desc"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/spinning_wheel_image"
android:background="#drawable/splashscreen1" />
</LinearLayout>
The mainanim.xml
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:drawable="#drawable/splashscreen1" android:duration="2500" />
<item android:drawable="#drawable/splashscreen2" android:duration="4000" />
</animation-list>
The Splash.java
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
ourSong = MediaPlayer.create(Splash.this, R.raw.splashsound);
ourSong.start();
Thread timer = new Thread(){
public void run(){
try{
sleep(10500);
} catch (InterruptedException e){
e.printStackTrace();
}finally{
Intent openStartingPoint = new Intent("com.theapplication.app.STARTINGPOINT");
startActivity(openStartingPoint);
}
}
};
timer.start();
}
#Override
public void setRequestedOrientation(int requestedOrientation) {
// TODO Auto-generated method stub
super.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}
#Override
public void onWindowFocusChanged(boolean hasFocus) {
// TODO Auto-generated method stub
super.onWindowFocusChanged(hasFocus);
ImageView mainimage = (ImageView)findViewById(R.id.spinning_wheel_image);
mainimage.setBackgroundResource(R.anim.mainamin);
mainanimation = (AnimationDrawable) mainimage.getBackground();
mainanimation.start();
use ImageSwitcher instead of ImageView which support animations by it self.
see this sample:
http://www.java2s.com/Code/Android/UI/UsingImageSwitcher.htm
you can add animation like this:
imageSwitcher.setInAnimation(fadeInAnimation);
imageSwitcher.setOutAnimation(fadeOutAnimation);
//
my test:
public class IntroActivity extends Activity implements ViewFactory {
private static final String TAG = "IntroActivity";
private final int[] images = { R.drawable.img3, R.drawable.img2,
R.drawable.img1, R.drawable.img4, R.drawable.img5, R.drawable.img6,
R.drawable.img7, R.drawable.img8 };
private int index = 0;
private final int interval = 10000;
private boolean isRunning = true;
#Override
public void onCreate(Bundle bundle) {
super.onCreate(bundle);
setContentView(R.layout.activity_intro);
startAnimatedBackground();
}
private void startAnimatedBackground() {
Animation aniIn = AnimationUtils.loadAnimation(this,
android.R.anim.fade_in);
aniIn.setDuration(3000);
Animation aniOut = AnimationUtils.loadAnimation(this,
android.R.anim.fade_out);
aniOut.setDuration(3000);
final ImageSwitcher imageSwitcher = (ImageSwitcher) findViewById(R.id.imageSwitcher1);
imageSwitcher.setInAnimation(aniIn);
imageSwitcher.setOutAnimation(aniOut);
imageSwitcher.setFactory(this);
imageSwitcher.setImageResource(images[index]);
final Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
if (isRunning) {
index++;
index = index % images.length;
Log.d("Intro Screen", "Change Image " + index);
imageSwitcher.setImageResource(images[index]);
handler.postDelayed(this, interval);
}
}
};
handler.postDelayed(runnable, interval);
}
#Override
public View makeView() {
ImageView imageView = new ImageView(this);
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setLayoutParams(new ImageSwitcher.LayoutParams(
LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
return imageView;
}
#Override
public void finish() {
isRunning = false;
super.finish();
}
}
to start next activity, in
#Override
public void run() {
if (isRunning) {
just check for the index,
if the index equals to 1 then start the next activity and finish the current;
You can simply just fade out the image, change it, and then finally fade it in again.
imageView.animate()
.alpha(0f)
.setDuration(100)
.setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) { }
#Override
public void onAnimationEnd(Animator animator) {
imageView.setImageResource(R.drawable.newimg);
imageView.animate().alpha(1).setDuration(200);
}
#Override
public void onAnimationCancel(Animator animator) { }
#Override
public void onAnimationRepeat(Animator animator) { }
});
I've got an activity in which several Views animate. The animations are triggered when a RelativeLayout containing a few images is clicked. It's set up in onCreate() like this:
mContainer.setOnClickListener(new ContainerClicked());
And the ContainerClicked() class looks like this:
private class ContainerClicked implements OnClickListener {
#Override
public void onClick(View arg0) {
Log.i(TAG, "TEST!");
mSomeButton.setOnClickListener(null);
mSomeButton.setEnabled(false);
mAnotherButton.setOnClickListener(null);
mAnotherButton.setEnabled(false);
mYetAnotherButton.setOnClickListener(null);
mYetAnotherButton.setEnabled(false);
mContainer.setOnClickListener(null);
if (mLogo.getVisibility() == View.VISIBLE)
new AnimateToParty().execute();
else
new AnimateFromParty().execute();
}
}
This works, and animates everything just how I want it.
AnimateToParty() looks like this:
private class AnimateToParty extends AsyncTask<Void, Integer, Void> {
#Override
protected void onProgressUpdate(final Integer... values) {
final View theView = findViewById(values[0]);
if (!(values[0] == mBackgroundImage.getId() || values[0] == mContainer
.getId())) {
final Animation animation = AnimationUtils.loadAnimation(
DashboardActivity.this, values[1]);
animation.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation arg0) {
}
#Override
public void onAnimationRepeat(Animation arg0) {
}
#Override
public void onAnimationEnd(Animation arg0) {
theView.setVisibility(View.INVISIBLE);
}
});
theView.startAnimation(animation);
} else if (values[0] == mBackgroundImage.getId()) {
TransitionDrawable transition = (TransitionDrawable) theView
.getBackground();
transition.startTransition(getResources().getInteger(
android.R.integer.config_mediumAnimTime));
} else {
TranslateAnimation animation = new TranslateAnimation(0, 0, 0,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-60, getResources().getDisplayMetrics()));
animation.setAnimationListener(new AnimationListener() {
#Override
public void onAnimationStart(Animation arg0) {
}
#Override
public void onAnimationRepeat(Animation arg0) {
}
#Override
public void onAnimationEnd(Animation arg0) {
LayoutParams lp = (LayoutParams) mContainer
.getLayoutParams();
lp.bottomMargin = 0;
RelativeLayout parent = (RelativeLayout) mContainer
.getParent();
mSomeButton.setVisibility(View.GONE);
mAnotherButton.setVisibility(View.GONE);
mYetAnotherButton.setVisibility(View.GONE);
mLogo.setVisibility(View.GONE);
// mContainer.setLayoutParams(lp);
// This caused a visible flicker, so I went with the solution below.
parent.removeView(mContainer);
parent.addView(mContainer, lp);
}
});
animation.setDuration(1000);
mContainer.startAnimation(animation);
}
}
#Override
protected Void doInBackground(Void... arg0) {
try {
publishProgress(mContainer.getId());
publishProgress(mLogo.getId(), R.anim.slide_out_up);
Thread.sleep(100);
publishProgress(mSomeButton.getId(), R.anim.slide_out_left);
Thread.sleep(110);
publishProgress(mAnotherButton.getId(), R.anim.slide_out_left);
Thread.sleep(120);
publishProgress(mYetAnotherButton.getId(), R.anim.slide_out_left);
Thread.sleep(130);
publishProgress(mBackgroundImage.getId());
Thread.sleep(550);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
mContainer.setOnClickListener(new LighterClicked());
}
}
The class above animates some buttons and the logo out of the way, and then reveals the container. In order to keep it there, I change it's layoutparams. Just changing them produces a visible flicker where the container jumps 60 dip up for about one frame (the animation doesn't seem to be quite finished), before settling where I want it. I read somewhere to remove it from its parent and put it back in, and this does not produce the same flicker. However, now the rebinding of the onclicklistener does not work!
I've tried just about everything, and I just now noticed that every click that didn't "go through" fires if I press the home button and open the application again.
Is there a better way to avoid the "flicker"? A better way to do the animations? (I'm quite new at animating views) Why do the click-events fire when I reopen the application?
To avoid the flicker you are seeing, you have to call clearAnimation() at the start of onAnimationEnd
#Override
public void onAnimationEnd(Animation arg0) {
theView.clearAnimation()
...
}