相关文章推荐
呐喊的书签  ·  Is there a way to get ...·  1 年前    · 

倒计时动画监听器:

			 mAnimator = ObjectAnimator
			                    .ofInt(viewHolder.progressBar, PROGRESS_PROPERTY, viewHolder.progressBar.getMax())
			                    .setDuration(EntertainVoicePreference.MILLIS_IN_FUTURE); 
            mAnimator.setInterpolator(new LinearInterpolator());
            mAnimator.start();
            mAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation, boolean isReverse) {
                    viewHolder.voiceOpenProgressBar.setVisibility(View.INVISIBLE);
                    viewHolder.voiceOpenCountDownTv.setVisibility(View.INVISIBLE);
                    raiseAction(R.id.anim_end); //回调给Fragment的一个方法
            });

正常流程如下:

//该方法在Fragment中,如果在Adapter中执行了raiseAction(上文会有调用)就会回调 mPresenter.registerDelegate(R.id.anim_end, new ActionListener<Object>() { @Override public void call(Context context, int actionId, Object data, ViewObject<?> viewObject) { if (data instanceof XXXModel) { if (TextUtils.equals(mEndId,((XXXModel)data).getTitle())) { mEndId = "end"; updateListDatas(false); updateSoundState(); //会有打点等其他操作 completedVoiceOpenAnim = true; mCommonRecyclerViewAdapter.notifyChangedAll(EntertainHotSoonShortVideoViewObject.UPDATE_VOICE_STOP); @Override public void onBindViewHolder(EntertainHotSoonShortVideoViewObject.ViewHolder viewHolder, List<Object> payloads) { super.onBindViewHolder(viewHolder, payloads); if (payloads != null) { for (Object object : payloads) { if (object instanceof String) { if (UPDATE_VOICE_STOP.equals((String) object)) { stopVoiceAnim(viewHolder); * 停止progressbar动画 private void stopVoiceAnim(ViewHolder viewHolder) { if (mAnimator != null && mAnimator.isRunning()) { mAnimator.end(); viewHolder.progressBar.setVisibility(View.INVISIBLE);

由于执行了mAnimator.end(); 所以会回调上文提及的onAnimationEnd方法,然后调用raiseAction方法。当然动画倒计时结束也会执行onAnimationEnd方法。

如果当前正在倒计时,下拉刷新,需要重新倒计时,所以需要终止正在倒计时的动画。正如上文提到,终止mAnimator.end(),会回调上文提及的onAnimationEnd方法。导致由于主动暂停动画而不需要上报的打点而上报了。所以要过滤掉取消的动画监听。

解决办法一

在启动动画的地方获取数据第一条的title:

Bean o = mRecyclerViewAdapter.getDataList().get(0);
mEndId = o.getTitle();  //mEndId是全局变量String类型
mRecyclerViewAdapter.notifyChangedAll(XXXiewObject.VOICE_START_ANIM);

在接受的地方去过滤即可:

