项目demo: https://download.csdn.net/download/BirdEatBug/19033060 无需积分

一、工程所依赖的库


1、播放器:IjkPlayer(哔哩哔哩开源库)、2、点赞库 :heartLirary、3、EventBus(用于视频资源回收调用)、4、弹窗:-》'com.kongzue.dialog_v3x:dialog:3.2.4' (视频加载等待)、5、下拉上拉库SmallRefreshLayout   6、so库 使用GsyVideoPlayer so     库 api 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.1.3-jitpack'

目录结构

heartlibraary  (子依赖)-->vpplayer、

kgplayer (子依赖)-->vpplayer、

二、主界面

1、activity_list_video.xml  activity主界面

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/clContainer"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#616161"tools:ignore="MissingDefaultResource"><Viewandroid:id="@+id/viewTopBg"android:layout_width="match_parent"android:layout_height="165dp"android:background="@drawable/gradient_gray_down"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><com.scwang.smart.refresh.layout.SmartRefreshLayoutandroid:id="@+id/refreshView"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent"><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><androidx.viewpager2.widget.ViewPager2android:id="@+id/viewPager"android:layout_width="match_parent"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout></com.scwang.smart.refresh.layout.SmartRefreshLayout></androidx.constraintlayout.widget.ConstraintLayout>

2、主Activity


