高仿抖音播放

背景

抖音,能夠說目前最火的短視頻APP!做爲一名 Android 開發,是時候研究一下功能是如何實現的了!
目前,也有一些其餘的小夥伴實現都有播放的效果,只要是用 ViewPager 來實現的上下滑動。 今天,咱們換個思路用咱們最經常使用的控件,RecyclerView 來實現,下面先看看須要實現的功能。java

背景

抖音,能夠說目前最火的短視頻APP!做爲一名 Android 開發,是時候研究一下功能是如何實現的了!
目前,也有一些其餘的小夥伴實現都有播放的效果,只要是用 ViewPager 來實現的上下滑動。 今天,咱們換個思路用咱們最經常使用的控件,RecyclerView 來實現,下面先看看須要實現的功能。android

需求

打開抖音看一下,大體都有什麼功能。git

 


這是抖音的播放頁面,能夠看到大體有如下幾個功能。github

1. 上下滑動播放詳情頁
2. 播放以後自動播放
3. 雙擊點贊效果,而且點贊
4. 單擊暫停,再單擊播放canvas

下面對比一下,我實現的效果bash


基本上完成了咱們咱們預先想實現的需求。網絡

GitHub地址:github.com/yang0range/…app

歡迎stardom

若是須要想看看實現原理那就請繼續看下一篇。ide

高仿抖音播放(二)——代碼的實現

GitHub地址:github.com/yang0range/…
歡迎star!

需求的實現

上一篇,文章展現了實現的效果,而且陳列了咱們要實現的功能。接下來經過兩篇文章,詳細的實現如下整個的過程。

代碼詳解

準備工做

建立項目,由於播放視頻要在網絡播放,因此須要添加網絡權限。

<uses-permission android:name="android.permission.INTERNET" />
複製代碼

該項目的代碼主要是採用Kotlin寫的,不太熟悉的同窗自行轉成Java

建立BaseAcivity

package com.yang.dyvideo.activity

import android.os.Bundle
import android.support.v7.app.AppCompatActivity

/**
 * @author yangzc
 *	@data 2019/9/26 18:35
 *	@desc BaseAcivity
 *
 */
abstract class BaseAcivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(layoutId())
        initData()
        initView()
        start()
        initListener()
    }

    abstract fun initListener()

    /**
     * 初始化數據
     */
    abstract fun initData()

    /**
     * 初始化 View
     */
    abstract fun initView()

    /**
     * 開始請求
     */
    abstract fun start()

    /**
     *  加載佈局
     */
    open fun layoutId(): Int {
        return 0
    }


}
複製代碼

首頁的列表頁

package com.yang.dyvideo.activity

import android.support.v7.widget.LinearLayoutManager
import android.view.View
import com.yang.dyvideo.R
import com.yang.dyvideo.adapter.VideoAdapter
import com.yang.dyvideo.data.Video
import com.yang.dyvideo.data.VideoDataProvider
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*

class MainActivity : BaseAcivity() {
    private var videolist: MutableList<Video>? = null
    private var mAdapter: VideoAdapter? = null


    override fun initListener() {
        mAdapter?.let {
            it.setOnItemClickListener(object : VideoAdapter.OnItemClickListener {
                override fun onItemClick(view: View, position: Int, mVideo: Video) {
                    startActivity(VideoPlayActivity.buildIntent(this@MainActivity, position, videolist as ArrayList<Video>?))
                }

            })

        }

    }
        override fun initData() {
            videolist = ArrayList()

            for (i in 0 until 15) {
                val mVideo = Video()
                mVideo.iamge = VideoDataProvider.getVideoListImg(this).getResourceId(i, R.mipmap.one)
                mVideo.title = VideoDataProvider.getVideoListTitle(this).getString(i)
                mVideo.videoplayer = VideoDataProvider.getVideoPlayer(this).getString(i)
                (videolist as ArrayList<Video>).add(mVideo)
            }


        }

        override fun initView() {

        }

        override fun start() {
            mAdapter = videolist?.let { VideoAdapter(this, it) }
            rlv.adapter = mAdapter
            val linearLayoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
            rlv.layoutManager = linearLayoutManager
        }


        override fun layoutId(): Int {
            return R.layout.activity_main
        }
    }

複製代碼

首頁的Adapter

package com.yang.dyvideo.adapter

import android.content.Context
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView

