动画兼容

由于更大厂商的rom奇怪,而且android手机的升级不及时,导致必须考虑各种啃爹的兼容。以下总结遇到的各种兼容的解决。

makeSceneTransitionAnimation动画

效果如下:

下面我使用的是:ActivityOptionsICS
提供的兼容库,不过已经停止了维护了。他里面的提供的demo是eclipse的。还需要自行变成as的方式。

下面记录下其步骤和注意点:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
/**
* 这里可以用多个元素或者是单个元素进行动画,这里用了多个元素。为了保证动画效果,这里进行了渐变操作,
* 在handle中进行了恢复操作,这样让动画看起来平滑了很多
* @param views
*/

@SuppressWarnings("unchecked")
public void screenTransitAnimByPair(Pair<View, Integer>... views) {
isSceneAnim = true;
Intent intent = ProductDetailActivity.makeIntent(getContext(), product.getId());

ByteArrayOutputStream stream = new ByteArrayOutputStream();
Bitmap productBmp = null;
Drawable drawable = itemGoodsListBinding.ivImage.getDrawable();
if (drawable instanceof BitmapDrawable) {
productBmp = ((BitmapDrawable) drawable).getBitmap();
} else {
Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
productBmp = bitmap;
}

productBmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
intent.putExtra("image", stream.toByteArray());
ActivityOptionsCompatICS options = ActivityOptionsCompatICS.makeSceneTransitionAnimation(
MainActivity.this, views);
ActivityCompatICS.startActivity(MainActivity.this, intent, options.toBundle());
}

调用:

1
2
3
4
screenTransitAnimByPair(
Pair.create((View)orginalImageView, R.id.target_imageView),//这里的idres是target的,这里必须要注意
Pair.create((View)orginalTextView, R.id.target_textView),
Pair.create((View)chromeIView, R.id.target_chrome_imageView));

注意这里再次声明传入的id必须是跳转到下一个act的imageview的id.

另外需要使用handle进行动画的通知显示和消失的操作

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* handler 更新UI
*/

public static class ViewRefreshHandler extends Handler {
private WeakReference<View> viewHolder;

public ViewRefreshHandler() {
}

public void bindView(View v) {
if (viewHolder != null && viewHolder.get() != null) {
viewHolder.clear();
viewHolder = null;
}
viewHolder = new WeakReference<>(v);
}

@Override
public void handleMessage(Message msg) {
if (msg.what == 123) {
//隐藏
if (viewHolder != null && viewHolder.get() != null) {
viewHolder.get().setVisibility(View.INVISIBLE);
}
} else if (msg.what == 321) {
//显示
if (viewHolder != null && viewHolder.get() != null) {
viewHolder.get().setVisibility(View.VISIBLE);
}
isSceneAnim = false;
}
}
}

对应的tagetAct:

设置动画

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
private void initAnimListener() {
/**
* view动画的监听器,比如thumbNailScaleAnim,screenTransitAnim这样的动画就会在这里得到监听
*
* 这里的值是说明动画进行到什么时候,原始的view开始显示
* 设置方式是:动画时间越长,可以设置的越精细,越靠近1,动画时间越短设置为0.95就差不多了
* 这里的值请自行根据你的动画长度进行调整,如果调整不好可能会出现动画结束后相应元素不见的问题。
* 这里测试是如果动画是2000ms,那么用0.998较为合适
*/

final float fraction = 0.9f;
TransitionCompat.addViewAnimListener(new ViewAnimationListenerAdapter() {
boolean isShowed = false;

@Override
public void onViewAnimationStart(View view, Animator animator) {
// TODO 自动生成的方法存根
super.onViewAnimationStart(view, animator);
if (ProductListAdapter.isSceneAnim && TransitionCompat.isEnter) {
ProductListAdapter.handler.sendEmptyMessage(123);
}
}

public void onViewAnimationUpdate(View view, ValueAnimator valueAnimator, float progress) {
super.onViewAnimationUpdate(view, valueAnimator, progress);
// 判断当前是否是进入的状态,如果是进入的那么isEnter= true
if (ProductListAdapter.isSceneAnim && !TransitionCompat.isEnter
&& progress >= fraction && !isShowed) {
ProductListAdapter.handler.sendEmptyMessage(321);
isShowed = true;
}
}

@Override
public void onViewAnimationEnd(View view, Animator animator) {
// TODO 自动生成的方法存根
super.onViewAnimationEnd(view, animator);
if (!TransitionCompat.isEnter && !isShowed) {
ProductListAdapter.handler.sendEmptyMessage(321);
isShowed = true;
}
binding.ivTargetImage.setImageBitmap(bmp);
}

@Override
public void onViewAnimationNotRunning() {
if (!TransitionCompat.isEnter && !isShowed) {
ProductListAdapter.handler.sendEmptyMessage(321);
isShowed = true;
}
}
});

/**
* 屏幕(场景)动画的监听器,这里用了适配器模式。可以传入完整的接口实现类
*/

TransitionCompat.addListener(new TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Animator animator, Animation animation,
boolean isEnter)
{

super.onTransitionEnd(animator, animation, isEnter);
// TODO:onEnd
}
});

// 这段代码必须放在ActivityOptionsCompat各种设置之后 TransitionCompat.startTransition(this,R.layout.activity_goods_details);
TransitionCompat.startTransition(this, R.layout.activity_goods_details, listener);
}

注意这种代码最后是添加一个listener,为什么需要添加这个listener呢,查看源码demo就是知道,他的imageview是直接设置android:src,并不是通过intent传递的。那么就需要在imageview得到intent后,设置bitmap后进行展示动画效果了。否则没有任何效果.

1
2
3
4
5
6
7
8
9
10
11
12
13
TransitionCompat.SetDataListener listener = new TransitionCompat.SetDataListener() {
@Override
public View onSetData(View view, int resId) {
switch (resId) {
case R.id.iv_target_image:
if (bmp != null) {
((ImageView) view).setImageBitmap(bmp);
}
break;
}
return view;
}
};

最后是调用:

1
2
3
4
5
6
setContentView(xxx);
byte[] byteArray = getIntent().getByteArrayExtra("image");
if (byteArray != null) {
bmp = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
}
initAnimListener();

再此,我也学习到:使用开源库的时候,先尝试使用后,看是否能自己得到效果,不能就参考其他项目的一样实现,否则不知道所以然就茫茫使用,只会弄巧成拙,错了都不知道怎么进行排查。

解决兼容库中makeSceneTransitionAnimation无法正确移动到目标位置

不知道github上的动画为什么这么和谐,我使用的demo都是移动在目标位置的下方而已。本想用其他兼容库来代替,但是还没有找到比这个好的。只好看源码去解决这个问题。

直到按着程序执行逻辑找到这个TransitionCompat的startSharedElementAnimation方法,才知道是偏移量出了问题,修改成如下就好:

1
2
3
4
5
6
7
8
-        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- int i = ActivityOptionsCompatICS.getStatusBarHeight(activity);
- finalOffsetY = -i * 2;
- } else if (mIsStartFullScreen == true && isFinalFullScreen == false) {
+ if (mIsStartFullScreen == true && isFinalFullScreen == false) {
finalOffsetY = -ActivityOptionsCompatICS.getStatusBarHeight(activity);
} else {
finalOffsetY = 0;