一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - Android LayoutTransiton實現簡單的錄制按鈕

Android LayoutTransiton實現簡單的錄制按鈕

2022-03-02 15:43saka Android

這篇文章主要介紹了Android LayoutTransiton實現簡單的錄制按鈕,主要實現開始,暫停,停止和顯示錄制時間長度,具有一定的參考價值,感興趣的小伙伴們可以參考一下

最近公司要做的項目中要求實現一個簡單的視頻錄制功能的組件,我簡單設計了一個,主要功能就是開始,暫停,停止和顯示錄制時間長度。首先看一下效果圖:

 

 
Android LayoutTransiton實現簡單的錄制按鈕

 

可以看到是一個非常簡單的動畫效果,為了方便使用,我把他做成了aar并發布到了jCenter,集成方式:

?
1
compile 'com.rangaofei:sakarecordview:0.0.2'

組件里用到的庫也非常簡單,包括databinding,屬性動畫和layouttransition。通過這個簡單的庫簡單的介紹一下LayoutTransition的用法,其中也會插入一些簡單的databinding和屬性動畫的知識點,遇到困難請自行解決。

使用方法: 在xml文件中添加自定義控件:

?
1
2
3
4
5
6
7
<com.hanlinbode.sakarecordview.RecordView
    android:id="@+id/rv_saka"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_margin="30dp"
    app:record_view_time_string="HHMMSS" />

record_view_time_string 屬性是枚舉類型,用來表示時間表示形式:

HHMMSS 00:00:00
MMSS 00:00
HH_MM_SS 00-00-00
MM_SS 00-00

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//更新時間
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)來設置自己的過渡效果。能夠出發動畫的情況有兩種:

  1. item添加(設置View.VISIBLE也可)
  2. 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分析

 

 

Android LayoutTransiton實現簡單的錄制按鈕

左邊的開始和暫停按鈕是一個checkbox實現的,通過一個簡單的selector來切換圖片,并在右側布局出現和消失的時候有一個縮放動畫。我們可以通過設置一個簡單的ObjectAnimator監聽器來實現這個縮放:

?
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
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是自定一個一個類,用來設置顯示的圖片和時間,并保存縮放的狀態:

?
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
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值:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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文件中可以直接飲用屬性:

?
1
2
3
4
5
6
7
8
9
10
<CheckBox
  android:id="@+id/start"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_centerVertical="true"
  android:layout_marginLeft="30dp"
  android:button="@{state.playDrawable}"
  android:checked="@{state.recording}"
  app:checkListener="@{checkListener}"
  app:recordScale="@{state.playScale}" />

這樣就基本完成了動畫操作,然后暴露一些接口即可:

?
1
2
3
4
5
6
7
8
9
10
11
12
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對應關系等等。

默認的方法和變量

?
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
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這個方法,可以看到其實這個啟動的延遲時間是不一樣的,對應的關系為:

?
1
2
3
4
5
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時,是一個逆向的過程。

看另個一有用的變量

?
1
2
private int mTransitionTypes = FLAG_CHANGE_APPEARING | FLAG_CHANGE_DISAPPEARING |
      FLAG_APPEARING | FLAG_DISAPPEARING;

這個mTransitionTypes就是在后邊的執行動畫中必須使用的一個變量,它默認激活了四種種類,只有前邊提到的FLAG_CHAGE未激活.

開發者可控的變量

這里集中講幾個方法:

?
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
//設置所有的動畫持續時間
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 List<TransitionListener> getTransitionListeners()

這些方法都比較簡單。

執行流程

先看一張簡單的圖:

 

 

Android LayoutTransiton實現簡單的錄制按鈕

從上面的方法中可以看到,flag全都沒有激活的話,那就沒有任何顯示或者隱藏的動畫了。 CHANGE_DISAPPEARINGCHANGE_APPEARING 控制的是父view和非新添加view的動畫, APPEARINGDISAPPEARING 控制的是新添加view的動畫。

mAnimateParentHierarchy這個變量控制的是是否顯示父布局的改變動畫,所以這個必須設置為true后父布局的 CHANGE_DISAPPEARINGCHANGE_APPEARING 才能有作用,設置為false后只有父布局沒有動畫,而子控件中非新添加的view還是用動畫效果。

viewgroup中調用

addview()用來為viewroup添加一個沒有父控件的view,這個方法最終調用的是

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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的顯示或者隱藏時會調用以下方法

?
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
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<View>();
          }
          mVisibilityChangingChildren.add(child);
          addDisappearingView(child);
        }
      }
    }
 
    // in all cases, for drags
    if (newVisibility == VISIBLE && mCurrentDragStartEvent != null) {
      if (!mChildrenInterestedInDrag.contains(child)) {
        notifyChildOfDragStart(child);
      }
    }
  }

可以看到在viewgroup中與上面圖中提到的方法調用是吻合的。

在調用ViewGroup.setLayoutTransition(LayoutTransition transition)的時候為自身設置了一個TransitionListener,這個地方加入的目的是為了緩存正在進行動畫的view,暫不分析。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://juejin.im/post/5b231e18f265da59601958bf

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 国产精品久久现线拍久青草 | 欧美精品99久久久久久人 | 免费国产之a视频 | 日本大尺度动漫在线观看缘之空 | caopren免费视频国产 | 国产自精品 | 天天操天天爽天天射 | 亚洲成人免费 | 国产亚洲精品一区二区在线观看 | 久久机热视频 这里只有精品首页 | 精品无码国产AV一区二区三区 | 国产a一级毛片午夜剧院 | 农村妇女野外性生话免费视频 | 国产精品国色综合久久 | vomoulei成人舞蹈 | 国产精品久久久久久久久久久搜索 | 午夜dj影院在线观看完整版 | 国产在线观看一区 | 久久免费看少妇高潮A片特爽 | 亚洲第一福利网 | 亚洲国产果果在线播放在线 | 奇米影视久久777中文字幕 | 亚洲欧美一区二区三区在线观看 | 欧美综合在线 | 欧美一区二区三区久久久 | v视界影院.vsjla | 男女小视频在线观看 | 无限在线观看视频大全免费高清 | 亚洲高清无码在线 视频 | 国产高清路线一路线二2022 | 成人做视频免费 | 成人私人影院www片免费高清 | 网友偷自拍原创区 | 国产精品久久免费 | 百合漫画咱啪全彩抚慰 | 99精品视频只99有精品 | 麻豆网站在线免费观看 | 妹妹你插的我好爽 | 精品国产乱码久久久久久免费 | 日韩综合久久 | 美女靠逼免费视频 |