import com.yang.dyvideo.R
import com.yang.dyvideo.data.Video

/**
 * @author yangzc
 * @data 2019/9/27 11:17
 * @desc
 */
 class VideoAdapter(private val mContext: Context, private val mVideoList: List<Video>) : RecyclerView.Adapter<VideoAdapter.VideoPlayerViewHolder>() {


    private var onItemClickListener: OnItemClickListener? = null

    //點擊事件的接口
    interface OnItemClickListener {
        fun onItemClick(view: View, position: Int, mVideo: Video)

    }

    fun setOnItemClickListener(listener: OnItemClickListener) {
        this.onItemClickListener = listener
    }


    override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int): VideoPlayerViewHolder {
        return VideoPlayerViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_video_list, viewGroup, false))
    }

    override fun onBindViewHolder(ViewHolder: VideoPlayerViewHolder, position: Int) {
        val mVideo = mVideoList[position]
        ViewHolder.dy_iv.setImageResource(mVideo.iamge)
        ViewHolder.dy_tv.text = mVideo.title
        setOnItemClick(ViewHolder)

    }


    private fun setOnItemClick(holder: VideoPlayerViewHolder) {
        if (this.onItemClickListener != null) {
            holder.itemView.setOnClickListener(object : View.OnClickListener {
                override fun onClick(v: View) {
                    run {
                        val position = holder.layoutPosition
                        this@VideoAdapter.onItemClickListener!!.onItemClick(holder.itemView, position, mVideoList[position])
                    }

                }
            })
        }
    }


    override fun getItemCount(): Int {
        return if (mVideoList.isEmpty()) 0 else mVideoList.size
    }


    inner class VideoPlayerViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
        internal val dy_iv: ImageView = itemView.findViewById(R.id.dy_iv)
        internal val dy_tv: TextView = itemView.findViewById(R.id.dy_tv)

    }
}

複製代碼

實現需求——上下滑動播放詳情頁

這幾部分沒有什麼可說的,都是常規的實現方式。接下來,咱們來看看最主要的播放的詳情頁的實現。

接下來,看看咱們的詳情頁的佈局.

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#2D2D33"
    tools:ignore="MissingConstraints,ContentDescription">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rlv_play_video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:descendantFocusability="beforeDescendants" />


    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintBottom_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="parent">

        <ImageView
            android:id="@+id/iv_user_avatar"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:src="@mipmap/default_avater" />

        <ImageView
            android:id="@+id/iv_user_follow"
            android:layout_width="36dp"
            android:layout_height="36dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            android:src="@mipmap/ic_love_white" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="10dp"
            android:text="@string/num"
            android:textColor="@color/colorWhite"
            android:textSize="14sp" />

        <ImageView
            android:layout_width="36dp"
            android:layout_height="36dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="15dp"
            android:src="@mipmap/ic_comment" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="10dp"
            android:text="@string/num"
            android:textColor="@color/colorWhite"
            android:textSize="14sp" />

        <ImageView
            android:layout_width="36dp"
            android:layout_height="36dp"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="20dp"
            android:src="@mipmap/ic_share" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:layout_marginTop="10dp"
            android:text="@string/num"
            android:textColor="@color/colorWhite"
            android:textSize="14sp" />
    </LinearLayout>


    <ImageView
        android:id="@+id/iv_video_play"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@mipmap/ic_video_play"
        android:visibility="visible"
        app:layout_constraintBottom_toTopOf="parent"
        app:layout_constraintLeft_toRightOf="parent"
        app:layout_constraintRight_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="parent" />

</android.support.constraint.ConstraintLayout>
複製代碼

以前,介紹過咱們此次採用的是 RecyclerView來實現的效果。

詳情的播放頁,才用的是Android自帶的VideoView,爲了實現咱們想要的全屏的效果,咱們須要簡單的自定義一下。

package com.yang.dyvideo.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.VideoView;

public class FullWindowVideoView extends VideoView {

    public FullWindowVideoView(Context context) {
        super(context);
    }

    public FullWindowVideoView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public FullWindowVideoView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


}

複製代碼

那接下來看一下Adapter的佈局的實現

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <com.yang.dyvideo.widget.FullWindowVideoView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />


    <ImageView
        android:id="@+id/iv_video_cover"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clickable="false"
        android:focusable="false"
        android:scaleType="centerInside"
        android:visibility="visible" />




