1棉浸、ViewPager2
網上很多仿抖音視頻切換的很多都是使用自定義豎方向的ViewPager或者使用RecyclerView+PagerSnapHelper實現(xiàn)侥猩。
但是這兩種方式其實都有一定的缺陷:
1、但是ViewPager實現(xiàn)镀迂,生命周期有一定問題(ps:FragmentPagerAdapter在新版本中提供了BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT來解決這個問題)圈匆,notifyDataSetChanged支持很不好邻吭。關于生命周期的更新可以參考androidx中的Fragment懶加載方案
2、而使用RecyclerView+PagerSnapHelper實現(xiàn)焕檬,需要自己處理生命周期姆坚。
其實我們可以使用androidx中提供的ViewPage2來實現(xiàn)這個功能。
dependencies {
implementation "androidx.viewpager2:viewpager2:1.0.0"
}
查看源碼实愚,ViewPager2繼承自ViewGroup兼呵,其中發(fā)現(xiàn)了三個比較重要的成員變量:
private LinearLayoutManager mLayoutManager;
RecyclerView mRecyclerView;
private PagerSnapHelper mPagerSnapHelper;
明眼人一看就知道了兔辅,ViewPager2的核心實現(xiàn)就是RecyclerView+LinearLayoutManager+PagerSnapHelper了,因為LinearLayoutManager本身就支持豎向和橫向兩種布局方式萍程,所以ViewPager2也能很容易地支持這兩種滾動方向了幢妄,而幾乎不需要添加任何多余的代碼。
使用上和老的ViewPager基本沒啥卻別茫负,下面ViewPager2的幾個新東西:
- 支持RTL布局
- 支持豎向滾動
- 完整支持notifyDataSetChanged
- FragmentStateAdapter替換了原來的 FragmentStatePagerAdapter
- RecyclerView.Adapter替換了原來的 PagerAdapter
- registerOnPageChangeCallback替換了原來的 addPageChangeListener
- public void setUserInputEnabled(boolean enabled)禁止滑動
- ...
關于更多ViewPager2的資料大家可以自行搜索蕉鸳。
2、視頻播放器
本demo中使用的是GSYVideoPlayer忍法,實際項目中可自行選擇封裝潮尝。demo中沒有對其進行過多的處理,只是為了看效果饿序,實際的抖音中有更多復雜的東西勉失。
3、demo結構
看最后實現(xiàn)的效果
4乱凿、無限上滑
借助ViewPager2的監(jiān)聽和adapter的notifyDataSetChanged即可實現(xiàn)
viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (position==urlList.size-1){
urlList.add(urlList[0])
mPagerAdapter.notifyDataSetChanged()
}
}
})
5、視頻和作者主頁切換控制
override fun onResume() {
super.onResume()
if (mCurrentPosition > 0) {
videoPlayer?.onVideoResume(false)
} else {
videoPlayer?.postDelayed({
videoPlayer?.startPlayLogic()
}, 200)
}
}
override fun onPause() {
super.onPause()
likeLayout?.onPause()
videoPlayer?.onVideoPause()
mCurrentPosition = videoPlayer?.gsyVideoManager?.currentPosition ?: 0
}
6咽弦、點贊效果
抖音的點贊效果是由右側的桃心點贊和屏幕的點擊構成徒蟆,右側的點擊后為點贊狀態(tài)并有點贊動畫,再次點擊取消點贊型型。
先來看看效果:
此處我們觀察效果基本和此前用過的一個三方庫比較相似段审,此處就先又此代替,后面有時間再進行完善闹蒜。
該庫為Like Button,使用方式很簡單寺枉,如下:
<com.github.like.LikeButton
android:id="@+id/likeBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
app:icon_size="40dp"
app:icon_type="heart"
app:like_drawable="@mipmap/ic_heart_on"
app:unlike_drawable="@mipmap/ic_heart_off" />
LikeButton具有以下屬性,使用時可自行去查看
<com.github.like.LikeButton
app:icon_type="Star"
app:circle_start_color="@color/colorPrimary"
app:like_drawable="@drawable/thumb_on"
app:unlike_drawable="@drawable/thumb_off"
app:dots_primary_color="@color/colorAccent"
app:dots_secondary_color="@color/colorPrimary"
app:circle_end_color="@color/colorAccent"
app:icon_size="25dp"
app:liked="true"
app:anim_scale_factor="2"
app:is_enabled="false"/>
設置LikeButton的監(jiān)聽事件:
likeBtn.setOnLikeListener(object : OnLikeListener {
override fun liked(p0: LikeButton?) {
toast("已點贊~~")
}
override fun unLiked(p0: LikeButton?) {
toast("取消點贊~~")
}
})
我們可以看一下點贊按鈕的點贊效果
7、屏幕點贊
分析抖音的屏幕點贊由幾個部分組合而成(紅心可自繪绷落,也可以直接使用圖片)
- 1姥闪、剛開始顯示的時候,有個由大到小縮放動畫
- 2砌烁、透明度變化
- 3甘畅、向上平移
- 4、由小到大的縮放
- 5往弓、剛開始顯示的時候有個小小的偏移疏唾,避免每個紅心在同一個位置
8、實現(xiàn)過程
獲取紅心
private var icon: Drawable = resources.getDrawable(R.mipmap.ic_heart)
監(jiān)聽Touch事件函似,并在按下位置添加View
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event?.action == MotionEvent.ACTION_DOWN) { //按下時在Layout中生成紅心
val x = event.x
val y = event.y
addHeartView(x, y)
onLikeListener()
}
return super.onTouchEvent(event)
}
為紅心添加一個隨機的偏移(此處為-10~10)
img.scaleType = ImageView.ScaleType.MATRIX
val matrix = Matrix()
matrix.postRotate(getRandomRotate()) //設置紅心的微量偏移
設置一開始的縮放動畫
private fun getShowAnimSet(view: ImageView): AnimatorSet {
// 縮放動畫
val scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1.2f, 1f)
val scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1.2f, 1f)
val animSet = AnimatorSet()
animSet.playTogether(scaleX, scaleY)
animSet.duration = 100
return animSet
}
設置慢慢消失時的動畫
private fun getHideAnimSet(view: ImageView): AnimatorSet {
// 1.alpha動畫
val alpha = ObjectAnimator.ofFloat(view, "alpha", 1f, 0.1f)
// 2.縮放動畫
val scaleX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 2f)
val scaleY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 2f)
// 3.translation動畫
val translation = ObjectAnimator.ofFloat(view, "translationY", 0f, -150f)
val animSet = AnimatorSet()
animSet.playTogether(alpha, scaleX, scaleY, translation)
animSet.duration = 500
return animSet
}
設置動畫關系槐脏,并且在動畫結束后Remove該紅心
val animSet = getShowAnimSet(img)
val hideSet = getHideAnimSet(img)
animSet.start()
animSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
hideSet.start()
}
})
hideSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
removeView(img) //動畫結束移除紅心
}
})
區(qū)分單擊和多次點擊,單擊的時候控制視頻的暫停和播放撇寞,多次點擊的時候實現(xiàn)點贊功能
override fun onTouchEvent(event: MotionEvent?): Boolean {
if (event?.action == MotionEvent.ACTION_DOWN) { //按下時在Layout中生成紅心
val x = event.x
val y = event.y
mClickCount++
mHandler.removeCallbacksAndMessages(null)
if (mClickCount >= 2) {
addHeartView(x, y)
onLikeListener()
mHandler.sendEmptyMessageDelayed(1, 500)
} else {
mHandler.sendEmptyMessageDelayed(0, 500)
}
}
return true
}
private fun pauseClick() {
if (mClickCount == 1) {
onPauseListener()
}
mClickCount = 0
}
fun onPause() {
mClickCount = 0
mHandler.removeCallbacksAndMessages(null)
}
完整代碼
只是為了做實驗顿天,代碼寫的比較亂
https://github.com/leiyun1993/DouYinLike