Android ImageView实现淡入淡出效果

93

我正在构建一个幻灯片,但遇到了一些问题。

我已经创建了两个 XML 动画以实现淡入和淡出效果:

fadein.xml

    <?xml version="1.0" encoding="UTF-8"?>
       <set xmlns:android="http://schemas.android.com/apk/res/android">
         <alpha android:fromAlpha="0.0" android:toAlpha="1.0" 
          android:interpolator="@android:anim/accelerate_interpolator" 
          android:duration="2000"/>
     </set>

淡出效果的XML文件

    <?xml version="1.0" encoding="UTF-8"?>
       <set xmlns:android="http://schemas.android.com/apk/res/android">
         <alpha android:fromAlpha="1.0" android:toAlpha="0.0" 
          android:interpolator="@android:anim/accelerate_interpolator" 
          android:duration="2000"/>
     </set>

我想做的是使用淡入淡出效果更改ImageView中的图像,以便当前显示的图像会逐渐消失,并且另一幅图像会淡入。 考虑到已经设置了一张图像,我可以使用以下代码将其淡出:

    Animation fadeInAnimation = AnimationUtils.loadAnimation(this, R.anim.your_fade_in_anim);
    imageView.startAnimation(fadeoutAnim);

但是,接下来我设置要显示的下一张图片:

    imageView.setImageBitmap(secondImage);

当我在imageView中设置动画时,图片会消失,然后再淡入。有没有办法解决这个问题?我的意思是,当我执行imageView.setImageBitmap(secondImage);命令时,图片不会立即显示,只有在淡入动画执行时才会显示出来。

9个回答

101
我想达到与您相同的目标,因此我编写了以下方法,如果您向其传递一个ImageView和对图片可绘制项的引用列表,则可以完全实现这一目标。
ImageView demoImage = (ImageView) findViewById(R.id.DemoImage);
int imagesToShow[] = { R.drawable.image1, R.drawable.image2,R.drawable.image3 };

animate(demoImage, imagesToShow, 0,false);  



  private void animate(final ImageView imageView, final int images[], final int imageIndex, final boolean forever) {

  //imageView <-- The View which displays the images
  //images[] <-- Holds R references to the images to display
  //imageIndex <-- index of the first image to show in images[] 
  //forever <-- If equals true then after the last image it starts all over again with the first image resulting in an infinite loop. You have been warned.

    int fadeInDuration = 500; // Configure time values here
    int timeBetween = 3000;
    int fadeOutDuration = 1000;

    imageView.setVisibility(View.INVISIBLE);    //Visible or invisible by default - this will apply when the animation ends
    imageView.setImageResource(images[imageIndex]);

    Animation fadeIn = new AlphaAnimation(0, 1);
    fadeIn.setInterpolator(new DecelerateInterpolator()); // add this
    fadeIn.setDuration(fadeInDuration);

    Animation fadeOut = new AlphaAnimation(1, 0);
    fadeOut.setInterpolator(new AccelerateInterpolator()); // and this
    fadeOut.setStartOffset(fadeInDuration + timeBetween);
    fadeOut.setDuration(fadeOutDuration);

    AnimationSet animation = new AnimationSet(false); // change to false
    animation.addAnimation(fadeIn);
    animation.addAnimation(fadeOut);
    animation.setRepeatCount(1);
    imageView.setAnimation(animation);

    animation.setAnimationListener(new AnimationListener() {
        public void onAnimationEnd(Animation animation) {
            if (images.length - 1 > imageIndex) {
                animate(imageView, images, imageIndex + 1,forever); //Calls itself until it gets to the end of the array
            }
            else {
                if (forever){
                animate(imageView, images, 0,forever);  //Calls itself to start the animation all over again in a loop if forever = true
                }
            }
        }
        public void onAnimationRepeat(Animation animation) {
            // TODO Auto-generated method stub
        }
        public void onAnimationStart(Animation animation) {
            // TODO Auto-generated method stub
        }
    });
}

1
展示淡入淡出动画如何在代码中定义,你赢得了我的投票。 - Herr Grumps
你也可以计算重复次数,并在你的onAnimationRepeat方法中执行Array.lenght%count。 - butelo
2
@Crocodile,你认为你的代码可能存在内存问题吗?让包含上述代码的活动继续运行。它将创建很多AnimationSet对象。有没有可能在一段时间后由于OutOfMemory而崩溃? - Gem
2
@Gem - 为什么这个特定的分配会引起问题?每次调用imageView.setAnimation(animation)时,对先前动画的任何引用都会丢失,因此垃圾收集器将能够删除此先前对象。据我所知,这不是内存问题。 - greg7gkb
1
我该如何设置它以实现重叠? - Omid Omidi
显示剩余2条评论