</android.support.constraint.ConstraintLayout>
複製代碼

在播放頁面,咱們添加了一個播放的按鈕。
再看看Adapter當中的代碼

package com.yang.dyvideo.adapter

import android.content.Context
import android.media.MediaPlayer
import android.net.Uri
import android.os.Build
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import com.yang.dyvideo.R
import com.yang.dyvideo.data.Video
import com.yang.dyvideo.widget.FullWindowVideoView

/**
 * @author yangzc
 * @data 2019/9/29 15:25
 * @desc
 */
class VideoPlayAdapter (private val mContext: Context, private val mVideoList: List<Video>): RecyclerView.Adapter<VideoPlayAdapter.VideoPlayAdapterViewHolder>() {
    private var onItemClickListener: OnItemClickListener? = null


    fun setOnItemClickListener(listener: OnItemClickListener) {
        this.onItemClickListener = listener
    }

    interface OnItemClickListener {
        fun onItemClick(view: View, position: Int)

    }

    override fun onBindViewHolder(mViewHolder: VideoPlayAdapterViewHolder, position: Int) {
        val mVideo = mVideoList[position]
        mViewHolder.iv_video_cover.setImageResource(mVideo.iamge)
        mViewHolder.surface_view.setVideoURI(Uri.parse(mVideo.videoplayer))
//        mViewHolder.surface_view.setVideoURI(Uri.parse("http://jzvd.nathen.cn/b201be3093814908bf987320361c5a73/2f6d913ea25941ffa78cc53a59025383-5287d2089db37e62345123a1be272f8b.mp4"))
        if (!mViewHolder.surface_view.isPlaying()) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                mViewHolder.surface_view.setOnPreparedListener { mp ->
                    mp.setOnInfoListener(MediaPlayer.OnInfoListener { mp, what, extra ->
                        if (what == MediaPlayer.MEDIA_INFO_VIDEO_RENDERING_START) {
                            mp.isLooping = true
                            mViewHolder.iv_video_cover.animate().alpha(0f).setDuration(0).start()
                            mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT)

                            return@OnInfoListener true

                        }
                        false
                    })
                }
            }
            mViewHolder.surface_view.start()

        }


        setOnItemClick(mViewHolder)
    }

    override fun onCreateViewHolder(viewGroup: ViewGroup, i: Int):VideoPlayAdapterViewHolder {
        return VideoPlayAdapterViewHolder(LayoutInflater.from(mContext).inflate(R.layout.item_video_play, viewGroup, false))
    }


    override fun getItemCount(): Int {
        return if (mVideoList.isEmpty()) 0 else mVideoList.size
    }

    protected fun setOnItemClick(holder: VideoPlayAdapterViewHolder) {
        if (onItemClickListener != null) {
            //爲holder增長點擊事件
            //爲了保持插入和刪除的position正確 不採用getview的position
            holder.itemView.setOnClickListener(object : View.OnClickListener {
                override fun onClick(v: View) {
                    run {
                        val position = holder.getLayoutPosition()
                        onItemClickListener!!.onItemClick(holder.itemView, position)
                    }

                }
            })
        }
    }

    inner class VideoPlayAdapterViewHolder internal constructor(itemView: View) : RecyclerView.ViewHolder(itemView) {
              internal  val  surface_view : FullWindowVideoView = itemView.findViewById(R.id.surface_view)
              internal  val  iv_video_cover : ImageView = itemView.findViewById(R.id.iv_video_cover)
    }
}


複製代碼

Adapter已經完成,接下來再看看如何自動實現上下滑動的時候
咱們須要自定一下LayoutManager,經過自定義 LayoutManager 來實現咱們對於 RecyclerView的滑動監聽

package com.yang.dyvideo.utils

import android.content.Context
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.PagerSnapHelper
import android.support.v7.widget.RecyclerView
import android.view.View
import com.yang.dyvideo.presenter.OnViewPagerListener

/**
 * @author yangzc
 *	@data 2019/5/29 14:14
 *	@desc
 *
 */
class MyLayoutManager : LinearLayoutManager, RecyclerView.OnChildAttachStateChangeListener {


    constructor(context: Context) : super(context)

    constructor(context: Context, @RecyclerView.Orientation orientation: Int,
                reverseLayout: Boolean) : super(context, orientation, reverseLayout) {
        pagerSpaner = PagerSnapHelper()
    }