package com.qiqilego.vpplayerimport android.os.Handler
import android.view.View
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.qiqilego.vpplayer.adapter.VideoListPagerAdapter
import com.qiqilego.vpplayer.event.VideoIndexEvent
import com.qiqilego.vpplayer.fragment.VideoPlayForActivityFragment
import com.scwang.smart.refresh.footer.ClassicsFooter
import com.scwang.smart.refresh.header.MaterialHeader
import kotlinx.android.synthetic.main.activity_list_video.*class VideoListPlayerActivity : BaseActivity() {companion object {const val TEST_URL = "http://clips.vorwaerts-gmbh.de/big_buck_bunny.mp4"}override fun getLayoutId() = R.layout.activity_list_videooverride fun setTitle() = ""private val handler = Handler()private lateinit var adapter: VideoListPagerAdapteroverride fun initView() {//初始化ViewPageradapter = VideoListPagerAdapter(this)viewPager.adapter = adapterviewPager.orientation = ViewPager2.ORIENTATION_VERTICAL//去除左右阴影if (viewPager.getChildAt(0) is RecyclerView) {viewPager.getChildAt(0).overScrollMode = View.OVER_SCROLL_NEVER}viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {override fun onPageSelected(position: Int) {super.onPageSelected(position)//发送EventBushandler.removeCallbacksAndMessages(null)handler.postDelayed({eventBus.post(VideoIndexEvent(position))}, 500)}})refreshView.setRefreshHeader(MaterialHeader(this).apply {//设置强调颜色setProgressBackgroundColorSchemeColor(resources.getColor(R.color.white))})refreshView.setRefreshFooter(ClassicsFooter(this).apply {//设置强调颜色setAccentColorId(R.color.white)//设置主题颜色setPrimaryColorId(R.color._000000)//设置刷新完成显示的停留时间setFinishDuration(0)})refreshView.setOnRefreshListener {val list = mutableListOf<VideoPlayForActivityFragment>()for (index in 0..20) {list.add(VideoPlayForActivityFragment(TEST_URL, index))}adapter.addFirstData(list as MutableList<Fragment>)refreshView.finishRefresh(500)}refreshView.setOnLoadMoreListener {val list = mutableListOf<VideoPlayForActivityFragment>()for (index in 0..10) {list.add(VideoPlayForActivityFragment(TEST_URL, index))}adapter.addData(list as MutableList<Fragment>)refreshView.finishLoadMore(500)}}override fun initDate() {refreshView.autoRefresh()}}

3、ViewPager2适配器

package com.qiqilego.vpplayer.adapterimport androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.qiqilego.vpplayer.VideoListPlayerActivity
import com.qiqilego.vpplayer.fragment.VideoPlayForActivityFragment
import kotlinx.android.synthetic.main.activity_list_video.*class VideoListPagerAdapter(private val activity: AppCompatActivity) : FragmentStateAdapter(activity) {private val dataList = mutableListOf<Fragment>()fun addData(newData: MutableList<Fragment>) {if (newData.isNotEmpty()) {val oldSize = dataList.size//安全验证 》=50条 删除之前缓存数据if (oldSize + newData.size >= 50) {newData.add(0,VideoPlayForActivityFragment((dataList.last() as VideoPlayForActivityFragment).videoUrl,-1))dataList.clear()dataList.addAll(newData)//重新赋值下标dataList.forEachIndexed { index, it ->if (it is VideoPlayForActivityFragment) {it.position = index}}notifyDataSetChanged()if (activity is VideoListPlayerActivity) {activity.viewPager.setCurrentItem(0, false)}} else {dataList.addAll(newData)dataList.forEachIndexed { index, it ->if (it is VideoPlayForActivityFragment) {it.position = index}}notifyItemRangeInserted(oldSize, dataList.size)}}}fun addFirstData(newData: MutableList<Fragment>) {if (newData.isNotEmpty()) {dataList.clear()dataList.addAll(newData)dataList.forEachIndexed { index, it ->if (it is VideoPlayForActivityFragment) {it.position = index}}notifyDataSetChanged()}}fun getData() = dataListoverride fun getItemCount() = dataList.sizeoverride fun createFragment(position: Int): Fragment {return dataList[position]}override fun getItemId(position: Int): Long {return dataList[position].hashCode().toLong()}
}

三、视频播放详情界面


1、视频详情xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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="#000000"><com.kuaige.player.player.VideoPlayerandroid:id="@+id/video"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><com.lzj.douyin.redheart.library.RedHeartLayoutandroid:id="@+id/ivHeart"android:layout_width="0dp"android:layout_height="0dp"app:heart_height="100"app:heart_image_resId="@drawable/ic_heart"app:heart_width="100"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><ImageViewandroid:id="@+id/ivVideoThumb"android:layout_width="0dp"android:layout_height="0dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><ImageViewandroid:id="@+id/ivPlayer"android:layout_width="85dp"android:layout_height="85dp"android:layout_marginBottom="90dp"android:src="@drawable/ic_find_video_play"android:visibility="gone"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"tools:visibility="visible" /><ImageViewandroid:id="@+id/ivLike"android:layout_width="55dp"android:layout_height="55dp"android:src="@drawable/ic_gray_heart"android:layout_marginTop="200dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintTop_toTopOf="parent"tools:ignore="MissingConstraints" /><includeandroid:id="@+id/videoPlayFail"layout="@layout/video_play_fail"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginBottom="90dp"android:visibility="gone"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"tools:visibility="visible" /></androidx.constraintlayout.widget.ConstraintLayout>

2、播放失败xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"xmlns:tools="http://schemas.android.com/tools"android:orientation="vertical"><TextViewandroid:singleLine="true"android:textColor="@color/white"android:textSize="18sp"android:layout_gravity="center"tools:text="@string/net_fail_retry"android:layout_width="wrap_content"android:layout_height="wrap_content"/><TextViewandroid:id="@+id/tvPlayRetry"android:gravity="center"android:textSize="18sp"android:textColor="@color/white"android:background="@drawable/white_corner_25"android:text="@string/click_retry"android:layout_marginTop="35dp"android:layout_gravity="center"android:layout_width="160dp"android:layout_height="46dp"/>
</LinearLayout>

3、视频播放Fragment

package com.qiqilego.vpplayer.fragmentimport android.annotation.SuppressLint
import android.os.Handler
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import androidx.appcompat.app.AppCompatActivity
import com.kongzue.dialog.v3.WaitDialog
import com.kuaige.player.listener.VideoListener
import com.lzj.douyin.redheart.library.RedHeartLayout
import com.qiqilego.vpplayer.R
import com.qiqilego.vpplayer.event.VideoIndexEvent
import kotlinx.android.synthetic.main.fragment_video_recommend.*
import kotlinx.android.synthetic.main.video_play_fail.*
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import tv.danmaku.ijk.media.player.IMediaPlayer
import kotlin.math.abs/*** 适用于Activity管理*/
class VideoPlayForActivityFragment(var videoUrl: String?,var position: Int
) : BaseFragment(),VideoListener, View.OnClickListener,RedHeartLayout.HeartTouchListener {private var isPrepared = false//是否释放过视频资源private var isRelease = false//是否执行过stopprivate var selfStop = falseoverride fun getLayoutId() = R.layout.fragment_video_recommendoverride fun setTitle() = ""private val handler = Handler()@SuppressLint("SetTextI18n")override fun viewCreated(view: View) {video?.run {setVideoListener(this@VideoPlayForActivityFragment)//硬件加速Gpu渲染setEnableMediaCodec(true)}}@SuppressLint("ClickableViewAccessibility")override fun initDate() {tvPlayRetry.setOnClickListener(this)ivPlayer.setOnClickListener(this)//点赞按钮ivLike.setOnClickListener(this)ivLike.tag = falseivLike.setImageResource(R.drawable.ic_gray_heart)ivHeart.setHeartTouchListener(this)}override fun onResume() {super.onResume()if (!video.isPlaying && isPrepared) {if (selfStop || isRelease) {selfStop = falsevideo.reset()video.setPath(videoUrl)video.load()} else {video.start()ivVideoThumb.visibility = GONE}ivPlayer.visibility = GONE}//首次拉取视频uri进行播放else if (!video.isPlaying && !isPrepared) {video.run {setPath(videoUrl)load()}ivPlayer.visibility = GONE}}/*** Eventbus机制 用来释放video资源引用 当前播放视频的前后5页不被释放、其余都要释放视频缓存* @param event.position 为当前播放页视频下标*/@Subscribe(threadMode = ThreadMode.MAIN)override fun onMessageEvent(event: Any?) {super.onMessageEvent(event)if (event is VideoIndexEvent) {val needRelease = abs(position - event.position) > 4if (needRelease && !isRelease) {video?.post {video?.stop()video?.release()videoUrl = nullisRelease = true}}}}override fun onPause() {super.onPause()ivPlayer.visibility = VISIBLEvideo.pause()}override fun onStop() {super.onStop()video?.stop()selfStop = trueivPlayer.visibility = VISIBLE}override fun onDestroy() {super.onDestroy()video?.stop()video?.release()}override fun onSeekComplete(player: IMediaPlayer?) {}override fun onInfo(player: IMediaPlayer?, p1: Int, p2: Int): Boolean {return false}override fun onVideoSizeChanged(player: IMediaPlayer?, p1: Int, p2: Int, p3: Int, p4: Int) {}override fun onBufferingUpdate(player: IMediaPlayer?, p1: Int) {}override fun onPrepared(player: IMediaPlayer?) {WaitDialog.dismiss()isPrepared = trueisRelease = falseif (player != null && !player.isPlaying && isResumed) {player.start()ivVideoThumb.visibility = GONE}videoPlayFail?.visibility = GONE}override fun onCompletion(player: IMediaPlayer?) {//循环播放if (isResumed) {player?.start()}}override fun onError(player: IMediaPlayer?, p1: Int, p2: Int): Boolean {WaitDialog.dismiss()videoPlayFail?.visibility = VISIBLEreturn false}override fun onClick(v: View?) {when (v) {tvPlayRetry -> {WaitDialog.show(activity as AppCompatActivity, R.string.video_loading)videoPlayFail.visibility = GONEvideo.reset()video.setPath(videoUrl)video.load()}ivPlayer -> {if (selfStop) {selfStop = falsevideo.reset()video.setPath(videoUrl)video.load()} else {video.start()ivVideoThumb.visibility = GONE}ivPlayer.visibility = GONE}ivLike -> {if (ivLike.tag == false) {ivLike.tag = trueivLike.setImageResource(R.drawable.ic_red_heart)} else {ivLike.tag = falseivLike.setImageResource(R.drawable.ic_gray_heart)}}}}override fun onDoubleBefore() {handler.postDelayed({if (video.isPlaying) {video.pause()ivPlayer.visibility = VISIBLE}}, 200)}override fun onDoubleListener() {handler.removeCallbacksAndMessages(null)if (ivLike.tag == false) {ivLike.tag = trueivLike.setImageResource(R.drawable.ic_red_heart)} else {ivLike.tag = falseivLike.setImageResource(R.drawable.ic_gray_heart)}}override fun onDoubleAfter() {}
}

3、EventBus 实体类

data class VideoIndexEvent(val position: Int)

四、相关Base类


1、BaseActivity

package com.qiqilego.vpplayerimport android.graphics.Typeface
import android.os.Bundle
import android.view.View
import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadModeabstract class BaseActivity : AppCompatActivity() {lateinit var eventBus: EventBusoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)supportActionBar?.hide()eventBus = EventBus.getDefault()if (needInputSoftMode())window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN)setViewBefore()if (getLayoutId() != null) {setContentView(getLayoutId()!!)} else {setContentView(getLayoutView())}initView()initDate()}/*** 布局id*/abstract fun getLayoutId(): Int?open fun getLayoutView(): View? = null/*** 状态栏颜色*/open fun statusBarColor() = -1/*** 设置标题*/abstract fun setTitle(): String//设置标题背景 资源idopen fun setTitleBackGround(): Int? = null//设置ttf格式字体open fun setTitleFontStyle(): Typeface? = null/*** 初始化*/abstract fun initView()/*** 数据初始化*/abstract fun initDate()/*** 左侧返回按钮*/open fun needBackIcon() = false/*** 是否需要返回监听事件*/open fun needBackEvent() = true/*** 右侧返回按钮*/open fun needRightIcon() = false/*** 右侧文字按钮*/open fun needRightTv() = false/*** 输入法高度自适应EdiTText*/open fun needInputSoftMode() = false/*** 隐私游乐园*/open fun needTeamTimeTitle() = false/*** setContentView 之前执行的逻辑*/open fun setViewBefore() {}override fun onStart() {super.onStart()if (!EventBus.getDefault().isRegistered(this))EventBus.getDefault().register(this)}override fun onDestroy() {super.onDestroy()EventBus.getDefault().unregister(this)}@Subscribe(threadMode = ThreadMode.MAIN)open fun onMessageEvent(event: Any?) {}}

2、BaseFragment

package com.qiqilego.vpplayer.fragmentimport android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadModeabstract class BaseFragment : Fragment() {lateinit var eventBus: EventBusoverride fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {return inflater.inflate(getLayoutId(), container, false)}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)viewCreated(view)eventBus=EventBus.getDefault()initDate()}/*** 布局id*/abstract fun getLayoutId(): Int/*** 设置标题*/abstract fun setTitle(): String/*** view初始化*/abstract fun viewCreated(view: View)/*** view监听绑定*/abstract fun initDate()/*** 左侧返回按钮*/open fun needBackIcon() = false/*** 右侧返回按钮*/open fun needRightIcon() = falseoverride fun onAttach(context: Context) {super.onAttach(context)if (!EventBus.getDefault().isRegistered(this))EventBus.getDefault().register(this)}override fun onDetach() {super.onDetach()EventBus.getDefault().unregister(this)}@Subscribe(threadMode = ThreadMode.MAIN)open fun onMessageEvent(event: Any?) {}
}