69
要按照你现在开始的方式实现,你需要添加一个AnimationListener来检测动画的开始和结束。当调用淡出的onAnimationEnd()时,您可以将ImageView对象的可见性设置为View.INVISIBLE,切换图像并开始淡入动画 - 这里也需要另一个AnimationListener。当您收到淡入动画的onAnimationEnd()时,将ImageView设置为View.VISIBLE,这样就可以获得您想要的效果。
我以前实现过类似的效果,但我使用了一个带有2个ImageView的ViewSwitcher而不是单个ImageView。您可以使用淡入和淡出设置ViewSwitcher的“in”和“out”动画,因此它可以管理AnimationListener实现。然后,您只需要在两个ImageView之间交替即可。

编辑: 为了更有用,这里提供了一个使用ViewSwitcher的快速示例。完整源代码包含在https://github.com/aldryd/imageswitcher中。

activity_main.xml

    <ViewSwitcher
        android:id="@+id/switcher"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:inAnimation="@anim/fade_in"
        android:outAnimation="@anim/fade_out" >

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitCenter"
            android:src="@drawable/sunset" />

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitCenter"
            android:src="@drawable/clouds" />
    </ViewSwitcher>

MainActivity.java

    // Let the ViewSwitcher do the animation listening for you
    ((ViewSwitcher) findViewById(R.id.switcher)).setOnClickListener(new View.OnClickListener() {

        @Override
        public void onClick(View v) {
            ViewSwitcher switcher = (ViewSwitcher) v;

            if (switcher.getDisplayedChild() == 0) {
                switcher.showNext();
            } else {
                switcher.showPrevious();
            }
        }
    });

谢谢,伙计!我忘记了serVisible!还有感谢你的ViewSwitcher提示,它似乎比自己处理一切更容易。 - IPValverde
@Aldryd 这个方案在性能方面更高效吗?相较于被选中的答案(使用单独的XML文件)? - Ron
@Ron 我并没有真正比较不同方法的性能。与解码/操作 ImageView 对象的位图相比,我认为使用 ViewSwitcher 而不是自己实现 AnimationListener 不会有太大区别。最近我发现,如果您使用的是 API 11 或更高版本,您可以使用一些新的动画类。在这里,您可以看到一个使用 API 11 实现两个视图交叉淡入淡出效果的示例:http://developer.android.com/training/animation/crossfade.html - Aldryd

47

你是否考虑过使用TransitionDrawable替代自定义动画? https://developer.android.com/reference/android/graphics/drawable/TransitionDrawable.html

实现你所需的效果的一种方法是:

// create the transition layers
Drawable[] layers = new Drawable[2];
layers[0] = new BitmapDrawable(getResources(), firstBitmap);
layers[1] = new BitmapDrawable(getResources(), secondBitmap);

TransitionDrawable transitionDrawable = new TransitionDrawable(layers);
imageView.setImageDrawable(transitionDrawable);
transitionDrawable.startTransition(FADE_DURATION);

2
这是这个问题的最佳答案。 - DragonT
抱歉打扰了,但如果我在Handler中使用它,并将其放在一个永久循环中,应用程序会失去其他常量淡入/淡出动画的性能。有什么建议吗? - James
TransitionDrawable 只能处理两个图像/层。 - Signcodeindie
转换可绘制对象在运行 Espresso 测试时会导致 AppNotIdleException 异常。 - PK Gupta

8

我使用了fadeIn动画来替换旧图像为新图像

ObjectAnimator.ofFloat(imageView, View.ALPHA, 0.2f, 1.0f).setDuration(1000).start();

2
请访问http://android-developers.blogspot.com/2011/05/introducing-viewpropertyanimator.html了解更干净的代码。 - zyamys
谢谢!仍然相关...这正是我现在最需要的! - Teekam Suthar
1
谢谢!这是最好的答案……非常简单易实现,而且运行顺畅。 - user3152377
在我的情况下,我认为更适合使用:(... 0.6f,1f).setDuration(450)...“the cleanest, thank you!” - Acauã Pitta

4

您可以通过两个简单的步骤和代码更改来实现:

1.在项目中的anim文件夹中的xml文件中,设置淡入和淡出持续时间不相等