    var pagerSpaner: PagerSnapHelper? = null
    var viewPagerListener: OnViewPagerListener? = null
    var diffY = 0

    override fun onAttachedToWindow(view: RecyclerView) {
        super.onAttachedToWindow(view)
        view.addOnChildAttachStateChangeListener(this)
        pagerSpaner!!.attachToRecyclerView(view)
    }


    override fun onChildViewDetachedFromWindow(p0: View) {
        val position = getPosition(p0)
        if (0 < diffY) {
            viewPagerListener?.onPageRelease(true, position)
        } else {
            viewPagerListener?.onPageRelease(false, position)
        }
    }

    override fun onChildViewAttachedToWindow(p0: View) {

        val position = getPosition(p0)
        if (0 == position) {
            viewPagerListener?.onPageSelected(position, false)
        }


    }

    override fun onScrollStateChanged(state: Int) {
        if (RecyclerView.SCROLL_STATE_IDLE == state) {
            val view = pagerSpaner!!.findSnapView(this)
            val position = getPosition(view!!)
            viewPagerListener?.onPageSelected(position, position == itemCount - 1)
        }
        super.onScrollStateChanged(state)
    }

    fun setOnViewPagerListener(listener: OnViewPagerListener) {
        viewPagerListener = listener
    }

    override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler?, state: RecyclerView.State?): Int {
        diffY = dy
        return super.scrollVerticallyBy(dy, recycler, state)
    }
}
複製代碼

定義完成以後,須要實現播放和中止播放的兩個方法,這兩個方法相對就比較簡單了,分別是

releaseVideo

private fun releaseVideo(index: Int) {
        val itemView = rlv_play_video.getChildAt(index)
        val videoView = itemView.findViewById<FullWindowVideoView>(R.id.surface_view)
        val imgThumb = itemView.findViewById<ImageView>(R.id.iv_video_cover)
        videoView.stopPlayback()
        imgThumb.animate().alpha(1f).start()

    }
複製代碼

playVideo

private fun playVideo(position: Int) {
        val itemView = rlv_play_video.getChildAt(position)
        val videoView = itemView.findViewById<FullWindowVideoView>(R.id.surface_view)
        val imgThumb = itemView.findViewById<ImageView>(R.id.iv_video_cover)
        val mediaPlayer = arrayOfNulls<MediaPlayer>(1)

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            videoView.setOnInfoListener { mp, what, extra ->
                mediaPlayer[0] = mp
                mp.isLooping = true
                imgThumb.animate().alpha(0f).start()
                mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT)
                false
            }
        }
        videoView.start()

        if (iv_video_play.visibility == View.VISIBLE) {
            iv_video_play.visibility = View.GONE
        }
    }
複製代碼

結合咱們以前自定義的滑動監聽,就能夠實現 上下滑動播放詳情頁的效果了

this.myLayoutManager?.setOnViewPagerListener(object : OnViewPagerListener {
            override fun onInitComplete() {

            }

            override fun onPageRelease(isNext: Boolean, position: Int) {
                var index: Int
                index = if (isNext) {
                    0
                } else {
                    1
                }
                releaseVideo(index)
            }

            override fun onPageSelected(position: Int, bottom: Boolean) {
                playVideo(0)
            }
        })

複製代碼

截止目前爲止,咱們須要實現的第一個需求 上下滑動播放詳情頁已經實現了。

高仿抖音播放(三)——細節的優化

上一篇文章,咱們着重實現了 上下滑動播放詳情頁播放以後自動播放 接下來,咱們要實現的就是 雙擊點贊效果,而且點贊單擊暫停,再單擊播放這兩個功能。

GitHub地址:github.com/yang0range/…

歡迎star

首先,實現如下雙擊點擊的動畫效果。
這個動畫,能夠看到咱們要實現的效果。


能夠看到,主要有如下幾個功能點。

1. 縮放的動畫
2. 位移的動畫
3. 透明度的動畫
4. 還有一個點擊的動做的監聽

梳理完功能點以後,就比較好實現對應的效果了。

三個動畫效果,咱們能夠直接用ObjectAnimator這個類來實現。

點擊效果,天然就涉及到了 dispatchTouchEvent

梳理完要實現的效果和方法以後,詳細就看代碼。