五、相关图片字符串资源


1、图片资源

drawable 资源

gradient_gray_down.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><gradientandroid:centerX="0"android:centerY="0"android:endColor="#00000000"android:gradientRadius="0dp"android:angle="270"android:startColor="#59000000"android:type="linear" />
</shape>

white_corner_25.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"><stroke android:width="1dp" android:color="#F4F4F4" /><corners android:radius="25dp" />
</shape>

drawable-xhdpi

ic_find_video_play->    ic_gray_heart->   ic_red_heart-> 

color 及string 文件

要用到的color   <color name="_000000">#000000</color>

相关string.xml

<string name="app_name">仿抖音</string><string name="net_fail_retry">网络不给力,请稍后重试!</string>
<string name="click_retry">点击重试</string>
<string name="video_loading">视频重新加载中…</string>

vpplayer----------->build.gradle

plugins {id 'com.android.application'id 'kotlin-android'id 'kotlin-android-extensions'id 'kotlin-kapt'
}android {compileSdkVersion 30buildToolsVersion "30.0.3"defaultConfig {applicationId "com.qiqilego.vpplayer"minSdkVersion 21targetSdkVersion 30versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility JavaVersion.VERSION_1_8targetCompatibility JavaVersion.VERSION_1_8}kotlinOptions {jvmTarget = '1.8'}
}dependencies {implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"implementation 'androidx.core:core-ktx:1.3.2'implementation 'androidx.appcompat:appcompat:1.2.0'implementation 'com.google.android.material:material:1.2.1'implementation 'androidx.constraintlayout:constraintlayout:2.0.4'implementation project(path: ':kgplayer')implementation project(path: ':heartlibrary')testImplementation 'junit:junit:4.+'androidTestImplementation 'androidx.test.ext:junit:1.1.2'androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'// SmartRefreshLayout 刷新框架implementation 'com.scwang.smart:refresh-layout-kernel:2.0.3'//经典刷新头implementation 'com.scwang.smart:refresh-header-classics:2.0.3'implementation  'com.scwang.smart:refresh-header-material:2.0.3'//Event buskapt "org.greenrobot:eventbus-annotation-processor:3.2.0"implementation 'com.kongzue.dialog_v3x:dialog:3.2.4'
}kapt {arguments {arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex')}
}

如图

kgplayer ---------------->build.gradle

apply plugin: 'com.android.library'android {compileSdkVersion rootProject.ext.compileSdkVersionbuildToolsVersion rootProject.ext.buildToolsVersiondefaultConfig {minSdkVersion rootProject.ext.minSdkVersiontargetSdkVersion rootProject.ext.targetSdkVersion}/* sourceSets {main {jniLibs.srcDirs = ['jniLibs']}}*/
}dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"api "tv.danmaku.ijk.media:ijkplayer-java:$rootProject.ijkPlayerVersion"api 'com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-ex_so:v8.1.3-jitpack'api 'org.greenrobot:eventbus:3.2.0'
}

