前言
又到了金三銀四的季節了,忙的人特別忙,面試啊,加班啊,閒的人也是特別閒吧,就比如我,天天划水,閒的寫文章,做動畫,同時呢各種新技術在不斷的湧進,推動者軟體行業的發展,不要焦慮,不要著急,學好本分,再擴充套件技能。不多說了,給大家看看做的效果吧。
github連結根據大家的一致意見開源了
這個是原生的效果
這個是做出來的效果
動畫分解
動畫分解其實很重要,如果說想模仿一個App的動畫及互動效果,一定要仔細的觀察他在互動細到放慢每一個操作,然後在根據這個動態來寫程式碼。不然最後實現的效果不一樣,那豈不是白費了。廢話不多說,上程式碼看步驟。
第一步視訊原始狀態分析
可以看到,視訊最初始的狀態是由一個VideoView(可以是surfaceview都行),加上一個list吧,上下佈局,但是VideoView呢,他是根據視訊尺寸大小動態的調整寬高的,而不是固定的,我們來分析這個過程
- 第一種下拉的時候如果視訊的高度沒有達到原生高度,這時候下面的listview 不能消費touch事件,由videoview來消費這個事件,根據當前的下滑Y值進行調整視訊寬高比
- 第二種情況是如果listview已經滑動了一部分這時候進行下拉,但是viewvideo還沒有到達真是高度,這時候touch事件還是由listview消費,當他滑動到頂部的時候進行攔截touch 然後傳遞給videoview
- 第三中情況,當listview 向下快速滑動的時候會有個慣性的過程,即使手離開手機了 listview還是在滑動,我們稱為慣性滑動,到慣性滑動到頂部的時候,如果視訊高度沒有達到原生高度,這時候要根據這個慣性值來調整調整視訊高度。
第二步 視訊下滑過程分析
由上圖可以看到當我們在下拉視訊的時候,視訊的頂部 左右邊距 機listview 都發生改變,同時videoview高度也發生了變化。
- 下拉的時候margin的 left right top bottom值都在發生改變,videoview 的高度也在傳送改變,同時listview 漸漸消失,變成白色的背景,listview的高度在逐漸變小,同時可以看到在Z軸也發生了改變。可能圖片不清晰,看不出Z軸的變換
- 下拉到listview消失的時候,這時候videoview的寬度發生改變,同時控制器出現。這時候videoview寬度還是在一直的縮小。
- 當達到了最小視訊懸浮層的時候,下拉整個檢視越來越透明,同時整個佈局在根據手勢在下滑。
第三步 視訊上滑過程分析
上滑過程其實就是把下滑過程反過來而已,這裡我就不再過多追溯了。
擼程式碼
主要的邏輯在上面都有,下面我就簡單的對上面的邏輯進行分析一下,整體程式碼想要的老鐵私信我留言都行
如何對listview和videoview進行touch的分發
這裡我使用的是一個LinearLayout作為父佈局進行組裝這兩個子view,然後根據viewgroup的dispatchTouchEvent方法裡面加判斷進行事件的分發。網上這些程式碼很多我就不詳細贅述了,其實我這裡還是有很多可以優化的地方,這只是個demo。
- 和上面動畫分解的邏輯一致,一看videoview 是否達到最大值,listview是否在頂部。
- listview(我這邊用的時候recyclerview都一樣)是否在頂部可以通過呼叫addOnScrollListener()來判斷是否是第一個可見的item *判斷videoview是否達到最大值呢,根據視訊的寬高來。可以通過Mediaplayer來獲取,
//分發recyclerview和videoview事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
y = ev.getY();
int pointerId = ev.getPointerId(0);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mDownY = ev.getY();
Log.i(TAG, "dispatchTouchEvent: ACTION_DOWN " + mDownY);
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
} else {
mVelocityTracker.clear();
}
mVelocityTracker.addMovement(ev);
break;
case MotionEvent.ACTION_MOVE:
float dDownY = y - mDownY;
Log.i(TAG, "dispatchTouchEvent: " + mDownY + " " + y);
mVelocityTracker.addMovement(ev);
mVelocityTracker.computeCurrentVelocity(1000);
if ((mDownY >= (layoutPVideo.getHeight() + layoutPVideo.getMarginTop())) && dDownY > 0 && layoutPVideo.getHeight() < originalHeight + 600 && (isList2Top)) {
//判斷點選的範圍,及當前視訊尺寸大小。listview是否已經滑到頂部
layoutPVideo.setHeight((int) (layoutPVideo.getHeight() + dDownY));
Log.i(TAG, "dispatchTouchEvent: xia " + dDownY);
mDownY = y;
return true;
} else if ((mDownY >= (layoutPVideo.getHeight() + layoutPVideo.getMarginTop())) && dDownY <= 0 && layoutPVideo.getHeight() >= originalHeight) {
//調整視訊view 高度
layoutPVideo.setHeight((int) (layoutPVideo.getHeight() + dDownY));
//可以加個彈性動畫顯得更流暢
Log.i(TAG, "dispatchTouchEvent: shang " + dDownY);
mDownY = y;
return true;
}
break;
case MotionEvent.ACTION_UP:
float yVelocity = VelocityTrackerCompat.getYVelocity(mVelocityTracker,
pointerId);
Log.i("VelocityTrackerCompat", "Y velocity: " +
yVelocity);
if (yVelocity >= 5685 && layoutPVideo.getHeight() < originalHeight + 600) {
//判斷慣性加速度根據慣性加速度進行引導視訊大小到底部
headMoveToMax();
} else {
Log.i(TAG, "headMoveToMax: not come in" + " " + (layoutPVideo.getHeight() < originalHeight + 600));
}
break;
case MotionEvent.ACTION_CANCEL:
mVelocityTracker.recycle();
break;
}
return super.dispatchTouchEvent(ev);
}
複製程式碼
如何動態的跳轉左右邊距及視訊寬高
這裡我用了一個包裝類拿到當前view的layoutParams,然後通過touchEvent 拿到滑動的值來動態的修改當前view 的寬高和margin值,這個程式碼我就補貼了,就是set get方法。
- 第一步下滑調整margin的左右上下,及videoview 的高度。
- 第二步繼續下滑調整videoview的寬度
- 第三步下滑隱藏整體view
private void updateVideoView(int m, int originY) {
canHide = false;
if (mDetailView.getHeight() == 0) {
if (layoutPVideoView.getMarginRight() <= videoWidthPx && 0 < originY) {
int value = layoutPVideoView.getMarginRight() + originY * 9;//加速縮小
if (value > videoWidthPx) {
value = (int) videoWidthPx;
}
float percent = (videoWidthPx - value) / videoWidthPx;
if (0 > percent) {
percent = 0.f;
}
int videoHeight = (int) (videoMinHeightPx * (1 - percent));
int videoMTop = (int) (allMinScrollY * (1 - percent));
layoutPVideo.setMarginTop((int) (allScrollY + videoMTop));
layoutPVideo.setHeight((int) (videoHeightPx - videoHeight));
layoutPVideoView.setMarginRight(value);
canHide = true;
Log.i(TAG, "updateVideoView: "+isBottomMax);
if (layoutPVideoView.getMarginRight() >= videoWidthPx) {
if (isBottomMax) {
layoutPVideo.setMarginTop(m);
float v = m - (allScrollY + allMinScrollY);
mVideoView.setAlpha(1.0f - v / swipePx2Dismiss);
if (v >= swipePx2Dismiss) {
setVisibility(INVISIBLE);
mVideoView.setAlpha(1f);
}
}
}
return;
}//縮小視訊右邊距
if (layoutPVideoView.getMarginRight() >= 0 && 0 > originY) {
int value = layoutPVideoView.getMarginRight() + originY * 9;
// if (0 > value) {
// value = 0;
// }
float percent = (videoWidthPx - value) / videoWidthPx;
if (0 > percent) {
percent = 0.f;
}
int videoHeight = (int) (videoMinHeightPx * (1 - percent));
int videoMTop = (int) (allMinScrollY * (1 - percent));
layoutPVideo.setMarginTop((int) (allScrollY + videoMTop));
layoutPVideo.setHeight((int) (videoHeightPx - videoHeight));
layoutPVideoView.setMarginRight(value);
return;
}//放大視訊右邊距
if (layoutPVideoView.getMarginRight() >= 0 && originY > 0) {
return;
}//最小化階段
}
if (layoutPVideo.getMarginTop() <= 0 && originY < 0) {
m = 0;
}//最大化階段
float percent = (allScrollY - m) / allScrollY;
if (0 > percent) {
percent = 0;
return;
}
int videoHeight = (int) (originalHeight - (originalHeight - videoHeightPx) * (1 - percent));
int listHeight = (int) ((originListHeight) * (percent));
layoutPVideo.setMarginTop(m);
layoutPVideo.setHeight(videoHeight);
layoutPList.setMarginBottom((int) (marginBottomPx * (1 - percent)));
layoutPList.setHeight(listHeight);
layoutPCoverView.getView().setAlpha((1 - percent));
layoutPContainer.setMarginRight((int) (marginRLPx * (1 - percent)));
layoutPContainer.setMarginLeft((int) (marginRLPx * (1 - percent)));
int mr = (int) ((1f - percent) * marginPx); //VideoView右邊和詳情View 上方的margin
layoutPVideo.setZ(mr / 2);//這個是Z軸的值,懸浮效果
}
複製程式碼
ending
花了個把小時寫的東西,希望給老鐵們帶來的是知識的儲備而不是時間的浪費。不早了不早了下班了,想要程式碼的老鐵可以私信 留言都行。