咱們自定義了一個點讚的動畫。

package com.yang.dyvideo.widget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
import android.content.Context;
import android.graphics.Canvas;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import com.yang.dyvideo.R;
import com.yang.dyvideo.presenter.MyClickListener;

import java.util.Random;



/**
 * @author yangzc
 * @data 2019/5/31 13:13
 * @desc
 */
public class VideoLikeView extends RelativeLayout {
    private Context mContext;
    float[] num = {-30, -20, 0, 20, 30};//隨機心形圖片角度
    //記錄上一次的點擊時間
    private long  lastClickTime = 0;
    //點擊的時間間隔
    private long INTERVAL = 200;
    private MyClickListener.MyClickCallBack onClickListener;

    public VideoLikeView(Context context) {
        super(context);
        initView(context);
    }

    public VideoLikeView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public VideoLikeView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView(context);
    }

    private void initView(Context context) {
        mContext = context;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                //獲取點擊時間
                long currTime = System.currentTimeMillis();
                //判斷點擊之間的時間差
                long  interval = currTime - lastClickTime;
                lastClickTime = currTime;
                if(interval <INTERVAL ){
                    final ImageView imageView = new ImageView(mContext);
                    //設置展現的位置,須要在手指觸摸的位置上方,即觸摸點是心形的右下角的位置
                    LayoutParams params = new LayoutParams(300, 300);
                    params.leftMargin = (int) event.getX() - 150;
                    params.topMargin = (int) event.getY() - 300;
                    //設置圖片資源
                    imageView.setImageDrawable(getResources().getDrawable(R.mipmap.ic_heart));
                    imageView.setLayoutParams(params);
                    //把IV添加到父佈局當中
                    addView(imageView);
                    //設置控件的動畫
                    AnimatorSet animatorSet = new AnimatorSet();
                    //縮放動畫,X軸2倍縮小至0.9倍
                    animatorSet.play(scale(imageView, "scaleX", 2f, 0.9f, 100, 0))
                            //縮放動畫,Y軸2倍縮放至0.9倍
                            .with(scale(imageView, "scaleY", 2f, 0.9f, 100, 0))
                            //旋轉動畫,隨機旋轉角
                            .with(rotation(imageView, 0, 0, num[new Random().nextInt(4)]))
                            //漸變透明動畫,透明度從0-1
                            .with(alpha(imageView, 0, 1, 100, 0))
                            //縮放動畫,X軸0.9倍縮小至
                            .with(scale(imageView, "scaleX", 0.9f, 1, 50, 150))
                            //縮放動畫,Y軸0.9倍縮放至
                            .with(scale(imageView, "scaleY", 0.9f, 1, 50, 150))
                            //位移動畫,Y軸從0上移至600
                            .with(translationY(imageView, 0, -600, 800, 400))
                            //透明動畫,從1-0
                            .with(alpha(imageView, 1, 0, 300, 400))
                            //縮放動畫,X軸1至3倍
                            .with(scale(imageView, "scaleX", 1, 3f, 700, 400))
                            //縮放動畫,Y軸1至3倍
                            .with(scale(imageView, "scaleY", 1, 3f, 700, 400));
                    //開始動畫
                    animatorSet.start();
                    //設置動畫結束監聽
                    animatorSet.addListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            super.onAnimationEnd(animation);
                            //當動畫結束之後,須要把控件從父佈局移除
                            removeViewInLayout(imageView);
                        }
                    });
                }
                break;
        }
        return super.dispatchTouchEvent(event);
    }
    /**
     * 縮放動畫
     * @param view
     * @param propertyName
     * @param from
     * @param to
     * @param time
     * @param delayTime
     * @return
     */
    public static ObjectAnimator scale(View view, String propertyName, float from, float to, long time, long delayTime) {
        ObjectAnimator translation = ObjectAnimator.ofFloat(view
                , propertyName
                , from, to);
        translation.setInterpolator(new LinearInterpolator());
        translation.setStartDelay(delayTime);
        translation.setDuration(time);
        return translation;
    }
    /**
     * 位移動畫
     * @param view
     * @param from
     * @param to
     * @param time
     * @param delayTime
     * @return
     */
    public static ObjectAnimator translationX(View view, float from, float to, long time, long delayTime) {
        ObjectAnimator translation = ObjectAnimator.ofFloat(view
                , "translationX"
                , from, to);
        translation.setInterpolator(new LinearInterpolator());
        translation.setStartDelay(delayTime);
        translation.setDuration(time);
        return translation;
    }
    public static ObjectAnimator translationY(View view, float from, float to, long time, long delayTime) {
        ObjectAnimator translation = ObjectAnimator.ofFloat(view
                , "translationY"
                , from, to);
        translation.setInterpolator(new LinearInterpolator());
        translation.setStartDelay(delayTime);
        translation.setDuration(time);
        return translation;
    }
    /**
     * 透明度動畫
     * @param view
     * @param from
     * @param to
     * @param time
     * @param delayTime
     * @return
     */
    public static ObjectAnimator alpha(View view, float from, float to, long time, long delayTime) {
        ObjectAnimator translation = ObjectAnimator.ofFloat(view
                , "alpha"
                , from, to);
        translation.setInterpolator(new LinearInterpolator());
        translation.setStartDelay(delayTime);
        translation.setDuration(time);
        return translation;
    }
    public static ObjectAnimator rotation(View view, long time, long delayTime, float... values) {
        ObjectAnimator rotation = ObjectAnimator.ofFloat(view, "rotation", values);
        rotation.setDuration(time);
        rotation.setStartDelay(delayTime);
        rotation.setInterpolator(new TimeInterpolator() {
            @Override
            public float getInterpolation(float input) {
                return input;
            }
        });
        return rotation;
    }
    public void setOnClickListener(MyClickListener.MyClickCallBack onClickListener) {
        this.onClickListener = onClickListener;
    }
    public MyClickListener.MyClickCallBack getOnClickListener() {
        return onClickListener;
    }
}

