and*_*per 16 animation android image-manipulation android-animation android-kenburnsview
我正在动作栏上实现" KenBurns效果 "(演示在这里),如图书馆的样本所示(移动的图标除外,我自己就是这样做的).
事实上,我甚至在很久以前(这里)问过这个问题,在这一点上我甚至都不知道它的名字.我确信我找到了解决方案,但它有一些问题.
此外,因为我有时从设备显示图像,有的甚至需要旋转,所以我用一个rotatableDrawable(如图所示这里).
当前实现无法处理动态给出的多个位图(例如,来自Internet),甚至不能查看输入图像的大小.
相反,它只是以随机方式进行缩放和平移,因此很多时候它可以缩放太多/很少,并且可以显示空白空间.
这是与问题相关的代码:
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);
}
Run Code Online (Sandbox Code Playgroud)
这是动画本身的一部分,它动画当前的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();
}
Run Code Online (Sandbox Code Playgroud)
如您所见,这不会查看视图/位图大小,只是随机选择如何缩放和平移.
我已经使用动态位图,但我不明白要改变什么,以便它将正确处理大小.
我还注意到有另一个库(这里)可以完成这项工作,但它也有同样的问题,而且更难理解如何在那里修复它们.加上它随机崩溃.这是我报道的关于它的帖子.
为了正确实现Ken-Burns效应应该怎么做才能处理动态创建的位图?
我想也许最好的解决方案是自定义ImageView绘制内容的方式,这样在任何给定的时间,它都会显示给它的位图的一部分,真正的动画将在两个矩形之间的位图.可悲的是,我不知道该怎么做.
同样,问题不在于获取位图或解码.这是关于如何使它们在没有崩溃或奇怪的放大/缩小显示空白空间的情况下使用此效果.
Xav*_*ler 11
我已经看过了它的源代码,KenBurnsView实际上并没有那么难实现你想要的功能,但我必须首先澄清一些事情:
当前的实现无法处理动态给出的多个位图(例如,来自Internet),...
如果您知道自己在做什么,从互联网上动态下载图像并不困难,但有很多方法可以做到.许多人实际上并没有提出他们自己的解决方案,而是使用像Volley这样的网络库来下载图像,或者直接使用Picasso或类似的东西.就个人而言,我大多使用自己的帮助程序类,但你必须决定你想要下载图像的确切程度.使用毕加索这样的库很可能是最适合您的解决方案.我在这个答案中的代码示例将使用Picasso库,这是一个如何使用Picasso的简单示例:
Picasso.with(context).load("http://foo.com/bar.png").into(imageView);
Run Code Online (Sandbox Code Playgroud)
...甚至没有看输入图像的大小.
我真的不明白你的意思.在内部,KenBurnsView用途ImageViews来显示图像.它们负责正确缩放和显示图像,并且它们肯定会考虑图像的大小.我认为你的困惑可能是由于scaleType为此而设置的ImageViews.如果你看一下R.layout.view_kenburns包含你的布局的布局文件,KenBurnsView你会看到:
<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>
Run Code Online (Sandbox Code Playgroud)
请注意,有两个ImageViews而不是一个来创建交叉淡化效果.重要的部分是这个标签,可以在以下两个标签上找到ImageViews:
android:scaleType="centerCrop"
Run Code Online (Sandbox Code Playgroud)
这样做是告诉ImageView:
ImageViewImageViewImageView它将被裁剪为的大小ImageView因此,在其当前状态下,KenBurnsView可以一直裁剪其中的图像.如果您希望图像缩放以完全适合内部,ImageView那么无需裁剪或删除任何内容,您需要将其更改scaleType为以下两者之一:
android:scaleType="fitCenter"android:scaleType="centerInside"我不记得这两者之间的确切差异,但是它们都应该具有缩放图像所需的效果,因此它同时适合在X和Y轴内ImageView同时将其居中ImageView.
重要提示:改变scaleType潜在的混乱KenBurnsView!
如果你真的只是用KenBurnsView来显示两个图像,则更改scaleType将不会从如何图像显示事不谈,但如果你调整KenBurnsView-例如在动画-和ImageViews有scaleType设定为比其他的东西centerCrop,你将失去的视差效应!使用centerCrop作为scaleType的ImageView是一个快速简便的方法来创建视差般的效果.这个技巧的缺点可能是你注意到的:遗嘱中的图像ImageView最有可能被裁剪而不是完全可见!
如果你看一下布局,你可以看到,所有Views在那里有match_parent作为layout_height和layout_width.对于某些图像而言,这也可能是一个问题,因为当图像明显小于或大于match_parent约束时,约束scaleTypes有时会产生奇怪的结果ImageView.
翻译动画也考虑了图像的大小 - 或者至少是图像的大小ImageView.如果你看一下源代码animate(...)和pickTranslation(...)你会看到:
// 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);
}
Run Code Online (Sandbox Code Playgroud)
因此,视图已经考虑了图像大小以及在计算翻译时缩放图像的程度.所以这个工作原理的概念是可以的,我看到的唯一问题是开始值和结束值都是随机的,两个值之间没有任何依赖关系.这意味着一件简单的事情:动画的起点和终点可能是完全相同的位置,也可能彼此非常接近.因此,动画有时可能非常重要,而其他时候根本不会引起注意.
我可以想出三种主要方法来解决这个问题:
fromScale应该之间的随机值1.2f和1.4f与toScale应之间的随机值1.6f和1.8f.无论您选择方法#1还是#2,您都需要这种方法:
// Returns a random value between min and max
private float randomRange(float min, float max) {
return random.nextFloat() * (max - min) + max;
}
Run Code Online (Sandbox Code Playgroud)
在这里,我修改了animate()方法以强制动画的起点和终点之间的某个距离:
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);
}
Run Code Online (Sandbox Code Playgroud)
如您所见,我只需要修改方式fromScale和toScale计算方法,因为翻译值是根据比例值计算的.这不是100%修复,但它是一个很大的改进.
KenBurnsView要解决这个问题,KenBurnsView您可以实施我上面提到的建议.此外,我们需要实现一种动态添加图像的方法.KenBurnsView处理图像的实现方式有点奇怪.我们需要稍微修改一下.由于我们使用Picasso,这实际上非常简单:
基本上你只需要修改swapImage()方法,我像这样测试它并且它正在工作:
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");
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
我省略了一些琐碎的部分,urlList只是一个List<String>包含我们想要显示的图像的所有网址,urlIndex用于循环通过urlList.我把动画移到了Callback.这样,图像将在后台下载,并且一旦成功下载图像,动画将播放并且ImageViews将交叉淡入淡出.KenBurnsView现在可以删除许多旧代码,例如方法setResourceIds()或fillImageViews()现在不需要.
KenBurnsView+毕加索您链接到的第二个库,这个库实际上包含了更多KenBurnsView.在KenBurnsView我讲上面的一个子类FrameLayout,并有与此方法有几个问题View需要.在KenBurnsView从第二个库是的子类ImageView,这已经是一个巨大的进步.因此我们可以直接在Picasso上使用图像加载器库KenBurnsView,我们不必自己处理任何事情.你说你遇到第二个库的随机崩溃?在过去的几个小时里,我一直在广泛地测试它,并没有遇到任何一次崩溃.
随着KenBurnsView从第二库和毕加索这一切都变得非常容易,代码非常几行,你只需要创建一个KenBurnsView例如XML:
<com.flaviofaria.kenburnsview.KenBurnsView
android:id="@+id/kbvExample"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/image" />
Run Code Online (Sandbox Code Playgroud)
然后在Fragment你的首先必须在布局中找到视图然后在onViewCreated()我们用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);
}
Run Code Online (Sandbox Code Playgroud)
我测试了运行Android 4.4.2的Nexus 5上的所有内容.自从ViewPropertyAnimators使用以来,这应该在API级别16以下可能兼容,甚至可能是12.
我在这里和那里省略了几行代码,所以如果你有任何问题可以随意提问!
| 归档时间: |
|
| 查看次数: |
7084 次 |
| 最近记录: |