Android实现抖音评论弹窗与视频联动缩放效果
闲来无事刷刷抖音,发现抖音从最开始的弹窗已换成了弹出弹窗视频在最上方并且按一定比例缩放,如下图:

要实现上图联动效果,接下来提几个方法:
setScaleX(float scaleX) //水平方向的缩放比例setScaleY(float scaleY) //垂直方向的缩放比例//scaleX scaleY = 1.0f 表示初始大小// scaleX scaleY < 1.0f,表示缩小,如scale=0.5f,表示宽高是原来的0.5倍//scaleX scaleY> 1.0f,表示放大,如scale=2.0f,表示宽高是原来的2.0倍//设置锚点的X坐标值,以像素为单位。默认是View的中心。setPivotX(float pivotX)//设置锚点的Y坐标值,以像素为单位。默认是View的中心。setPivotX(float pivotX)
其他的我就不一一介绍了,本文主要用到的就是这几个方法。
接下来讲一下我的思路,我在这里并没有用特别复杂的功能,所以本文主要是为了实现滑动缩放功能。
首先分析一下布局方式,主页一个视频View,然后点击评论按钮弹出底部操作栏,放出布局,一个视频一个按钮:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:layout_width="match_parent"android:layout_height="match_parent"><VideoViewandroid:id="@+id/videoView"android:layout_width="match_parent"android:layout_height="match_parent" /><android.support.design.widget.FloatingActionButtonandroid:id="@+id/floatBtn"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentEnd="true"android:layout_alignParentBottom="true"android:layout_gravity="bottom|end"android:layout_margin="@dimen/fab_margin"app:srcCompat="@android:drawable/ic_dialog_email" />RelativeLayout>
Fab添加点击事件,弹出BottomSheetDialog
findViewById(R.id.floatBtn).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {BottomSheetDialog bsd= new BottomSheetDialog();bsd.setFragmentManager(getSupportFragmentManager()).setLayoutRes(R.layout.view_dialog_comment).setCancelOutside(true).setViewListener(v1 -> {}).show();}});

是弹出来了但是还没有关联上,所以还要定义一个状态回调接口来获取每次操作状态回调。
关键代码
public IBehaviorChanged getBehaviorChanged() {return mBehaviorChanged;}public void setBehaviorChanged(IBehaviorChanged behaviorChanged) {mBehaviorChanged = behaviorChanged;}public interface IBehaviorChanged {void changedState(View bottomSheet, int state);void changedOffset(View bottomSheet, float slideOffset);}
BottomSheetBehavior behavior = BottomSheetBehavior.from(bottomSheet);// 初始为展开状态behavior.setState(BottomSheetBehavior.STATE_EXPANDED);behavior.setPeekHeight(0);//注意这句,初始的时候给一个默认值,因为默认是展开状态,所以给的状态是STATE_EXPANDED,具体可以根据需求来if (mBehaviorChanged != null)mBehaviorChanged.changedState(null, BottomSheetBehavior.STATE_EXPANDED);behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {@Overridepublic void onStateChanged(@NonNull View bottomSheet, int newState) {//这里加上这句话是因为需要把状态加上,不然的话滑出屏幕会有黑色阴影//具体可以看源码if (newState == BottomSheetBehavior.STATE_HIDDEN || newState == BottomSheetBehavior.STATE_COLLAPSED) {dismiss();}if (mBehaviorChanged != null)mBehaviorChanged.changedState(bottomSheet, newState);}@Overridepublic void onSlide(@NonNull View bottomSheet, float slideOffset) {if (mBehaviorChanged != null)mBehaviorChanged.changedOffset(bottomSheet, slideOffset);}});
因为手机坐标系统默认是从屏幕左上角开始计算。
我在这里默认给了dialog的高度是1280/(3/2),也就是用1280-1280/(3/2)就是视频的最小高度。
得到高度之后还要继续计算高度占比来进行等比例缩放:
Dialog 高度占比 = 1280/(3/2)/1280≈0.67
VideoView 高度占比 = 1280-(1280/(3/2))/1280≈0.33
所以0.33这个系数就是setScaleX()和setScaleY()的缩放比
因为Dialog滑动距离一直在改变,得到滑动距离之后用当前距离除以总高度,就是缩放比。
当Dialog滑动到屏幕最大之后也就会变成1280/1280=1.0f,所以视频也要恢复初始大小scale(1.0f)。
可以看到视频始终是在屏幕正上方,中心点在屏幕宽度的1/2处,进行缩放,所以当Dialog弹出的时候videoView的x,y坐标可以固定在这个地方,初始状态也就是从0开始
if (scale) {float width = Screen.getWidth();float x = width / 2f;videoView.setPivotX(x);videoView.setPivotY(0);} else {videoView.setPivotX(0);videoView.setPivotY(0);}
这里画个图:

按照这个思路,查看BottomSheetCallback回调,这里来添加一个Log打印一下bottomSheet的Y坐标:
behavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {public void onStateChanged(@NonNull View bottomSheet, int newState) {}public void onSlide(@NonNull View bottomSheet, float slideOffset) {Log.e("BottomSheetCallback","bottomSheet"+bottomSheet.getY());});
com.behavior.bottombehaviormaster E/BottomSheetCallback: bottomSheet382.0~~~com.behavior.bottombehaviormaster E/BottomSheetCallback: bottomSheet1230.0
因太长这里取最大值和最小值来比较一下1230~382 之间的高度差是848,为什么是1230呢?因为没有计算系统状态栏的高度,默认是50dp,所以要加上这50dp
接下来,实现过程在回调方法中添加按比例缩放代码及x,y坐标代码:
bcs.setBehaviorChanged(new BaseBottomSheetDialog.IBehaviorChanged() {public void changedState(View bottomSheet, int state) {if (state == BottomSheetBehavior.STATE_EXPANDED) {float width = Screen.getWidth();float height = Screen.getHeight();float x = width / 2f;float scale = height - view.getHeight();videoView.setScaleX(scale / height);videoView.setScaleY(scale / height);videoView.setPivotX(x);videoView.setPivotY(0);});}}public void changedOffset(View bottomSheet, float slideOffset) {startAnimator(bottomSheet);}});/*** 根据滑动高度进行缩放* @param parent*/private void startAnimator(View parent) {float width = Screen.getWidth();float height = Screen.getHeight();float x = width / 2f;float py = (parent.getY() + 50) / height;videoView.setScaleX(py);videoView.setScaleY(py);videoView.setPivotX(x);videoView.setPivotY(0);}
接下来看实现结果:
有一些需要优化的地方,比如弹出Dialog不显示StatusBar,弹出Dialog背景会变暗,解决掉这两个问题之后基本符合。


源码地址:
https://github.com/futureLix/behavior
到这里就结束啦。