複製代碼

接下來,咱們就要實現單擊暫停、播放的功能

由於這個功能也涉及到了的點擊,因此,咱們就須要區分一下在屏幕上是雙擊,仍是單擊。

雙擊就是點讚的動畫,單擊就是播放、暫停的功能。

在這裏,採用的HandlerpostDelayed方法,來區分兩次點擊之間的延時點擊時間timeout,同時設計一個連續點擊的計數器clickCount來記錄在timeout的時間裏的點擊次數。clickCount爲1則是播放、暫停的方法,若是 clickCount爲2 則爲點讚的效果。

理清了思路,接下來咱們看一下實現的代碼。

mAdapter!!.setOnItemClickListener(object : VideoPlayAdapter.OnItemClickListener {
             var timeout = 500//雙擊間百毫秒延時
             var clickCount = 0//記錄連續點擊次數
             var handler = Handler()

            override fun onItemClick(view: View, position: Int) {
                clickCount++
                val mini_surface_view = view.findViewById<FullWindowVideoView>(R.id.surface_view)
                view.setOnTouchListener { v, event ->
                    if (event.action == MotionEvent.ACTION_DOWN) {
                        handler.postDelayed({
                            if (clickCount == 1) {
                                if (mini_surface_view.isPlaying()) {
                                    mini_surface_view.pause()
                                    iv_video_play.setVisibility(View.VISIBLE)
                                } else {
                                    iv_video_play.setVisibility(View.GONE)
                                    val mediaPlayer = arrayOfNulls<MediaPlayer>(1)
                                    mini_surface_view.setOnPreparedListener(MediaPlayer.OnPreparedListener { })
                                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                                        mini_surface_view.setOnInfoListener(MediaPlayer.OnInfoListener { mp, what, extra ->
                                            mediaPlayer[0] = mp
                                            mp.isLooping = true
                                            mp.setVideoScalingMode(MediaPlayer.VIDEO_SCALING_MODE_SCALE_TO_FIT)
                                            false
                                        })
                                    }
                                    mini_surface_view.start()
                                }
                            } else if (clickCount >= 2) {
                                if (!videolist?.get(position)!!.like) {
                                    praiseMethod(position)
                                }
                            }
                            handler.removeCallbacksAndMessages(null)
                            //清空handler延時,並防內存泄漏
                            clickCount = 0//計數清零
                        }, timeout.toLong())//延時timeout後執行run方法中的代碼
                    }
                    false
                }
            }
        })
複製代碼

至此,咱們要實現的整個功能都完成了。

歡迎「點贊」、「關注」。

若是要有任何問題、BUG歡迎給我留言。

相關文章
相關標籤/搜索