//该方法在Fragment中,如果在Adapter中执行了raiseAction(上文会有调用)就会回调
 	mPresenter.registerActionDelegate(R.id.anim_end, new ActionListener<Object>() {
            @Override
            public void call(Context context, int actionId, Object data, ViewObject<?> viewObject) {
                if (data instanceof HotsoonModel) {
                    if (TextUtils.equals(mEndId,((XXXModel)data).getTitle())) {
                        mEndId = "end";
                        ......
                        completedVoiceOpenAnim = true;
        });

如果是刷新,单面上条数据的title和当前的title不一样。
如果是多任务(进入前倒计时未结束)进入重新计时,由于上次动画设置了mVoiceAnimEndId = “end”,所以也会过滤掉。

停止倒计时,AnimatorListenerAdapter依然会回调onAnimatorEnd.
即使用mAnimator.cancel();也会执行:

package android.animation;
public abstract class Animator implements Cloneable {
     * Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
     * stop in its tracks, sending an
     * {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
     * its listeners, followed by an
     * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
     * <p>This method must be called on the thread that is running the animation.</p>
    public void cancel() {
     * Ends the animation. This causes the animation to assign the end value of the property being
     * animated, then calling the
     * {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
     * its listeners.
     * <p>This method must be called on the thread that is running the animation.</p>
    public void end() {

不明白为什么官方执行cancel,还要执行end。用户如果需要end,可以执行cancel之后,再执行end。

解决方法二

解决方法一在线上环境运行没问题,但是测试环境下,刷新之后,第一条数据的title和刷新之前的title一样,通过判断title来确定是哪次刷新就不准确了。
新版本的策略是通过计算时间来确定,比如开始动画的地方记录一个时间戳,
在动画结束回调,也就是Animation#end方法再记录一个时间戳,两个时间戳差值,也就是动画实际运行的时间,该时间和动画预定的时间值误差在200毫秒内(由于动画初始化也需要消耗一定的时间),就认为动画真正的执行完了,否则认为动画被取消了。

但该方法也有个弊端,比如动画暂停时,时间计算比较复杂,解决方法是和产品沟通,去掉了动画暂停的功能,因为动画暂停的时机比较边缘,case复现的时机比较少。

解决方法三

咱们没实践,调研如下:
停止动画,可以调用的方法有两种方式:

  • mVoiceobjectAnimator.end();
  • mVoiceobjectAnimator.cancel();

end最后调用的就是listener#onAnimationEnd;由于无法区分是主动取消还是动画执行完,所以弃用。
cancel调用的是listener#onAnimationCancel,然后是onAnimationEnd.
如果有 onAnimationCancel回调,记一次Boolean flag,在onAnimationEnd 如果flag表明是取消的,就不再继续后面的步骤了,同时flag设置false。

需求背景需求背景,取消当前的动画,重新开始倒计时动画。倒计时动画布局:&lt;ProgressBar android:id="@+id/progress_voice" style="?android:attr/progressBarStyleHorizontal" android:layout_centerInParent="true" andr
Android动画的分类 Android中的动画主要有三类:逐帧动画、补间动画和属性动画,。逐帧动画的原理是利用人类的“视觉残留”,通过改变播放图片的形式来达到动画的效果,在实际开发中使用较少,故本文中不予以展开。主要介绍补间动画和属性动画。 补间动画:主要有旋转、位移、缩放、透明度和组合5种场景。 属性动画:可以说是以上动画的补充,可以作用于对象的任意属性。 补间动画和属性动画的区别 使用补间动画已经可以实现很多的动画效果了,但是仍然有一些局限,所以推出了属性动画作为动画的补充,但它们两者还是存在一些
Android-ObjectAnimator实现动画开始,暂停,继续,结束 本人计机专业大学学生,这是第一篇博客。在某些地方的术语可能有所偏颇,酌情参考。接下来的博客内容都围绕大学Android移动应用开发中设计一个角色扮演游戏APP中遇到的开发问题并相应的解决问题。 当设计两个角色对战界面时候,需要根据进度条动画显示可选技能界面,用户选择技能后继续进行进度条动画。这里就需要解决进度条...
ViewPropertyAnimator animate = imageView.animate(); animate.translationX(50000).setDuration(10000); imageView.animate获取到ViewPropertyAnimator对象,tran...
最近做音乐播放相关的开发,需要实现一个较为简单的图片转动的一个功能,同时转动的时候在暂停播放的时候,需要停止在当前转动到的角度,所以使用了ObejctAnimator来实现,记录下实现的方法: ObjectAnimator  mMusicAnimation =ObjectAnimator.ofFloat(mMusicImage, "rotation", 0f,360f);        
本周做一个动画的时候用到了ObjectAnimator动画要求,一个柱状图根据数值的不同慢慢升起,但是使用了ObjectAnimator类的时候,发现该类并不能满足我的需求,后来使用了它的父类ValueAnimator,十分简单的解决了这个问题。 首先介绍下ObjectAnimator: public final class ObjectAnimator extends Value
ValueAnimator:这个动画是针对属性的值进行动画的 ,不会对UI造成改变,不能直接实现动画效果。需要通过对动画的监听去做一些操作,在监听中将这个值设置给对应的属性,对应的属性才会改变。 ObjectAnimator:直接动画所给的对象,他会调用对象对应属性的get/set方法吧...
★★★ 我在动画开始时设置了按钮无法点击,再结束动画的监听中设置了按钮可点击 ★★★ 结果就是这个动画结束事件出了问题: 第一种:onAnimationEnd(Animator animation, boolean isReverse), 这个方法有时候不调用: animatorSet.addListener(new AnimatorListenerAdapter() {
一个 RecyclerView 中的项目需要被删除时,通常可以使用适配器的 notifyItemRemoved() 方法来触发这个过程。在这个方法被调用之后,RecyclerView 会移除对应位置的项目并重新对其它项目进行布局,但这个过程默认不会产生任何动画效果。 如果你想要一个平滑、有动画效果的删除过渡效果,可以参考以下的实现方式: 首先,在你的 Adapter 类中添加一个方法来删除指定位置的项目,这个方法中应该把该项目从数据列表中移除并调用 Adapter 的 notifyItemRemoved() 来通知 RecyclerView 对其它项目进行布局调整。 ```java public void removeItem(int position) { dataList.remove(position); notifyItemRemoved(position); 然后,在你的 RecyclerView.ViewHolder 类中添加一个方法来执行实际的动画效果。这个方法应该使用 ViewPropertyAnimator 来平滑地动画删除该项目的视图,而不是直接让视图突然消失。 ```java public void animateDelete() { View itemView = itemView; ViewGroup.LayoutParams layoutParams = itemView.getLayoutParams(); int originalHeight = itemView.getHeight(); // 如果需要删除的项目不是最后一个,我们需要将所有后面的项目上移一个指定的距离 if (!isLastItem()) { int itemHeight = itemView.getResources().getDimensionPixelSize(R.dimen.list_item_height); ObjectAnimator upAnimator = ObjectAnimator.ofFloat(itemView, "translationY", 0, -itemHeight); upAnimator.setDuration(ANIMATION_DURATION); upAnimator.start(); // 待上移的项目需要重新调整它们的位置 for (int i = getAdapterPosition() + 1; i < adapter.getItemCount(); i++) { RecyclerView.ViewHolder vh = recyclerView.findViewHolderForAdapterPosition(i); if (vh != null) { ObjectAnimator upAnimator2 = ObjectAnimator.ofFloat(vh.itemView, "translationY", 0, -itemHeight); upAnimator2.setDuration(ANIMATION_DURATION); upAnimator2.start(); // 动画删除当前项目 ObjectAnimator animator = ObjectAnimator.ofInt(layoutParams.height, 0); animator.setDuration(ANIMATION_DURATION); animator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animator) { adapter.removeItem(getAdapterPosition()); itemView.setLayoutParams(layoutParams); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { int val = (Integer) valueAnimator.getAnimatedValue(); layoutParams.height = val; itemView.setLayoutParams(layoutParams); animator.start(); 最后,在你的 RecyclerView.Adapter 类中,重写 onCreateViewHolder() 方法来为每个项目设置一个点击监听器。在监听器被触发时,你可以先使用适配器的 getViewHolderForAdapterPosition() 方法获取到该项目的 ViewHolder,然后调用它的 animateDelete() 方法来实现动画效果并删除该项目。 ```java @Override public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(parent.getContext()) .inflate(R.layout.list_item, parent, false); MyViewHolder viewHolder = new MyViewHolder(itemView); // 为每个项目设置一个点击监听器 itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { MyViewHolder holder = (MyViewHolder) recyclerView.findContainingViewHolder(view); holder.animateDelete(); return viewHolder; 注意,以上代码中 animator 的播放时间 ANIMATION_DURATION 应该根据你自己的需求来设定。动画效果可以根据自己的需求来调整,这里给出的只是一个简单的例子。