对Android的KenBurns效果实现动态位图设置 [英] Implementation of KenBurns effect on Android with dynamic bitmaps setting

查看:398
本文介绍了对Android的KenBurns效果实现动态位图设置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景

我工作的一个实施 KenBurns效果 (演示<一个href="http://en.wikipedia.org/wiki/File%3aKen_Burns_Effect_demonstration.ogg">here)在操作栏,如图上 这个库 的样品(除了移动的图标,我已经做了我自己)。

其实,我连问了一下它在很久以前(<一href="http://stackoverflow.com/questions/15473969/how-to-slowly-scroll-a-zoomed-in-image-like-in-a-$p$psentation">here),它在这一点上我甚至不知道它的名字,我相信我已经找到了解决办法,但它也存在一些问题。

此外,因为我有时会显示从设备,有的甚至需要旋转图像,所以我用一个rotatableDrawable(如图所示的 此处 )。

问题

目前的实现不能处理动态给出(来自互联网,例如)多个位图,甚至不看输入图像大小。

相反,它只是做了缩放和平移以随机方式,因此很多时候它可以放大太多/少了,空出的空间可以显示。

的code

这里的code,它涉及到的问题:

 专用浮pickScale(){
    返回MIN_SCALE_FACTOR + this.random.nextFloat()*(MAX_SCALE_FACTOR  -  MIN_SCALE_FACTOR);
}

私人浮动pickTranslation(最终诠释价值,最终浮动比率){
    返回值*(比 -  1.0F)*(this.random.nextFloat() -  0.5F);
}

公共无效动画(最终ImageView的视图){
    最终浮动fromScale = pickScale();
    最终浮动toScale = pickScale();
    最终浮动fromTranslationX = pickTranslation(view.getWidth(),fromScale);
    最终浮动fromTranslationY = pickTranslation(view.getHeight(),fromScale);
    最终浮动toTranslationX = pickTranslation(view.getWidth(),toScale);
    最终浮动toTranslationY = pickTranslation(view.getHeight(),toScale);
    启动(视图,KenBurnsView.DELAY_BETWEEN_IMAGE_SWAPPING_IN_MS,fromScale,toScale,fromTranslationX,
            fromTranslationY,toTranslationX,toTranslationY);
}
 

和这里的动画本身,而动画当前的ImageView的一部分:

 私人无效的开始(查看查看,持续时间长,浮动fromScale,浮toScale,浮fromTranslationX,浮fromTranslationY,浮toTranslationX,浮toTranslationY){
    view.setScaleX(fromScale);
    view.setScaleY(fromScale);
    view.setTranslationX(fromTranslationX);
    view.setTranslationY(fromTranslationY);
    ViewPropertyAnimator propertyAnimator = view.animate().translationX(toTranslationX).translationY(toTranslationY).scaleX(toScale).scaleY(toScale).setDuration(duration);
    propertyAnimator.start();
}
 

正如你所看到的,这不看视图/位图的大小,只是随机选择如何缩放和平移。

我已经试过

我做了它与动态位图的工作​​,但我不明白,在其上改变什么,这样它会正确处理的大小。

我也注意到有另一个库( 此处 ),做这项工作,但它也有同样的问题,这是更加不明白如何解决这些问题存在。此外它随机崩溃。 这里我报一个关于它的帖子。

问题

我应该以正确执行肯烧伤效果来实现,因此,它可以处理动态创建位图?

我在想,也许是最好的解决方案是定制的ImageView绘制它的内容,因此在任何给定的时间,它会显示给它的位图的一部分的方式,真正的动画会间位图的两个矩形。可悲的是,我不知道如何做到这一点。

此外,这个问题是不是得到位图或解码。它是关于如何使他们没有这表明空格崩溃或怪异的放大/缩小这种影响正常工作。

解决方案

我看 KenBurnsView 来源$ C ​​$ C,这是不实际的很难实现你想要的功能,但也有几件事情我必须先澄清:


1。加载图像动态

  

目前的实现不能处理多个位图是   动态地给(来自互联网,例如),...

这不是很难动态地从互联网上,如果你知道你在做什么下载图像,但也有很多方法可以做到这一点。很多人并没有真正拿出自己的解决方案,但使用如 排球 一个网络图书馆下载图片或者直行 毕加索 或类似的东西。我个人主要是用我自己的一套辅助类的,但你必须决定你要如何精确地下载图像。使用库如 毕加索 是最有可能最适合您的解决方案。在这个答案我的code样品将使用 毕加索 库,这里是一个简单的例子如何使用 毕加索

  Picasso.with(上下文).load(http://foo.com/bar.png)。走进(ImageView的);
 


2。图像尺寸

  

...甚至不看输入图像大小。

我真的不明白你的意思。在内部, KenBurnsView 使用 ImageViews 来显示图像。他们采取适当的缩放和显示图像的关心和他们肯定拍摄的图像的大小考虑进去。我想你的困惑可能是由于引起 scaleType 被设置为 ImageViews 。如果你看一下布局文件 R.layout.view_kenburns ,其中包含的布局 KenBurnsView 您看到这一点:

 &LT;的FrameLayout的xmlns:机器人=htt​​p://schemas.android.com/apk/res/android
    机器人:layout_width =match_parent
    机器人:layout_height =match_parent&GT;

    &LT; ImageView的
        机器人:ID =@ + ID / image0
        机器人:scaleType =centerCrop
        机器人:layout_width =match_parent
        机器人:layout_height =match_parent/&GT;

    &LT; ImageView的
        机器人:ID =@ + ID /此搜索
        机器人:scaleType =centerCrop
        机器人:layout_width =match_parent
        机器人:layout_height =match_parent/&GT;
&LT; /的FrameLayout&GT;
 

请注意,有两个 ImageViews ,而不是只是一个创建交叉淡入淡出的效果。最重要的部分是这样的标签,该标签上都找到 ImageViews

 安卓scaleType =centerCrop
 

这样做是告诉的ImageView 来:

  1. 在中心内的图像的ImageView
  2. 缩放图像,其宽度内适合的的ImageView
  3. 如果图像比更高的的ImageView 将被裁剪为的ImageView

因此​​,在当前状态下在 KenBurnsView 可在任何时候都裁剪图像。如果你想要的图像缩放到适合完全在的ImageView 因此不需要进行裁剪或删除您需要更改 scaleType 这两个中的一个:

  • 安卓scaleType =fitCenter
  • 安卓scaleType =centerInside

我不记得那两个之间的确切区别,但他们都应该有缩放图像,使其适合既在X轴和Y轴的的ImageView ,而在同一时间围绕它里面的的ImageView

重要提示:更改 scaleType 可能搅乱 KenBurnsView
如果你真的只需要使用 KenBurnsView 来显示两个图像,然后更改 scaleType 也不会不管除了如何图片显示,但是如果你调整 KenBurnsView - 例如在动画 - 和 ImageViews scaleType 设置为比 centerCrop 其他的东西,你将失去的视差效果!使用 centerCrop scaleType 的ImageView 是一个快速,简单的方法来创建视差般的效果。这一招的缺点可能是你发现了什么: ImageView的图像将最有可能被裁剪并没有完全可见

如果你看一下布局,你可以看到,所有浏览在那里有 match_parent layout_height layout_width 。这也可能是某些影像为 match_parent 约束和某些 scaleTypes 有时会产生当图像奇怪的结果有问题相当小或大于该的ImageView


该转换动画也需要对图像的大小考虑 - 或者至少是的ImageView 的大小。如果你看一下源$ C ​​$ C 动画(...) pickTranslation(...)你会看到这样的:

  //这是传递给pickTranslation()的值是视图的大小!
私人浮动pickTranslation(最终诠释价值,最终浮动比率){
    返回值*(比 -  1.0F)*(this.random.nextFloat() -  0.5F);
}

公共无效动画(最终ImageView的视图){
    最终浮动fromScale = pickScale();
    最终浮动toScale = pickScale();

    //根据宽度或高度被传递给pickTranslation轴线()
    最终浮动fromTranslationX = pickTranslation(view.getWidth(),fromScale);
    最终浮动fromTranslationY = pickTranslation(view.getHeight(),fromScale);
    最终浮动toTranslationX = pickTranslation(view.getWidth(),toScale);
    最终浮动toTranslationY = pickTranslation(view.getHeight(),toScale);

    启动(视图,KenBurnsView.DELAY_BETWEEN_IMAGE_SWAPPING_IN_MS,fromScale,toScale,fromTranslationX,fromTranslationY,toTranslationX,toTranslationY);
}
 

于是视图已占图像大小和多少计算翻译时对图像进行缩放。所以,这是如何工作的概念是好的,我看到的唯一的问题是,无论是开始和结束的值是随机的,而不这两个值之间有任何相关性。这意味着一个简单的事情:动画的起点和终点可能是完全相同的位置,或者可能会非常接近对方。作为一个结果,动画有时会非常显著和其他时间几乎没有明显的。

我能想到的修复方式主要有三种:

  1. 而不是两个随机化的开始和结束值,你只是随机化 起始值并根据起始值挑最终值。
  2. 您保留所有随机化值的当前策略,但你强加给每个值范围的限制。例如, fromScale 应该是之间的随机值 1.2F 1.4f toScale 应>的 1.6F 1.8F
  3. 实施一个固定的平移和缩放动画(换句话说无聊的样子)。

无论您选择的方法#1或#2你会需要这个方法:

  //返回的最小值和最大值之间的随机值
私人浮动randomRange(浮分钟,浮动最大){
    返回random.nextFloat()*(最大值 - 最小值)+最大;
}
 

在这里,我已经修改了动画()方法强制动画的起点和终点之间有一定的距离:

 公共无效动画(查看视图){
    最终浮动fromScale = randomRange(1.2F,1.4f);
    最终浮动fromTranslationX = pickTranslation(view.getWidth(),fromScale);
    最终浮动fromTranslationY = pickTranslation(view.getHeight(),fromScale);


    最终浮动toScale = randomRange(1.6F,1.8F);
    最终浮动toTranslationX = pickTranslation(view.getWidth(),toScale);
    最终浮动toTranslationY = pickTranslation(view.getHeight(),toScale);

    启动(视图,this.mSwapMs,fromScale,toScale,fromTranslationX,fromTranslationY,toTranslationX,toTranslationY);
}
 

正如你所见,我只需要修改如何 fromScale toScale 的计算,因为翻译值的计算从刻度值。这不是一个100%解决,但它是一个很大的进步。


3。解决方案1:修复 KenBurnsView

(使用解决方案#2如果可能的话)

要解决我上面提到的 KenBurnsView 可以实现的建议。此外,我们还需要实现一种方式被动态地添加的图像。如何 KenBurnsView 处理图像的实施是一个有点怪异。我们将需要修改了一下。由于我们使用 毕加索 这实际上将是pretty的简单:

从本质上讲,你只需要修改 swapImage()的方法,我测试了它这样的,它是工作:

 私人无效swapImage(){
    如果(this.urlList.size()大于0){
        如果(mActiveImageIndex == -1){
            mActiveImageIndex = 1;
            动画(mImageViews [mActiveImageIndex]);
            返回;
        }

        最终诠释inactiveIndex = mActiveImageIndex;
        mActiveImageIndex =(1 + mActiveImageIndex)%mImageViews.length;
        Log.d(TAG,新的主动=+ mActiveImageIndex);

        字符串URL = this.urlList.get(this.urlIndex ++);
        this.urlIndex = this.urlIndex%this.urlList.size();

        最后ImageView的activeImageView = mImageViews [mActiveImageIndex]
        activeImageView.setAlpha(0.0);

        Picasso.with(this.context).load(URL)。走进(activeImageView,新的回调(){

            @覆盖
            公共无效的onSuccess(){
                ImageView的inactiveImageView = mImageViews [inactiveIndex]

                动画(activeImageView);

                AnimatorSet animatorSet =新AnimatorSet();
                animatorSet.setDuration(mFadeInOutMs);
                animatorSet.playTogether(
                        ObjectAnimator.ofFloat(inactiveImageView,阿尔法,1.0F,0.0),
                        ObjectAnimator.ofFloat(activeImageView,阿尔法,0.0,1.0F)
                );
                animatorSet.start();
            }

            @覆盖
            公共无效onerror的(){
                Log.i(LOG_TAG,无法下载一个图像);
            }
        });
    }
}
 

我省略了一些微不足道的部分, urlList 只是一个名单,其中,字符串&GT; 里面包含了所有的网址我们所要显示的图像, urlIndex 通过 urlList 用于循环。我搬到动画到回调。这样的图像将在后台下载,一旦图像已被成功下载的动画将发挥与 ImageViews 将交叉衰减。很多从 KenBurnsView 现在可以删除,例如方法老code setResourceIds() fillImageViews()现在是不必要的。


4。解决方案#2:更好的 KenBurnsView +毕加索

您链接到第二个库, 这个 ,实际上包含了一个更好的 KenBurnsView 。该 KenBurnsView 我说说上面的FrameLayout 的子类,有几个问题与方法本查看需要。该 KenBurnsView 第二个库 的ImageView 的子类,这已经是一个巨大的进步。它,因为我们可以使用图像加载器库,比如 毕加索 直接在 KenBurnsView ,我们没有采取任何自我保健。你说你遇到随机崩溃,并 第二个库 ?我一直在测试它相当广泛的最后几个小时,没有遇到一个单一的崩溃。

随着 KenBurnsView 第二个库 毕加索 这一切都变得非常容易和code非常几行你,只需要创建一个 KenBurnsView 例如XML:

 &LT; com.flaviofaria.kenburnsview.KenBurnsView
    机器人:ID =@ + ID / kbvExample
    机器人:layout_width =match_parent
    机器人:layout_height =match_parent
    机器人:SRC =@可绘制/图像/&GT;
 

,然后在片段你首先要找到在布局视图,然后在 onViewCreated()我们加载图像毕加索:

 私人KenBurnsView kbvExample;

@覆盖
公共查看onCreateView(LayoutInflater充气,容器的ViewGroup,捆绑savedInstanceState){
    查看查看= inflater.inflate(R.layout.fragment_kenburns_test,集装箱,假);

    this.kbvExample =(KenBurnsView)view.findViewById(R.id.kbvExample);

    返回查看;
}

@覆盖
公共无效onViewCreated(查看视图,捆绑savedInstanceState){
    super.onViewCreated(查看,savedInstanceState);

    。Picasso.with(getActivity())负载(IMAGE_URL)。走进(this.kbvExample);
}
 

5。测试

我在我的Nexus 5运行的是Android 4.4.2测试一切。由于 ViewPropertyAnimators 用于这应该都是兼容的某处以API级别16,甚至12

我有一个省略了几行code在这里和那里,所以如果你有任何问题随时问!

Background

I am working on an implementation of the "KenBurns effect" (demo here) on the action bar , as shown on this library's sample (except for the icon that moves, which I've done so myself).

In fact, I even asked about it a long time ago (here), which at this point I didn't even know its name. I was sure I've found a solution, but it has some problems.

Also, since I sometimes show the images from the device, some of them even need to be rotated, so I use a rotatableDrawable (as shown here).

The problem

The current implementation cannot handle multiple bitmaps that are given dynamically (from the Internet, for example), and doesn't even look at the input images' size.

Instead, it just does the zooming and translation in a random way, so many times it can zoom too much/little, and empty spaces can be shown.

The code

Here's the code that is related to the problems:

private float pickScale() {
    return MIN_SCALE_FACTOR + this.random.nextFloat() * (MAX_SCALE_FACTOR - MIN_SCALE_FACTOR);
}

private float pickTranslation(final int value, final float ratio) {
    return value * (ratio - 1.0f) * (this.random.nextFloat() - 0.5f);
}

public void animate(final ImageView view) {
    final float fromScale = pickScale();
    final float toScale = pickScale();
    final float fromTranslationX = pickTranslation(view.getWidth(), fromScale);
    final float fromTranslationY = pickTranslation(view.getHeight(), fromScale);
    final float toTranslationX = pickTranslation(view.getWidth(), toScale);
    final float toTranslationY = pickTranslation(view.getHeight(), toScale);
    start(view, KenBurnsView.DELAY_BETWEEN_IMAGE_SWAPPING_IN_MS, fromScale, toScale, fromTranslationX,
            fromTranslationY, toTranslationX, toTranslationY);
}

And here's the part of the animation itself, which animates the current ImageView:

private void start(View view, long duration, float fromScale, float toScale, float fromTranslationX, float fromTranslationY, float toTranslationX, float toTranslationY) {
    view.setScaleX(fromScale);
    view.setScaleY(fromScale);
    view.setTranslationX(fromTranslationX);
    view.setTranslationY(fromTranslationY);
    ViewPropertyAnimator propertyAnimator = view.animate().translationX(toTranslationX).translationY(toTranslationY).scaleX(toScale).scaleY(toScale).setDuration(duration);
    propertyAnimator.start();
}

As you can see, this doesn't look at the view/bitmap sizes, and just randomly selects how to zoom and pan.

What I've tried

I've made it work with dynamic bitmaps, but I don't understand what to change on it so that it will handle the sizes correctly.

I've also noticed there is another library (here) that does this work, but it also has the same problems, and it's even harder to understand how to fix them there. Plus it randomly crashes . Here's a post I've reported about it.

The question

What should be done in order to implement Ken-Burns effect correctly, so that it could handle dynamically created bitmaps?

I'm thinking that maybe the best solution is to customize the way the ImageView draws its content, so that at any given time, it will show a part of the bitmap that is given to it, and the real animation would be between two rectangles of the bitmap . Sadly, I'm not sure how to do this.

Again, the question isn't about getting bitmaps or decoding. It's about how to make them work well with this effect without crashes or weird zoom in/out which show empty spaces.

解决方案

I have look at the source code of the KenBurnsView and it isn't actually that hard to implement the features you want, but there are a few things I have to clarify first:


1. Loading images dynamically

The current implementation cannot handle multiple bitmaps that are given dynamically (from the Internet, for example),...

It isn't difficult to download images dynamically from the internet if you know what you are doing, but there are many ways to do it. Many people don't actually come up with their own solution but use a networking library like Volley to download the image or they go straight for Picasso or something similar. Personally I mostly use my own set of helper classes but you have to decide how exactly you want to download the images. Using a library like Picasso is most likely the best solution for you. My code samples in this answer will use the Picasso library, here is a quick example of how to use Picasso:

Picasso.with(context).load("http://foo.com/bar.png").into(imageView);


2. Image Size

...and doesn't even look at the input images' size.

I really don't understand what you mean by that. Internally the KenBurnsView uses ImageViews to display the images. They take care of properly scaling and displaying the image and they most certainly take the size of the images into account. I think your confusion might be caused by the scaleType which is set for the ImageViews. If you look at the layout file R.layout.view_kenburns which contains the layout of the KenBurnsView you see this:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/image0"
        android:scaleType="centerCrop"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageView
        android:id="@+id/image1"
        android:scaleType="centerCrop"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</FrameLayout>

Notice that there are two ImageViews instead of just one to create the crossfade effect. The important part is this tag which is found on both ImageViews:

android:scaleType="centerCrop"

What this does is tell the ImageView to:

  1. Center the image inside the ImageView
  2. Scale the image so its width fits inside the ImageView
  3. If the image is taller than the ImageView it will be cropped to the size of the ImageView

So in its current state the images inside the KenBurnsView may be cropped at all times. If you want the image to scale to fit completely inside the ImageView so nothing has to be cropped or removed you need to change the scaleType to one of those two:

  • android:scaleType="fitCenter"
  • android:scaleType="centerInside"

I don't remember the exact difference between those two, but they should both have the desired effect of scaling the image so it fits both on the X and Y axis inside the ImageView while at the same time centering it inside the ImageView.

IMPORTANT: Changing the scaleType potentially messes up the KenBurnsView!
If you really just use the KenBurnsView to display two images then changing the scaleType won't matter aside from how the images are displayed, but if you resize the KenBurnsView - for example in an Animation - and the ImageViews have the scaleType set to something other than centerCrop you will loose the parallax effect! Using centerCrop as scaleType of an ImageView is a quick and easy way to create parallax-like effects. The drawback of this trick is probably what you noticed: The image in the ImageView will most likely be cropped and not completely visible!

If you look at the layout you can see that all Views in there have match_parent as layout_height and layout_width. This could also be a problem for certain images as the match_parent constraint and certain scaleTypes sometimes produce strange results when the images are considerably smaller or larger than the ImageView.


The translate animation also takes the size of the image into account - or at least the size of the ImageView. If you look at the source code of animate(...) and pickTranslation(...) you will see this:

// The value which is passed to pickTranslation() is the size of the View!
private float pickTranslation(final int value, final float ratio) {
    return value * (ratio - 1.0f) * (this.random.nextFloat() - 0.5f);
}

public void animate(final ImageView view) {
    final float fromScale = pickScale();
    final float toScale = pickScale();

    // Depending on the axis either the width or the height is passed to pickTranslation()
    final float fromTranslationX = pickTranslation(view.getWidth(), fromScale);
    final float fromTranslationY = pickTranslation(view.getHeight(), fromScale);
    final float toTranslationX = pickTranslation(view.getWidth(), toScale);
    final float toTranslationY = pickTranslation(view.getHeight(), toScale);

    start(view, KenBurnsView.DELAY_BETWEEN_IMAGE_SWAPPING_IN_MS, fromScale, toScale, fromTranslationX, fromTranslationY, toTranslationX, toTranslationY);
}

So the view already accounts for the images size and how much the image is scaled when calculating the translation. So the concept of how this works is okay, the only problem I see is that both the start and end values are randomised without any dependencies between those two values. What this means is one simple thing: The start and endpoint of the animation might be the exact same position or may be very close to each other. As a result of that the animation may sometimes be very significant and other times barely noticeable at all.

I can think of three main ways to fix that:

  1. Instead of randomising both start and end values you just randomise the start values and pick the end values based on the start values.
  2. You keep the current strategy of randomising all values, but you impose range restrictions on each value. For example the fromScale should be a random value between 1.2f and 1.4f and toScale should be a random value between 1.6f and 1.8f.
  3. Implement a fixed translation and scale animation (In other words the boring way).

Whether you choose approach #1 or #2 you are going to need this method:

// Returns a random value between min and max
private float randomRange(float min, float max) {
    return random.nextFloat() * (max - min) + max;
}

Here I have modified the animate() method to force a certain distance between start and end points of the animation:

public void animate(View view) {
    final float fromScale = randomRange(1.2f, 1.4f);
    final float fromTranslationX = pickTranslation(view.getWidth(), fromScale);
    final float fromTranslationY = pickTranslation(view.getHeight(), fromScale);


    final float toScale =  randomRange(1.6f, 1.8f);
    final float toTranslationX = pickTranslation(view.getWidth(), toScale);
    final float toTranslationY = pickTranslation(view.getHeight(), toScale);

    start(view, this.mSwapMs, fromScale, toScale, fromTranslationX, fromTranslationY, toTranslationX, toTranslationY);
}

As you can see I only need to modify how fromScale and toScale are calculated because the translations values are calculated from the scale values. This is not a 100% fix, but it is a big improvement.


3. Solution #1: Fixing KenBurnsView

(Use solution #2 if possible)

To fix the KenBurnsView you can implement the suggestions I mentioned above. Additionally we need to implement a way for the images to be added dynamically. The implementation of how the KenBurnsView handles images is a little weird. We are going to need to modify that a bit. Since we are using Picasso this is actually going to be pretty simple:

Essentially you just need to modify the swapImage() method, I tested it like this and it is working:

private void swapImage() {
    if (this.urlList.size() > 0) {
        if(mActiveImageIndex == -1) {
            mActiveImageIndex = 1;
            animate(mImageViews[mActiveImageIndex]);
            return;
        }

        final int inactiveIndex = mActiveImageIndex;
        mActiveImageIndex = (1 + mActiveImageIndex) % mImageViews.length;
        Log.d(TAG, "new active=" + mActiveImageIndex);

        String url = this.urlList.get(this.urlIndex++);
        this.urlIndex = this.urlIndex % this.urlList.size();

        final ImageView activeImageView = mImageViews[mActiveImageIndex];
        activeImageView.setAlpha(0.0f);

        Picasso.with(this.context).load(url).into(activeImageView, new Callback() {

            @Override
            public void onSuccess() {
                ImageView inactiveImageView = mImageViews[inactiveIndex];

                animate(activeImageView);

                AnimatorSet animatorSet = new AnimatorSet();
                animatorSet.setDuration(mFadeInOutMs);
                animatorSet.playTogether(
                        ObjectAnimator.ofFloat(inactiveImageView, "alpha", 1.0f, 0.0f),
                        ObjectAnimator.ofFloat(activeImageView, "alpha", 0.0f, 1.0f)
                );
                animatorSet.start();
            }

            @Override
            public void onError() {
                Log.i(LOG_TAG, "Could not download next image");
            }
        });        
    }
}

I have omitted a few trivial parts, urlList is just a List<String> which contains all the urls to the images we want to display, urlIndex is used to cycle through the urlList. I moved the animation into the Callback. That way the image will be downloaded in the background and as soon as the image has been downloaded successfully the animations will play and the ImageViews will crossfade. A lot of the old code from the KenBurnsView can now be deleted, for example the methods setResourceIds() or fillImageViews() are now unnecessary.


4. Solution #2: Better KenBurnsView + Picasso

The second library you link to, this one, actually contains a MUCH better KenBurnsView. The KenBurnsView I talk about above is a subclass of FrameLayout and there are a few problems with the approach this View takes. The KenBurnsView from the second library is a subclass of ImageView, this is already a huge improvement. Because of it we can use image loader libraries like Picasso directly on the KenBurnsView and we don't have to take care of anything ourselves. You say that you experience random crashes with the second library? I have been testing it rather extensively the last few hours and didn't encounter a single crash.

With the KenBurnsView from the second library and Picasso this all becomes very easy and very few lines of code, you just have to create a KenBurnsView for example in xml:

<com.flaviofaria.kenburnsview.KenBurnsView
    android:id="@+id/kbvExample"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:src="@drawable/image" />

And then in your Fragment you first have to find the view in the layout and then in onViewCreated() we load the image with Picasso:

private KenBurnsView kbvExample;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.fragment_kenburns_test, container, false);

    this.kbvExample = (KenBurnsView) view.findViewById(R.id.kbvExample);

    return view;
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    Picasso.with(getActivity()).load(IMAGE_URL).into(this.kbvExample);
}

5. Testing

I tested everything on my Nexus 5 running Android 4.4.2. Since ViewPropertyAnimators are used this should all be compatible somewhere down to API Level 16, maybe even 12.

I have a omitted a few lines of code here and there so if you have any questions feel free to ask!

这篇关于对Android的KenBurns效果实现动态位图设置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