2.在您的Java类中,在淡出动画开始之前,将第二个imageView的可见性设置为Gone,然后在淡出动画开始后将您想要淡入可见的第二个imageView的可见性设置为可见

fadeout.xml

<alpha
    android:duration="4000"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="0.0" />

fadein.xml

<alpha
    android:duration="6000"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" />

在你的Java类中
Animation animFadeOut = AnimationUtils.loadAnimation(this, R.anim.fade_out);
    ImageView iv = (ImageView) findViewById(R.id.imageView1);
    ImageView iv2 = (ImageView) findViewById(R.id.imageView2);
    iv.setVisibility(View.VISIBLE);
    iv2.setVisibility(View.GONE);
    animFadeOut.reset();
    iv.clearAnimation();
    iv.startAnimation(animFadeOut);

    Animation animFadeIn = AnimationUtils.loadAnimation(this, R.anim.fade_in);
    iv2.setVisibility(View.VISIBLE);
    animFadeIn.reset();
    iv2.clearAnimation();
    iv2.startAnimation(animFadeIn);

3
无限淡入淡出效果
AlphaAnimation fadeIn=new AlphaAnimation(0,1);

AlphaAnimation fadeOut=new AlphaAnimation(1,0);


final AnimationSet set = new AnimationSet(false);

set.addAnimation(fadeIn);
set.addAnimation(fadeOut);
fadeOut.setStartOffset(2000);
set.setDuration(2000);
imageView.startAnimation(set);

set.setAnimationListener(new Animation.AnimationListener() {
    @Override
    public void onAnimationStart(Animation animation) { }
    @Override
    public void onAnimationRepeat(Animation animation) { }
    @Override
    public void onAnimationEnd(Animation animation) {
        imageView.startAnimation(set);
    }
});

1

对我来说最好也最简单的方法是这样的:

->只需创建一个包含sleep()的处理程序线程即可。

private ImageView myImageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_shape_count); myImageView= (ImageView)findViewById(R.id.shape1);
    Animation myFadeInAnimation = AnimationUtils.loadAnimation(this, R.anim.fadein);
    myImageView.startAnimation(myFadeInAnimation);

    new Thread(new Runnable() {
        private Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                Log.w("hendler", "recived");
                    Animation myFadeOutAnimation = AnimationUtils.loadAnimation(getBaseContext(), R.anim.fadeout);
                    myImageView.startAnimation(myFadeOutAnimation);
                    myImageView.setVisibility(View.INVISIBLE);
            }
        };

        @Override
        public void run() {
            try{
                Thread.sleep(2000); // your fadein duration
            }catch (Exception e){
            }
            handler.sendEmptyMessage(1);

        }
    }).start();
}

1

我正在使用这种例程来以编程方式链接动画。

    final Animation anim_out = AnimationUtils.loadAnimation(context, android.R.anim.fade_out); 
    final Animation anim_in  = AnimationUtils.loadAnimation(context, android.R.anim.fade_in); 

    anim_out.setAnimationListener(new AnimationListener()
    {
        @Override
        public void onAnimationStart(Animation animation) {}

        @Override
        public void onAnimationRepeat(Animation animation) {}

        @Override
        public void onAnimationEnd(Animation animation)
        {
            ////////////////////////////////////////
            // HERE YOU CHANGE YOUR IMAGE CONTENT //
            ////////////////////////////////////////
            //ui_image.setImage...

            anim_in.setAnimationListener(new AnimationListener()
            {
                @Override
                public void onAnimationStart(Animation animation) {}

                @Override
                public void onAnimationRepeat(Animation animation) {}

                @Override
                public void onAnimationEnd(Animation animation) {}
            });

            ui_image.startAnimation(anim_in);
        }
    });

    ui_image.startAnimation(anim_out);

1
这可能是你能得到的最好的解决方案。简单易懂。我在Udemy上学到了它。 假设你有两个图片,分别具有图像ID id1 和 id2,当前图像视图设置为id1,每当有人点击时,你想将其更改为另一个图像。所以这是在 MainActivity.java 文件中的基本代码。
int clickNum=0;
public void click(View view){
clickNum++;
ImageView a=(ImageView)findViewById(R.id.id1);
ImageView b=(ImageView)findViewById(R.id.id2);
if(clickNum%2==1){
  a.animate().alpha(0f).setDuration(2000); //alpha controls the transpiracy
}
else if(clickNum%2==0){
  b.animate().alpha(0f).setDuration(2000); //alpha controls the transpiracy
}

}

我希望这一定会有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接