heartlibrary------------->build.gradle

apply plugin: 'com.android.library'android {compileSdkVersion rootProject.ext.compileSdkVersionbuildToolsVersion rootProject.ext.buildToolsVersiondefaultConfig {minSdkVersion rootProject.ext.minSdkVersiontargetSdkVersion rootProject.ext.targetSdkVersion}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'}}compileOptions {sourceCompatibility = 1.8targetCompatibility = 1.8}
}dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])implementation 'androidx.appcompat:appcompat:1.1.0'testImplementation 'junit:junit:4.12'androidTestImplementation 'androidx.test.ext:junit:1.1.1'androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}

根目录build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {ext.kotlin_version = "1.4.32"repositories {google()jcenter()}dependencies {classpath "com.android.tools.build:gradle:4.1.1"classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"// NOTE: Do not place your application dependencies here; they belong// in the individual module build.gradle files}
}ext {compileSdkVersion = 30buildToolsVersion = "30.0.3"minSdkVersion = 21targetSdkVersion = 30appTargetSdkVersion = 30supportLibraryVersion = '30.0.3'ijkPlayerVersion = '0.8.8'
}allprojects {repositories {google()jcenter()maven { url 'https://jitpack.io' }}
}task clean(type: Delete) {delete rootProject.buildDir
}

版本信息:gradle-6.5   android studio 4.1.1  Kotlin版本 "1.4.32"

sdk版本参考 vpplayer.build.gradle


至此完成!!!

ViewPager2 +Fragment 模仿抖音短视频相关推荐

  1. 整合vite2.0+electron12+vant3.x跨端仿抖音短视频+聊天+直播exe应用

    vite2-electron-douyin 基于vite2.x+electron模仿抖音短视频应用实例. 整合了vite2+electron跨端开发技术仿制抖音界面桌面版exe应用软件.基于Vite2 ...

  2. 抖音初期运营,如何让自己的抖音短视频账号快速涨粉:国仁楠哥

    在没有做抖音短视频之前是不是感觉很简单,只是简单那的发发视频就可以上热门,就可以有很多人看?当自己在看别人视频的时候,内心就在想,我其实可以做的比他更好的内容,也能轻松上热门. 有多少人曾经对自己发布 ...

  3. 抖音短视频企业号如何运营

    我们今天说说企业号的运营,我们知道个人号的内容,就像我们自己在生活中遇到的一些有趣的事,有意思的人.和个人号运营不同的是,企业号的运营,其实有更多的技巧和出路,当然也会有更多的资源和优势,在我们现在这 ...

  4. 抖音短视频零基础能做到百万粉丝吗?国仁楠哥

    抖音短视频,一个旨在帮助大众用户表达自我,记录美好生活的短视频分享平台. 相信很多人遇到这样糟心的经历,抖音发作品浏览量不是很高,慢慢的做着做着就放弃了,都在抱怨没有流量,看到别人在抖音上面带货卖货, ...

  5. 高新计算机模拟视频2018,抖音短视频电脑版2018 无需模拟器版

    抖音短视频电脑版,终于是在本站发布了,这款不同于以往的版本,无需你们用模拟器,直接打开后,就能进行快速的登陆,可以在线的欣赏令人称奇的作品,认识到更多朋友. 抖音短视频电脑版简介 拍摄一个音乐短视频, ...

  6. 抖音短视频APP——产品体验报告

    本文是抖音短视频APP的一份产品体验报告,主要从体验环境.市场概况.产品概况.目标用户人群.产品架构图.主要功能分析.交互体验.视觉设计和优化建议这九个方面进行阐述. 本文是本人入门产品经理的一份练习 ...

  7. 抖音短视频批量采集下载软件哪些好?如何下载?轻松搬运视频,快速批量处理水印去除LOGO!...

    抖音短视频批量采集下载软件哪些好?如何下载? 抖音短视频去重消重去水印软件哪些好?如何下载? 腾讯视频批量采集下载软件哪些好?怎么下载? 腾讯视频去重消重去水印软件哪些好?如何下载? 轻松搬运视频,快 ...

  8. 看看做抖音短视频的十大误区

    在没有做抖音短视频之前是不是感觉很简单,只是发发视频就可以上热门,就可以有很多人看? 看了别人热门视频,我可以做比他更好的视频,也能上热门? 学习了很多抖音的课程,去操作了发现还是不行,上不去量! 很 ...

  9. 抖音短视频运营干货:从入门到精通值得一阅!

    "两微一抖"已成企业的营销标配,但对于玩转抖音还懵懵懂懂的你,这篇超级干货,会让你满载而归的! ‍ 01 抖音入门篇 1.新注册的号为何不能马上发作品,需要先养号? 在正式开始发布 ...

最新文章

  1. 笔记本高分屏字体模糊_高色域+高分辨率轻薄本推荐,你需要2K屏笔记本电脑么?...
  2. 【杂谈】超过12个,150页深度学习开源框架指导手册与GitHub项目,初学CV你值得拥有...
  3. 解决toolbar左边空出一部分的问题
  4. 守护线程Daemon的理解
  5. Java反射机制是什么?
  6. pytorch分布式训练(一):torch.nn.DataParallel
  7. [dfs] 洛谷 P1822 魔法指纹
  8. android adjust,【报Bug】安卓 adjust-position设置为false 页面依然被顶起 ios是好的
  9. mysql系统变量配置文件_MySQL系统变量配置基础
  10. windows 环境下.Net使用Redis缓存
  11. Linux vim常用命令
  12. 如何在matlab中读写segy格式数据
  13. 在VirtualBox Linux 7u2 中安装Oracle RAC 12.2.0.1.0
  14. Android控制所有播放器的音频切换上下首歌、播放、停止
  15. Thread多线程-(最容易被问到的面试题)
  16. 几个国外广告联盟介绍
  17. freesurfer对结构核磁共振成像分割输出结果介绍
  18. 计算机的硬盘类型及特点是,电脑硬盘中的蓝盘、黑盘、红盘、绿盘有什么区别?特点?...
  19. usercity 小程序_微信小程序API 用户信息 wx.getUserInfo(OBJECT)
  20. SQL更改表名,数据库名,字段名

热门文章

  1. ViT-Adapter:Vision Transformer Adapter for Dense Predictions
  2. 基于Python制作的24点游戏生成器
  3. VR全景旅游开发让旅游足不出户身临其境的感觉
  4. Android MTK 6750 系统语言与输入法
  5. 应用统计432考研复试提问总结精简版【二】
  6. CCIE自学-by闫辉NP视频:QoS小结
  7. 300亿收购芯片厂,佳能还能继续“感动常在”吗?
  8. Matlab二维循环卷积程序 cconv2
  9. Daz dForce:基础知识及教程
  10. SpringBoot项目国际化