这篇文章将为大家详细讲解有关怎么在Android中利用LayoutTransiton实现一个录制按钮,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
目前创新互联公司已为近1000家的企业提供了网站建设、域名、网站空间、网站改版维护、企业网站设计、慈利网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
compile 'com.rangaofei:sakarecordview:0.0.2'
组件里用到的库也非常简单,包括databinding,属性动画和layouttransition。通过这个简单的库简单的介绍一下LayoutTransition的用法,其中也会插入一些简单的databinding和属性动画的知识点,遇到困难请自行解决。
使用方法: 在xml文件中添加自定义控件:
record_view_time_string 属性是枚举类型,用来表示时间表示形式:
HHMMSS 00:00:00
MMSS 00:00
HH_MM_SS 00-00-00
MM_SS 00-00
//更新时间 void updateTime(long) /*设置监听器, void onInitial(); void onStartRecord(); void onPauseRecord(); void onResumeRecord(); void onStopRecord();*/ void setRecordListener(RecordListener) void setDebug(boolean)
LayoutTransition简介
来源于官方文档
LayoutTransition能够在viewgroup的布局发生变化时产生一个动画效果。可以通过 ViewGroup.setLayoutTransition(LayoutTransition transition) 来设置过度效果。调用这个方法将会使用内置的过渡动画(alpha值变化,xy位置变化等),开发者可用通过`LayoutTransition.setAnimator(int transitionType,Animator animator)来设置自己的过渡效果。能够出发动画的情况有两种:
- item添加(设置View.VISIBLE也可) 
- item移除(设置View.GON也可) 
当viewgroup中发生上述两种行为时,或者由于添加删除而引起其他item变化,都会触发动画。
过渡动画的触发种类
这个种类指的是在发生某种行为时(例如item添加或者删除),共有5种: CHANGE_APPEARING,CHANGE_DISAPPERING,APPEARING,DISAPPEARING,CHANGING 。每种状态有自己的一个位标记。
CHANGE_APPEARING
指示动画将会在新的控件添加到viewgroup中的时候引起其他view变化触发。它的标志位是0x01。也就是当addview或者将非VISIBLE状态的view设置为VISIBILE状态时其他的view被影响到时也会触发。
CHANGE_DISAPPEARING
指示动画将会在viewgroup删除控件的时候引起其他view变化触发,它的标志位是0x02。也就是当removeview或者将VISIBLE状态的view设置为非VISIBLE状态时其他的view被影响到也会触发。
APPEARING
当新的view添加到viewgroup中的时候触发。它的标志位是0x04。也就是当addview或者将非VISIBLE状态的view设置为VISIBILE状态时会触发。
DISAPPERAING
指示动画将会在viewgroup删除控件时触发,它的标志位是0x08。也就是当removeview或者将VISIBLE状态的view设置为非VISIBLE状态时会触发。
CHANGING
出去前边的四种,当布局发生变化时会触发动画。它的标志位是0x10。这个标志位默认是不激活的,但是可以通过enableTransitonType(int)来激活。
了解了这些,这个库基本就能实现了。
RecordView分析

左边的开始和暂停按钮是一个checkbox实现的,通过一个简单的selector来切换图片,并在右侧布局出现和消失的时候有一个缩放动画。我们可以通过设置一个简单的ObjectAnimator监听器来实现这个缩放:
ObjectAnimator animShow = ObjectAnimator.ofFloat(null, "scaleX", 0, 1);
  animShow.setInterpolator(new OvershootInterpolator());
  animShow.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
      if (isDebug()) {
        Log.e(TAG, "show anim value=" + (float) animation.getAnimatedValue());
      }
      recordState.setPlayScale(1 + (float) animation.getAnimatedValue() / 5);
    }
  });
  layoutTransition.setAnimator(LayoutTransition.APPEARING, animShow);
  ObjectAnimator animHide = ObjectAnimator.ofFloat(null, "alpha", 1, 0);
  animHide.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
      if (isDebug()) {
        Log.e(TAG, "hide anim value=" + (float) animation.getAnimatedValue());
      }
      recordState.setPlayScale(1 + (float) animation.getAnimatedValue() / 5);
    }
  });
  layoutTransition.addTransitionListener(this);
  layoutTransition.setAnimator(LayoutTransition.DISAPPEARING, animHide);
  binding.rootView.setLayoutTransition(layoutTransition);
  binding.rootContainer.setLayoutTransition(layoutTransition);record是自定一个一个类,用来设置显示的图片和时间,并保存缩放的状态:
public class RecordState extends BaseObservable implements Parcelable {
  private boolean recording;
  private String time = "00:00:00";
  private float playScale = 1;
  @DrawableRes
  private int playDrawable;
  @DrawableRes
  private int stopDrawable;
  public RecordState(int playDrawable, int stopDrawable) {
    this.playDrawable = playDrawable;
    this.stopDrawable = stopDrawable;
  }
  @Bindable
  public boolean isRecording() {
    return recording;
  }
  public void setRecording(boolean recording) {
    this.recording = recording;
    notifyPropertyChanged(BR.recording);
  }
  //省略其他的getter和setter
  @Bindable
  public float getPlayScale() {
    return playScale;
  }
  public void setPlayScale(float playScale) {
    this.playScale = playScale;
    notifyPropertyChanged(BR.playScale);
  }
  //省略parcelable代码
}这里需要提一个view的局限性,就是只能改变x或者y的缩放,不能同时改变,所以这里做了一个双向绑定并写了一个adapter来设置同时更改X和Y的scale值:
public class CheckboxAttrAdapter {
  @BindingAdapter("checkListener")
  public static void setCheckBoxListener(CheckBox view, CompoundButton.OnCheckedChangeListener listener) {
    view.setOnCheckedChangeListener(listener);
  }
  @BindingAdapter("android:button")
  public static void setButton(CheckBox view, @DrawableRes int drawableId) {
    view.setButtonDrawable(drawableId);
  }
  @BindingAdapter("recordScale")
  public static void setRecordScale(CheckBox view, float scale) {
    view.setScaleX(scale);
    view.setScaleY(scale);
  }
}然后在xml文件中可以直接饮用属性:
这样就基本完成了动画操作,然后暴露一些接口即可:
public interface RecordListener {
  void onInitial();
  void onStartRecord();
  void onPauseRecord();
  void onResumeRecord();
  void onStopRecord();
}这样就完成了一个最简单的RecordView了。
原理探究
本人水平有限,这里只进行最简单的一些分析。
LayoutTransition设置了一系列的默认值,这些默认值有默认的animator,animator的duration,动画开始的延迟时间,动画的错开间隔,插值器,等待执行view的动画map关系,正在显示或者消失的view动画的map关系,view和view的onlayoutchangelistenr对应关系等等。
默认的方法和变量
public LayoutTransition() {
  if (defaultChangeIn == null) {
    PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1);
    PropertyValuesHolder pvhTop = PropertyValuesHolder.ofInt("top", 0, 1);
    PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1);
    PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1);
    PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1);
    PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1);
    defaultChangeIn = ObjectAnimator.ofPropertyValuesHolder((Object)null,
        pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY);
    defaultChangeIn.setDuration(DEFAULT_DURATION);
    defaultChangeIn.setStartDelay(mChangingAppearingDelay);
    defaultChangeIn.setInterpolator(mChangingAppearingInterpolator);
    defaultChangeOut = defaultChangeIn.clone();
    defaultChangeOut.setStartDelay(mChangingDisappearingDelay);
    defaultChangeOut.setInterpolator(mChangingDisappearingInterpolator);
    defaultChange = defaultChangeIn.clone();
    defaultChange.setStartDelay(mChangingDelay);
    defaultChange.setInterpolator(mChangingInterpolator);
    defaultFadeIn = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f);
    defaultFadeIn.setDuration(DEFAULT_DURATION);
    defaultFadeIn.setStartDelay(mAppearingDelay);
    defaultFadeIn.setInterpolator(mAppearingInterpolator);
    defaultFadeOut = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f);
    defaultFadeOut.setDuration(DEFAULT_DURATION);
    defaultFadeOut.setStartDelay(mDisappearingDelay);
    defaultFadeOut.setInterpolator(mDisappearingInterpolator);
  }
  mChangingAppearingAnim = defaultChangeIn;
  mChangingDisappearingAnim = defaultChangeOut;
  mChangingAnim = defaultChange;
  mAppearingAnim = defaultFadeIn;
  mDisappearingAnim = defaultFadeOut;
}可以看到,默认动画持有的属性有left、top、right、bottom、scrollY和scrollX,这里注意一下startDelay这个方法,可以看到其实这个启动的延迟时间是不一样的,对应的关系为:
private long mAppearingDelay = DEFAULT_DURATION; private long mDisappearingDelay = 0; private long mChangingAppearingDelay = 0; private long mChangingDisappearingDelay = DEFAULT_DURATION; private long mChangingDelay = 0;
官方文档中特别说明了:
By default, the DISAPPEARING animation begins immediately, as does the CHANGE_APPEARING animation. The other animations begin after a delay that is set to the default duration of the animations.
DISAPPEARING和CHANGE_APPEARING没有延迟时间,其他的动画都会有延迟300ms。这样做的目的是为了在动画展示的时候有一个顺序展示的视觉效果,看起来更符合逻辑:
当一个item添加到viewgroup的时候,其他阿德item首先要移动来调整出一块空白区域供新添加的item显示,然后执行新添加的item的显示动画。当移除一个item时,是一个逆向的过程。
看另个一有用的变量
private int mTransitionTypes = FLAG_CHANGE_APPEARING | FLAG_CHANGE_DISAPPEARING | FLAG_APPEARING | FLAG_DISAPPEARING;
这个mTransitionTypes就是在后边的执行动画中必须使用的一个变量,它默认激活了四种种类,只有前边提到的FLAG_CHAGE未激活.
开发者可控的变量
这里集中讲几个方法:
//设置所有的动画持续时间 public void setDuration(long duration) //设置指定种类的动画持续时间:CHANGE_APPEARING,CHANGE_DISAPPEARING,APPEARING,DISAPPEARRING,CHANGING public void setDuration(int transitionType, long duration) //获取指定种类动画的持续时间 public long getDuration(int transitionType) //设置在CHANGEINGXX状态下时间的间隔 public void setStagger(int transitionType, long duration) //获取在CHANGEINGXX状态下时间的间隔 public long getStagger(int transitionType) //为指定的种类添加动画插值器 public void setInterpolator(int transitionType, TimeInterpolator interpolator) //获取指定的种类添加动画插值器 public TimeInterpolator getInterpolator(int transitionType) //为指定的种类添加动画 public void setAnimator(int transitionType, Animator animator) //设置viewgroup的属性是否随着view的变化而变化,比如viewgroup使用的是wrapcontent,添加view时会有一个扩张动画 public void setAnimateParentHierarchy(boolean animateParentHierarchy) //是否正在执行引起布局改变动画 public boolean isChangingLayout() //是否有正在执行的动画 public boolean isRunning() //添加item public void addChild(ViewGroup parent, View child) //移除item public void removeChild(ViewGroup parent, View child) //显示item public void showChild(ViewGroup parent, View child, int oldVisibility) //隐藏item public void hideChild(ViewGroup parent, View child, int newVisibility) //添加监听器 public void addTransitionListener(TransitionListener listener) //移除监听器 public void removeTransitionListener(TransitionListener listener) //获取监听器 public ListgetTransitionListeners() 
这些方法都比较简单。
执行流程
先看一张简单的图:

从上面的方法中可以看到,flag全都没有激活的话,那就没有任何显示或者隐藏的动画了。 CHANGE_DISAPPEARING 和 CHANGE_APPEARING 控制的是父view和非新添加view的动画, APPEARING 和 DISAPPEARING 控制的是新添加view的动画。
mAnimateParentHierarchy这个变量控制的是是否显示父布局的改变动画,所以这个必须设置为true后父布局的 CHANGE_DISAPPEARING 和 CHANGE_APPEARING 才能有作用,设置为false后只有父布局没有动画,而子控件中非新添加的view还是用动画效果。
viewgroup中调用
addview()用来为viewroup添加一个没有父控件的view,这个方法最终调用的是
private void addViewInner(View child, int index, LayoutParams params,
      boolean preventRequestLayout){
        //省略代码
  if (mTransition != null) {
    // Don't prevent other add transitions from completing, but cancel remove
    // transitions to let them complete the process before we add to the container
    mTransition.cancel(LayoutTransition.DISAPPEARING);
  }
  //省略代码
  if (mTransition != null) {
    mTransition.addChild(this, child);
  }
  //省略代码
  //省略代码
  
}设置view的显示或者隐藏时会调用以下方法
protected void onChildVisibilityChanged(View child, int oldVisibility, int newVisibility) {
    if (mTransition != null) {
      if (newVisibility == VISIBLE) {
        mTransition.showChild(this, child, oldVisibility);
      } else {
        mTransition.hideChild(this, child, newVisibility);
        if (mTransitioningViews != null && mTransitioningViews.contains(child)) {
          // Only track this on disappearing views - appearing views are already visible
          // and don't need special handling during drawChild()
          if (mVisibilityChangingChildren == null) {
            mVisibilityChangingChildren = new ArrayList();
          }
          mVisibilityChangingChildren.add(child);
          addDisappearingView(child);
        }
      }
    }
    // in all cases, for drags
    if (newVisibility == VISIBLE && mCurrentDragStartEvent != null) {
      if (!mChildrenInterestedInDrag.contains(child)) {
        notifyChildOfDragStart(child);
      }
    }
  } Android是什么
Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。
关于怎么在Android中利用LayoutTransiton实现一个录制按钮就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
当前文章:怎么在Android中利用LayoutTransiton实现一个录制按钮
浏览路径:http://www.scyingshan.cn/article/ipsjdi.html

 建站
建站
 咨询
咨询 售后
售后
 建站咨询
建站咨询 
 