Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Contribute to GitLab
Sign in
Toggle navigation
I
ImageVideoBanner
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
jiangjiantao
ImageVideoBanner
Commits
0ef30b50
Commit
0ef30b50
authored
Apr 28, 2021
by
委座-江
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
性能优化
parent
d75937a0
Show whitespace changes
Inline
Side-by-side
Showing
26 changed files
with
1084 additions
and
1005 deletions
+1084
-1005
build.gradle
imageVideoBanner/build.gradle
+8
-4
AndroidManifest.xml
imageVideoBanner/src/main/AndroidManifest.xml
+9
-2
DetailActivity.java
...main/java/com/widget/imagevideobanner/DetailActivity.java
+29
-0
MainActivity.java
...c/main/java/com/widget/imagevideobanner/MainActivity.java
+48
-25
ImageVideoBanner.java
.../com/widget/imagevideobanner/banner/ImageVideoBanner.java
+0
-307
ImageVideoFragment.java
...om/widget/imagevideobanner/banner/ImageVideoFragment.java
+0
-446
MyVideoView.java
.../java/com/widget/imagevideobanner/banner/MyVideoView.java
+0
-50
MediaBean.java
...main/java/com/widget/imagevideobanner/bean/MediaBean.java
+87
-0
IPlayTarget.java
.../java/com/widget/imagevideobanner/player/IPlayTarget.java
+11
-0
PlayerManager.java
...ava/com/widget/imagevideobanner/player/PlayerManager.java
+112
-0
ImageVideoBanner.java
...va/com/widget/imagevideobanner/view/ImageVideoBanner.java
+126
-0
KeyEventCompat.java
...java/com/widget/imagevideobanner/view/KeyEventCompat.java
+4
-4
KeyEventCompatHoneycomb.java
...widget/imagevideobanner/view/KeyEventCompatHoneycomb.java
+1
-1
OnActionFinishListener.java
.../widget/imagevideobanner/view/OnActionFinishListener.java
+5
-0
ViewPager.java
...main/java/com/widget/imagevideobanner/view/ViewPager.java
+142
-139
VpImageView.java
...in/java/com/widget/imagevideobanner/view/VpImageView.java
+94
-0
VpVideoView.java
...in/java/com/widget/imagevideobanner/view/VpVideoView.java
+308
-0
activity_detail.xml
imageVideoBanner/src/main/res/layout/activity_detail.xml
+19
-0
activity_main.xml
imageVideoBanner/src/main/res/layout/activity_main.xml
+15
-4
banner_imge_video.xml
imageVideoBanner/src/main/res/layout/banner_imge_video.xml
+3
-3
item_video_view.xml
imageVideoBanner/src/main/res/layout/item_video_view.xml
+1
-1
layout_exo_player_view.xml
...ideoBanner/src/main/res/layout/layout_exo_player_view.xml
+15
-0
layout_player_view.xml
imageVideoBanner/src/main/res/layout/layout_player_view.xml
+35
-0
layout_simple_exo_player_view.xml
...ner/src/main/res/layout/layout_simple_exo_player_view.xml
+12
-0
styles.xml
imageVideoBanner/src/main/res/values/styles.xml
+0
-2
ExampleUnitTest.java
...c/test/java/com/zxj/imagevideobanner/ExampleUnitTest.java
+0
-17
No files found.
imageVideoBanner/build.gradle
View file @
0ef30b50
...
...
@@ -3,7 +3,7 @@ apply plugin: 'com.android.application'
android
{
compileSdkVersion
28
defaultConfig
{
minSdkVersion
18
minSdkVersion
22
targetSdkVersion
22
versionCode
1
versionName
"1.0"
...
...
@@ -21,13 +21,17 @@ dependencies {
implementation
fileTree
(
dir:
'libs'
,
include:
[
'*.jar'
])
implementation
'com.android.support:appcompat-v7:28.0.0'
implementation
'com.android.support.constraint:constraint-layout:1.1.0'
implementation
'com.github.bumptech.glide:glide:
3.7
.0'
implementation
'com.github.bumptech.glide:glide:
4.9
.0'
//当需要FFmpegMediaMetadataRetriever时必选
implementation
'com.github.wseemann:FFmpegMediaMetadataRetriever-armeabi-v7a:1.0.14'
implementation
'com.wang.avi:library:2.1.3'
// implementation 'com.google.android.exoplayer:exoplayer:2.13.3'
// compile 'com.github.wseemann:FFmpegMediaMetadataRetriever:1.0.14'
//视频播放组件
api
'com.google.android.exoplayer:exoplayer-core:2.7.2'
api
'com.google.android.exoplayer:exoplayer-dash:2.7.2'
api
'com.google.android.exoplayer:exoplayer-ui:2.7.2'
api
'jp.wasabeef:glide-transformations:4.0.0'
}
imageVideoBanner/src/main/AndroidManifest.xml
View file @
0ef30b50
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
package=
"com.widget.imagevideobanner"
>
<uses-permission
android:name=
"android.permission.INTERNET"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.WRITE_EXTERNAL_STORAGE"
/>
<uses-permission
android:name=
"android.permission.READ_EXTERNAL_STORAGE"
/>
<application
android:allowBackup=
"true"
android:name=
".BaseApplication"
android:allowBackup=
"true"
android:icon=
"@mipmap/ic_launcher"
android:label=
"@string/app_name"
android:roundIcon=
"@mipmap/ic_launcher_round"
...
...
@@ -28,6 +29,12 @@
</activity>
<activity
android:name=
".DetailActivity"
android:configChanges=
"orientation|keyboardHidden|screenSize|keyboard|navigation"
android:exported=
"true"
android:screenOrientation=
"portrait"
/>
</application>
</manifest>
\ No newline at end of file
imageVideoBanner/src/main/java/com/widget/imagevideobanner/DetailActivity.java
0 → 100644
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
;
import
android.os.Bundle
;
import
android.support.v7.app.AppCompatActivity
;
import
android.view.View
;
import
android.widget.TextView
;
import
com.widget.imagevideobanner.bean.MediaBean
;
import
java.util.ArrayList
;
import
java.util.List
;
public
class
DetailActivity
extends
AppCompatActivity
{
private
List
<
MediaBean
>
list
=
new
ArrayList
<>();
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setContentView
(
R
.
layout
.
activity_detail
);
TextView
tvBack
=
findViewById
(
R
.
id
.
tvBack
);
tvBack
.
setOnClickListener
(
new
View
.
OnClickListener
()
{
@Override
public
void
onClick
(
View
v
)
{
finish
();
}
});
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/MainActivity.java
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
;
import
android.content.Intent
;
import
android.os.Bundle
;
import
android.os.Environment
;
import
android.support.v7.app.AppCompatActivity
;
import
android.view.View
;
import
android.widget.TextView
;
import
com.widget.imagevideobanner.banner.ImageVideoBanner
;
import
com.widget.imagevideobanner.bean.BannerBean
;
import
com.widget.imagevideobanner.utils.IMMLeaks
;
import
com.widget.imagevideobanner.bean.MediaBean
;
import
com.widget.imagevideobanner.view.ImageVideoBanner
;
import
java.io.File
;
import
java.util.ArrayList
;
import
java.util.List
;
import
static
com
.
widget
.
imagevideobanner
.
bean
.
MediaBean
.
TYPE_IMAGE
;
import
static
com
.
widget
.
imagevideobanner
.
bean
.
MediaBean
.
TYPE_VIDEO
;
public
class
MainActivity
extends
AppCompatActivity
{
private
List
<
BannerBean
>
list
=
new
ArrayList
<>();
private
List
<
MediaBean
>
list
=
new
ArrayList
<>();
ImageVideoBanner
banner
;
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
setContentView
(
R
.
layout
.
activity_main
);
ImageVideoBanner
banner
=
findViewById
(
R
.
id
.
banner
);
setDatas
();
banner
.
addData
(
list
);
banner
.
startBanner
();
banner
=
findViewById
(
R
.
id
.
banner
);
//修复inputManager引发的内存泄漏
IMMLeaks
.
fixFocusedViewLeak
(
BaseApplication
.
getApplication
());
}
MediaBean
mediaBean
=
new
MediaBean
();
mediaBean
.
setUrl
(
"https://hh-oss-html.miyapay.com/hhops/picture/16045733014903c31992a674f.png"
);
mediaBean
.
setResType
(
TYPE_IMAGE
);
list
.
add
(
mediaBean
);
private
void
setDatas
()
{
for
(
int
i
=
0
;
i
<
2
;
i
++)
{
BannerBean
listBean
=
new
BannerBean
();
if
(
i
==
0
)
{
String
uri
=
"android.resource://"
+
getPackageName
()
+
"/"
+
R
.
raw
.
default_guide
;
listBean
.
setUrl
(
uri
);
listBean
.
setType
(
1
);
list
.
add
(
listBean
);
}
else
{
String
url
=
Environment
.
getExternalStorageDirectory
()
+
"/vertical_test.mp4"
;
listBean
.
setUrl
(
url
);
listBean
.
setType
(
1
);
list
.
add
(
listBean
);
}
MediaBean
mediaBean2
=
new
MediaBean
();
mediaBean2
.
setUrl
(
"https://hh-oss-html.miyapay.com/hhops/picture/1611112341654cffdd7b004bb.png"
);
mediaBean2
.
setResType
(
TYPE_IMAGE
);
list
.
add
(
mediaBean2
);
String
basePath
=
"http://miya-hz.oss-cn-shanghai.aliyuncs.com/huihua-test/face-pay/archive/"
;
MediaBean
mediaBean3
=
new
MediaBean
();
mediaBean3
.
setUrl
(
basePath
+
"shu.mp4"
);
mediaBean3
.
setCover
(
basePath
+
"shu.jpeg"
);
mediaBean3
.
setWidth
(
1080
);
mediaBean3
.
setHeight
(
1440
);
mediaBean3
.
setResType
(
TYPE_VIDEO
);
list
.
add
(
mediaBean3
);
MediaBean
mediaBean4
=
new
MediaBean
();
mediaBean4
.
setUrl
(
basePath
+
"heng.mp4"
);
mediaBean4
.
setCover
(
basePath
+
"heng.jpeg"
);
mediaBean4
.
setWidth
(
1440
);
mediaBean4
.
setHeight
(
1080
);
mediaBean4
.
setResType
(
TYPE_VIDEO
);
list
.
add
(
mediaBean4
);
banner
.
bindData
(
list
);
TextView
tvGoDetail
=
findViewById
(
R
.
id
.
tvGoDetail
);
tvGoDetail
.
setOnClickListener
(
new
View
.
OnClickListener
()
{
@Override
public
void
onClick
(
View
v
)
{
startActivity
(
new
Intent
(
MainActivity
.
this
,
DetailActivity
.
class
));
}
});
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/banner/ImageVideoBanner.java
deleted
100644 → 0
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
banner
;
import
android.content.Context
;
import
android.content.res.TypedArray
;
import
android.media.MediaPlayer
;
import
android.os.Bundle
;
import
android.os.Handler
;
import
android.os.Message
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.v4.app.Fragment
;
import
android.support.v4.app.FragmentActivity
;
import
android.support.v4.app.FragmentManager
;
import
android.support.v4.app.FragmentStatePagerAdapter
;
import
android.text.TextUtils
;
import
android.util.AttributeSet
;
import
android.view.LayoutInflater
;
import
android.view.MotionEvent
;
import
android.view.View
;
import
android.widget.FrameLayout
;
import
com.bumptech.glide.util.Util
;
import
com.widget.imagevideobanner.R
;
import
com.widget.imagevideobanner.bean.BannerBean
;
import
com.widget.imagevideobanner.cache.BitmapCache
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Timer
;
import
java.util.TimerTask
;
import
java.util.concurrent.ScheduledExecutorService
;
public
class
ImageVideoBanner
extends
FrameLayout
implements
ViewPager
.
OnPageChangeListener
,
com
.
widget
.
imagevideobanner
.
banner
.
ImageVideoFragment
.
OnVideoCompletionListener
{
private
static
final
int
UPTATE_VIEWPAGER
=
0
;
private
ViewPager
mViewPager
;
private
List
<
BannerBean
>
mList
=
new
ArrayList
<>();
private
ViewsPagerAdapter
mAdapter
;
private
int
autoCurrIndex
=
0
;
//设置当前 第几个图片 被选中
private
Timer
timer
;
private
TimerTask
timerTask
;
private
long
period
;
//轮播图展示时长,默认5秒
public
static
ScheduledExecutorService
sExecutor
;
public
Context
mContext
;
public
ImageVideoBanner
(
@NonNull
Context
context
)
{
super
(
context
);
mContext
=
context
;
initView
(
context
);
}
public
ImageVideoBanner
(
@NonNull
Context
context
,
@Nullable
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
mContext
=
context
;
TypedArray
typedArray
=
context
.
obtainStyledAttributes
(
attrs
,
R
.
styleable
.
banner
);
period
=
typedArray
.
getInt
(
R
.
styleable
.
banner_period
,
5000
);
typedArray
.
recycle
();
initView
(
context
);
}
private
void
initView
(
Context
context
)
{
View
view
=
LayoutInflater
.
from
(
context
).
inflate
(
R
.
layout
.
banner_imge_video
,
this
,
true
);
mViewPager
=
view
.
findViewById
(
R
.
id
.
view_pager
);
mAdapter
=
new
ViewsPagerAdapter
(((
FragmentActivity
)
context
).
getSupportFragmentManager
());
mViewPager
.
setAdapter
(
mAdapter
);
mViewPager
.
setOnPageChangeListener
(
this
);
//ViewPager手势滑动禁用
mViewPager
.
setOnTouchListener
(
new
OnTouchListener
()
{
@Override
public
boolean
onTouch
(
View
v
,
MotionEvent
event
)
{
return
true
;
}
});
}
public
void
replaceData
(
List
<
com
.
widget
.
imagevideobanner
.
bean
.
BannerBean
>
listBean
)
{
mAdapter
.
replaceData
(
listBean
);
}
public
void
addData
(
List
<
com
.
widget
.
imagevideobanner
.
bean
.
BannerBean
>
listBean
)
{
mAdapter
.
addData
(
listBean
);
}
/**
* 初始化视频的Bitmap
*/
private
void
initBannerBitmap
()
{
if
(
mList
.
size
()
==
0
)
{
return
;
}
for
(
BannerBean
bannerBean
:
mList
)
{
if
(
bannerBean
.
getType
()
==
1
)
{
BitmapCache
.
getInstance
(
mContext
).
addCache
(
bannerBean
.
getUrl
());
}
}
}
@Override
public
void
onPageScrolled
(
int
position
,
float
positionOffset
,
int
positionOffsetPixels
)
{
}
@Override
public
void
onPageSelected
(
int
position
)
{
autoCurrIndex
=
position
;
if
(
mList
.
get
(
position
).
getType
()
==
1
)
{
//如果是视频
stopBanner
();
}
}
@Override
public
void
onPageScrollStateChanged
(
int
state
)
{
}
//定时轮播图片,需要在主线程里面修改 UI
private
Handler
mHandler
=
new
Handler
()
{
public
void
handleMessage
(
Message
msg
)
{
switch
(
msg
.
what
)
{
case
UPTATE_VIEWPAGER:
if
(
msg
.
arg1
!=
0
)
{
mViewPager
.
setCurrentItem
(
msg
.
arg1
);
}
else
{
mViewPager
.
setCurrentItem
(
msg
.
arg1
,
false
);
}
break
;
}
}
};
public
void
stopBanner
()
{
if
(
timer
!=
null
)
{
timer
.
cancel
();
timer
=
null
;
if
(
timerTask
!=
null
)
{
timerTask
.
cancel
();
timerTask
=
null
;
}
}
}
/**
* 设置轮播图 图片展示间隔
*/
public
void
setPeriod
(
int
period
)
{
this
.
period
=
period
;
}
public
void
startBanner
()
{
if
(
mList
.
get
(
0
).
getType
()
==
0
)
{
startBanner
(
period
);
}
}
public
void
startBanner
(
long
delay
)
{
stopBanner
();
timer
=
new
Timer
();
createTimerTask
();
timer
.
schedule
(
timerTask
,
delay
,
period
);
}
public
void
createTimerTask
()
{
timerTask
=
new
TimerTask
()
{
@Override
public
void
run
()
{
Message
message
=
new
Message
();
message
.
what
=
UPTATE_VIEWPAGER
;
if
(
autoCurrIndex
==
mList
.
size
()
-
1
)
{
autoCurrIndex
=
-
1
;
}
message
.
arg1
=
autoCurrIndex
+
1
;
mHandler
.
sendMessage
(
message
);
}
};
}
@Override
public
void
onVideoCompletion
(
MediaPlayer
mp
)
{
if
(
mList
.
size
()
==
1
)
{
mAdapter
.
getFragment
().
circulationPlayer
();
}
startBanner
(
0
);
}
@Override
public
void
onError
(
MediaPlayer
mp
)
{
startBanner
(
0
);
}
@Override
public
void
updateSize
(
BannerBean
bannerBean
)
{
if
(
mList
.
size
()
>
0
){
for
(
BannerBean
bean
:
mList
){
if
(
bannerBean
.
getUrl
().
equals
(
bean
.
getUrl
())){
bean
.
setHeight
(
bannerBean
.
getHeight
());
bean
.
setWidth
(
bannerBean
.
getWidth
());
}
}
}
}
private
final
class
ViewsPagerAdapter
extends
FragmentStatePagerAdapter
{
private
com
.
widget
.
imagevideobanner
.
banner
.
ImageVideoFragment
fragment
;
private
FragmentManager
fm
;
public
ViewsPagerAdapter
(
FragmentManager
fm
)
{
super
(
fm
);
this
.
fm
=
fm
;
}
@Override
public
Fragment
getItem
(
int
position
)
{
fragment
=
new
com
.
widget
.
imagevideobanner
.
banner
.
ImageVideoFragment
();
fragment
.
setOnVideoCompletionListener
(
ImageVideoBanner
.
this
);
com
.
widget
.
imagevideobanner
.
bean
.
BannerBean
bannerBean
=
mList
.
get
(
position
);
Bundle
bundle
=
new
Bundle
();
bundle
.
putSerializable
(
"bannerBean"
,
bannerBean
);
bundle
.
putBoolean
(
"loop"
,
getLoop
());
fragment
.
setArguments
(
bundle
);
return
fragment
;
}
/**
* 只有1个视频的时候循环播放
* @return
*/
private
boolean
getLoop
()
{
if
(
mList
.
size
()
==
1
&&
mList
.
get
(
0
).
getType
()
==
1
)
{
return
true
;
}
return
false
;
}
@Override
public
int
getCount
()
{
return
mList
.
size
();
}
@Override
public
int
getItemPosition
(
Object
object
)
{
if
(
mList
.
size
()
>
0
)
{
return
POSITION_NONE
;
}
return
super
.
getItemPosition
(
object
);
}
public
void
replaceData
(
List
<
BannerBean
>
listBean
)
{
// 新数据和原来数据对比,不一致才去刷新
boolean
change
=
compareData
(
listBean
);
if
(!
change
)
{
notifyDataSetChanged
();
return
;
}
if
(
listBean
.
size
()
>
0
)
{
mList
.
clear
();
addData
(
listBean
);
if
(
listBean
.
get
(
0
).
getType
()
==
0
)
{
startBanner
();
}
autoCurrIndex
=
0
;
}
}
/**
* 对比数据
* @param listBean
* @return
*/
private
boolean
compareData
(
List
<
BannerBean
>
listBean
)
{
if
(
listBean
==
null
||
listBean
.
size
()
==
0
)
{
return
false
;
}
if
(
listBean
.
size
()
==
mList
.
size
())
{
for
(
int
i
=
0
;
i
<
mList
.
size
();
i
++)
{
if
(
mList
.
get
(
i
).
getType
()
!=
listBean
.
get
(
i
).
getType
()
||
!
mList
.
get
(
i
).
getUrl
().
equals
(
listBean
.
get
(
i
).
getUrl
()))
{
return
true
;
}
}
return
false
;
}
return
true
;
}
public
void
addData
(
List
<
BannerBean
>
listBean
)
{
if
(
null
!=
listBean
)
{
mList
.
clear
();
mList
.
addAll
(
listBean
);
}
initBannerBitmap
();
notifyDataSetChanged
();
}
public
ImageVideoFragment
getFragment
()
{
return
fragment
;
}
}
public
List
<
BannerBean
>
getBannerInfos
(){
return
mList
;
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/banner/ImageVideoFragment.java
deleted
100644 → 0
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
banner
;
import
android.graphics.Bitmap
;
import
android.graphics.Color
;
import
android.graphics.drawable.Drawable
;
import
android.media.MediaPlayer
;
import
android.net.Uri
;
import
android.os.Bundle
;
import
android.os.Handler
;
import
android.os.Message
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.constraint.ConstraintLayout
;
import
android.support.v4.app.Fragment
;
import
android.util.Log
;
import
android.util.Pair
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.ImageView
;
import
android.widget.LinearLayout
;
import
com.bumptech.glide.Glide
;
import
com.bumptech.glide.load.resource.drawable.GlideDrawable
;
import
com.bumptech.glide.request.RequestListener
;
import
com.bumptech.glide.request.target.Target
;
import
com.widget.imagevideobanner.R
;
import
com.widget.imagevideobanner.bean.BannerBean
;
import
com.widget.imagevideobanner.cache.BitmapCache
;
import
com.widget.imagevideobanner.log.BannerLogFileUtils
;
import
com.widget.imagevideobanner.utils.BitmapUtils
;
import
com.widget.imagevideobanner.utils.ThrowableUtils
;
import
java.lang.ref.WeakReference
;
import
java.math.BigDecimal
;
public
class
ImageVideoFragment
extends
Fragment
{
private
static
final
String
TAG
=
ImageVideoFragment
.
class
.
getSimpleName
();
private
OnVideoCompletionListener
listener
;
private
MyVideoView
mVideoView
;
private
BannerBean
bannerBean
;
private
boolean
loop
;
private
ImageView
ivWaitLoading
;
private
ConstraintLayout
cVideoView
;
private
LinearLayout
llWaitLoading
;
private
int
currentPosition
;
private
boolean
playerPaused
;
private
String
mUrl
;
private
static
final
int
STOP_PLAYER
=
0x2000
;
private
static
final
int
START_PLAYER
=
0x2001
;
private
static
final
int
PAUSE_PLAYER
=
0x2002
;
private
static
final
int
SET_VIDEO_URL
=
0x2003
;
private
static
final
int
DEFAULT_WIDTH
=
983
;
private
static
final
int
DEFAULT_HEIGHT
=
1080
;
/**
* 使用Handler是为了避免出现ANR异常
*/
private
Handler
handler
;
@Override
public
void
onCreate
(
@Nullable
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
);
Bundle
bundle
=
getArguments
();
bannerBean
=
(
BannerBean
)
bundle
.
getSerializable
(
"bannerBean"
);
loop
=
bundle
.
getBoolean
(
"loop"
);
handler
=
new
InnerHandler
(
this
);
}
@Nullable
@Override
public
View
onCreateView
(
@NonNull
LayoutInflater
inflater
,
@Nullable
ViewGroup
container
,
@Nullable
Bundle
savedInstanceState
)
{
View
view
;
if
(
bannerBean
!=
null
)
{
int
type
=
bannerBean
.
getType
();
if
(
type
==
0
)
{
view
=
LayoutInflater
.
from
(
getActivity
()).
inflate
(
R
.
layout
.
item_image_view
,
container
,
false
);
ImageView
imageView
=
view
.
findViewById
(
R
.
id
.
iv
);
Glide
.
with
(
container
.
getContext
()).
load
(
bannerBean
.
getUrl
()).
listener
(
new
RequestListener
<
String
,
GlideDrawable
>()
{
@Override
public
boolean
onException
(
Exception
e
,
String
model
,
Target
<
GlideDrawable
>
target
,
boolean
isFirstResource
)
{
BannerLogFileUtils
.
writeLog
(
"图片加载出错 url ="
+
bannerBean
.
getUrl
()
+
"出错原因是:"
+
ThrowableUtils
.
getFullStackTrace
(
e
));
return
false
;
}
@Override
public
boolean
onResourceReady
(
GlideDrawable
resource
,
String
model
,
Target
<
GlideDrawable
>
target
,
boolean
isFromMemoryCache
,
boolean
isFirstResource
)
{
return
false
;
}
}).
error
(
R
.
drawable
.
load_fail
).
into
(
imageView
);
}
else
{
BannerLogFileUtils
.
writeLog
(
"开始加载视频 url ="
+
bannerBean
.
getUrl
());
view
=
LayoutInflater
.
from
(
getActivity
()).
inflate
(
R
.
layout
.
item_video_view
,
container
,
false
);
mVideoView
=
view
.
findViewById
(
R
.
id
.
video_view
);
ivWaitLoading
=
view
.
findViewById
(
R
.
id
.
iv_wait_loading_layout
);
llWaitLoading
=
view
.
findViewById
(
R
.
id
.
wait_loading_layout
);
cVideoView
=
view
.
findViewById
(
R
.
id
.
cVideoView
);
setVideoBackground
();
setPicture
(
bannerBean
.
getWidth
(),
bannerBean
.
getHeight
());
initData
();
}
}
else
{
view
=
LayoutInflater
.
from
(
getActivity
()).
inflate
(
R
.
layout
.
item_image_view
,
container
,
false
);
}
return
view
;
}
public
void
setVideoBackground
(){
Bitmap
bitmap
=
BitmapCache
.
getInstance
(
getContext
()).
getBlurBitmap
(
bannerBean
.
getUrl
());
if
(
bitmap
==
null
)
{
cVideoView
.
setBackgroundResource
(
R
.
drawable
.
guide_defaut
);
}
else
{
cVideoView
.
setBackground
(
BitmapUtils
.
bitmap2Drawable
(
bitmap
));
}
}
/**
* 设置加载视频的等待图片
*/
public
void
setPicture
(
int
width
,
int
height
)
{
if
(
width
==
0
||
height
==
0
){
return
;
}
//设置设置尺寸
ConstraintLayout
.
LayoutParams
lp2
=
(
ConstraintLayout
.
LayoutParams
)
ivWaitLoading
.
getLayoutParams
();
lp2
.
height
=
height
;
lp2
.
width
=
width
;
ivWaitLoading
.
setLayoutParams
(
lp2
);
//视频未加载完成,使用菊花
Bitmap
bitmap
=
BitmapCache
.
getInstance
(
getContext
()).
getBitmap
(
bannerBean
.
getUrl
());
if
(
bitmap
==
null
)
{
if
(
bannerBean
.
getUrl
().
contains
(
"android.resource"
))
{
ivWaitLoading
.
setVisibility
(
View
.
VISIBLE
);
ivWaitLoading
.
setImageResource
(
R
.
drawable
.
guide_defaut
);
llWaitLoading
.
setVisibility
(
View
.
GONE
);
}
else
{
ivWaitLoading
.
setVisibility
(
View
.
GONE
);
llWaitLoading
.
setVisibility
(
View
.
VISIBLE
);
}
}
else
{
//加载完成使用首帧图片
ivWaitLoading
.
setVisibility
(
View
.
VISIBLE
);
ivWaitLoading
.
setImageBitmap
(
bitmap
);
llWaitLoading
.
setVisibility
(
View
.
GONE
);
}
}
private
void
initData
()
{
if
(
null
!=
mVideoView
)
{
mVideoView
.
setAlpha
(
0
);
mVideoView
.
requestFocus
();
mVideoView
.
setVideoURI
(
Uri
.
parse
(
bannerBean
.
getUrl
()));
mVideoView
.
setOnCompletionListener
(
new
MediaPlayer
.
OnCompletionListener
()
{
@Override
public
void
onCompletion
(
MediaPlayer
mp
)
{
mVideoView
.
pause
();
if
(
null
!=
listener
)
{
listener
.
onVideoCompletion
(
mp
);
}
}
});
mVideoView
.
setOnPreparedListener
(
new
MediaPlayer
.
OnPreparedListener
()
{
@Override
public
void
onPrepared
(
MediaPlayer
mp
)
{
resizeVideoView
(
mp
.
getVideoWidth
(),
mp
.
getVideoHeight
());
mp
.
setLooping
(
loop
);
mp
.
setOnInfoListener
(
new
MediaPlayer
.
OnInfoListener
()
{
@Override
public
boolean
onInfo
(
MediaPlayer
mp
,
int
what
,
int
extra
)
{
if
(
what
==
MediaPlayer
.
MEDIA_INFO_VIDEO_RENDERING_START
)
{
mVideoView
.
setAlpha
(
1
);
llWaitLoading
.
setVisibility
(
View
.
GONE
);
ivWaitLoading
.
setVisibility
(
View
.
GONE
);
mVideoView
.
setBackgroundColor
(
Color
.
TRANSPARENT
);
return
true
;
}
return
false
;
}
});
}
});
mVideoView
.
setOnErrorListener
(
new
MediaPlayer
.
OnErrorListener
()
{
@Override
public
boolean
onError
(
MediaPlayer
mp
,
int
what
,
int
extra
)
{
BannerLogFileUtils
.
writeLog
(
"视频播放出错了-what="
+
what
+
",extra="
+
extra
);
Log
.
e
(
TAG
,
"视频播放出错了-what="
+
what
+
",extra="
+
extra
);
mVideoView
.
stopPlayback
();
if
(
null
!=
listener
)
{
listener
.
onError
(
mp
);
}
if
(
what
==
MediaPlayer
.
MEDIA_ERROR_SERVER_DIED
)
{
//媒体服务器挂掉了。此时,程序必须释放MediaPlayer 对象,并重新new 一个新的。
Log
.
e
(
TAG
,
"媒体服务器挂掉了"
);
}
else
if
(
what
==
MediaPlayer
.
MEDIA_ERROR_UNKNOWN
)
{
if
(
extra
==
MediaPlayer
.
MEDIA_ERROR_IO
)
{
//文件不存在或错误,或网络不可访问错误
Log
.
e
(
TAG
,
"文件不存在或错误,或网络不可访问错误"
);
}
else
if
(
extra
==
MediaPlayer
.
MEDIA_ERROR_TIMED_OUT
)
{
//超时
Log
.
e
(
TAG
,
"超时"
);
}
}
return
true
;
}
});
sendSetVideoUrlMsg
();
}
}
public
void
resizeVideoView
(
int
width
,
int
height
)
{
Pair
<
Integer
,
Integer
>
pair
=
calacScaleSize
(
width
,
height
);
width
=
pair
.
first
;
height
=
pair
.
second
;
ConstraintLayout
.
LayoutParams
lp
=
(
ConstraintLayout
.
LayoutParams
)
mVideoView
.
getLayoutParams
();
lp
.
height
=
height
;
lp
.
width
=
width
;
mVideoView
.
setLayoutParams
(
lp
);
mVideoView
.
setVideoSize
(
width
,
height
);
if
(
bannerBean
.
getWidth
()
==
0
||
bannerBean
.
getHeight
()
==
0
){
bannerBean
.
setWidth
(
width
);
bannerBean
.
setHeight
(
height
);
listener
.
updateSize
(
bannerBean
);
}
}
private
Pair
<
Integer
,
Integer
>
calacScaleSize
(
int
width
,
int
height
){
if
(
bannerBean
.
getUrl
().
contains
(
"android.resource"
))
{
width
=
DEFAULT_WIDTH
;
height
=
DEFAULT_HEIGHT
;
}
int
mHeight
=
mVideoView
.
getHeight
();
int
mWidth
=
mVideoView
.
getWidth
();
BigDecimal
viewScale
=
new
BigDecimal
(
mWidth
+
""
)
.
divide
(
new
BigDecimal
(
mHeight
+
""
),
2
,
BigDecimal
.
ROUND_HALF_UP
);
BigDecimal
videoScale
=
new
BigDecimal
(
width
+
""
)
.
divide
(
new
BigDecimal
(
height
+
""
),
2
,
BigDecimal
.
ROUND_HALF_UP
);
if
(
viewScale
.
compareTo
(
videoScale
)
>=
0
){
//按照高度缩放
BigDecimal
heightScale
=
new
BigDecimal
(
mHeight
+
""
)
.
divide
(
new
BigDecimal
(
height
+
""
),
2
,
BigDecimal
.
ROUND_HALF_UP
);
height
=
mHeight
;
width
=
new
BigDecimal
(
width
+
""
).
multiply
(
heightScale
).
intValue
();
}
else
{
//按照宽度缩放
BigDecimal
widthScale
=
new
BigDecimal
(
mWidth
+
""
)
.
divide
(
new
BigDecimal
(
width
+
""
),
2
,
BigDecimal
.
ROUND_HALF_UP
);
width
=
mWidth
;
height
=
new
BigDecimal
(
height
+
""
).
multiply
(
widthScale
).
intValue
();
}
BannerLogFileUtils
.
writeLog
(
"重新计算屏幕宽高 width="
+
width
+
",height="
+
height
);
return
new
Pair
<>(
width
,
height
);
}
private
void
setVideoUrl
()
{
String
url
=
bannerBean
.
getUrl
();
mUrl
=
url
;
mVideoView
.
setVideoURI
(
Uri
.
parse
(
url
));
}
public
void
startPlayer
()
{
if
(
null
!=
mVideoView
)
{
mVideoView
.
setBackgroundColor
(
Color
.
TRANSPARENT
);
mVideoView
.
seekTo
(
currentPosition
);
mVideoView
.
start
();
}
}
public
void
circulationPlayer
()
{
sendStartVideoMsg
(
true
);
}
private
void
stopPlayer
()
{
if
(
null
!=
mVideoView
)
{
mVideoView
.
stopPlayback
();
//内存泄漏fix
mVideoView
.
setOnCompletionListener
(
null
);
mVideoView
.
setOnErrorListener
(
null
);
mVideoView
.
setOnPreparedListener
(
null
);
mVideoView
.
setOnInfoListener
(
null
);
handler
.
removeCallbacksAndMessages
(
null
);
}
}
private
void
pausePlayer
()
{
if
(
null
!=
mVideoView
)
{
mVideoView
.
setBackgroundColor
(
getResources
().
getColor
(
R
.
color
.
white
));
mVideoView
.
setAlpha
(
0
);
playerPaused
=
true
;
this
.
currentPosition
=
mVideoView
.
getCurrentPosition
();
mVideoView
.
pause
();
}
}
private
void
sendStartVideoMsg
()
{
sendStartVideoMsg
(
false
);
}
private
void
sendStartVideoMsg
(
boolean
isHasUrl
)
{
removeMessages
();
if
(!
handler
.
hasMessages
(
START_PLAYER
))
{
if
(
null
!=
mVideoView
)
{
if
(
isHasUrl
)
{
try
{
mVideoView
.
setVideoURI
(
Uri
.
parse
(
mUrl
));
}
catch
(
Exception
e
)
{
e
.
printStackTrace
();
}
}
handler
.
sendEmptyMessage
(
START_PLAYER
);
}
}
}
private
void
sendStopVideoMsg
()
{
removeMessages
();
if
(!
handler
.
hasMessages
(
STOP_PLAYER
))
{
if
(
null
!=
mVideoView
)
{
handler
.
sendEmptyMessage
(
STOP_PLAYER
);
}
}
}
private
void
sendPauseVideoMsg
()
{
removeMessages
();
if
(!
handler
.
hasMessages
(
PAUSE_PLAYER
))
{
if
(
null
!=
mVideoView
)
{
handler
.
sendEmptyMessage
(
PAUSE_PLAYER
);
}
}
}
private
void
sendSetVideoUrlMsg
()
{
removeMessages
();
if
(!
handler
.
hasMessages
(
SET_VIDEO_URL
))
{
if
(
null
!=
mVideoView
)
{
handler
.
sendEmptyMessage
(
SET_VIDEO_URL
);
}
}
}
private
void
removeMessages
()
{
if
(
handler
.
hasMessages
(
START_PLAYER
))
{
handler
.
removeMessages
(
START_PLAYER
);
}
if
(
handler
.
hasMessages
(
STOP_PLAYER
))
{
handler
.
removeMessages
(
STOP_PLAYER
);
}
if
(
handler
.
hasMessages
(
PAUSE_PLAYER
))
{
handler
.
removeMessages
(
PAUSE_PLAYER
);
}
if
(
handler
.
hasMessages
(
SET_VIDEO_URL
))
{
handler
.
removeMessages
(
SET_VIDEO_URL
);
}
}
@Override
public
void
onResume
()
{
super
.
onResume
();
if
(
playerPaused
)
{
sendStartVideoMsg
();
}
}
@Override
public
void
onPause
()
{
super
.
onPause
();
sendPauseVideoMsg
();
}
@Override
public
void
onDestroyView
()
{
//2021/2/24 记录:这里要判断LoadingImage的存在,否则会回收本地Resource,造成加载错误.本地id标识的图片也会处理为BitmapDrawable
if
(
ivWaitLoading
!=
null
)
{
Drawable
drawable
=
ivWaitLoading
.
getDrawable
();
ivWaitLoading
.
setImageDrawable
(
null
);
//todo bugfixed
// if (drawable instanceof BitmapDrawable) {
// Bitmap bm = ((BitmapDrawable) drawable).getBitmap();
// if (!bm.isRecycled()) {
// bm.recycle();
// }
// }
}
super
.
onDestroyView
();
}
@Override
public
void
onDestroy
()
{
super
.
onDestroy
();
sendStopVideoMsg
();
}
public
interface
OnVideoCompletionListener
{
void
onVideoCompletion
(
MediaPlayer
mp
);
void
onError
(
MediaPlayer
mp
);
void
updateSize
(
BannerBean
bannerBean
);
}
public
void
setOnVideoCompletionListener
(
OnVideoCompletionListener
listener
)
{
this
.
listener
=
listener
;
}
private
static
class
InnerHandler
extends
Handler
{
private
final
WeakReference
<
Fragment
>
mFragment
;
public
InnerHandler
(
Fragment
fragment
){
mFragment
=
new
WeakReference
<
Fragment
>(
fragment
);
}
@Override
public
void
handleMessage
(
Message
msg
)
{
ImageVideoFragment
fragment
=(
ImageVideoFragment
)
mFragment
.
get
();
super
.
handleMessage
(
msg
);
if
(
fragment
!=
null
){
switch
(
msg
.
what
)
{
case
STOP_PLAYER:
fragment
.
stopPlayer
();
break
;
case
START_PLAYER:
fragment
.
startPlayer
();
break
;
case
PAUSE_PLAYER:
fragment
.
pausePlayer
();
break
;
case
SET_VIDEO_URL:
fragment
.
setVideoUrl
();
fragment
.
startPlayer
();
break
;
}
}
}
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/banner/MyVideoView.java
deleted
100644 → 0
View file @
d75937a0
package
com
.
widget
.
imagevideobanner
.
banner
;
import
android.content.Context
;
import
android.util.AttributeSet
;
import
android.widget.VideoView
;
public
class
MyVideoView
extends
VideoView
{
private
int
mVideoWidth
;
private
int
mVideoHeight
;
public
MyVideoView
(
Context
context
,
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
}
public
MyVideoView
(
Context
context
,
AttributeSet
attrs
,
int
defStyle
)
{
super
(
context
,
attrs
,
defStyle
);
}
public
MyVideoView
(
Context
context
)
{
super
(
context
);
}
public
void
setVideoSize
(
int
width
,
int
height
)
{
mVideoWidth
=
width
;
mVideoHeight
=
height
;
}
@Override
protected
void
onMeasure
(
int
widthMeasureSpec
,
int
heightMeasureSpec
)
{
// Log.i("@@@", "onMeasure");
int
width
=
getDefaultSize
(
mVideoWidth
,
widthMeasureSpec
);
int
height
=
getDefaultSize
(
mVideoHeight
,
heightMeasureSpec
);
if
(
mVideoWidth
>
0
&&
mVideoHeight
>
0
)
{
if
(
mVideoWidth
*
height
>
width
*
mVideoHeight
)
{
// Log.i("@@@", "image too tall, correcting");
height
=
width
*
mVideoHeight
/
mVideoWidth
;
}
else
if
(
mVideoWidth
*
height
<
width
*
mVideoHeight
)
{
// Log.i("@@@", "image too wide, correcting");
width
=
height
*
mVideoWidth
/
mVideoHeight
;
}
else
{
// Log.i("@@@", "aspect ratio is correct: " +
// width+"/"+height+"="+
// mVideoWidth+"/"+mVideoHeight);
}
}
// Log.i("@@@", "setting size: " + width + 'x' + height);
setMeasuredDimension
(
width
,
height
);
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/bean/
Banner
Bean.java
→
imageVideoBanner/src/main/java/com/widget/imagevideobanner/bean/
Media
Bean.java
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
.
bean
;
import
android.support.annotation.Nullable
;
import
android.text.TextUtils
;
import
java.io.Serializable
;
public
class
BannerBean
implements
Serializable
{
/**
* 资源类型
*/
public
class
MediaBean
implements
Serializable
{
public
static
final
int
TYPE_IMAGE
=
0
;
//图片
public
static
final
int
TYPE_VIDEO
=
1
;
//视频
private
String
url
;
private
int
type
;
//资源类型
private
int
resType
;
//视频的封面
private
String
cover
;
private
int
width
;
private
int
height
;
//视频首帧图片key
private
String
key
;
//视频首帧图片 高斯模糊key
private
String
blurKey
;
public
String
getKey
()
{
return
key
;
public
String
getUrl
()
{
return
url
;
}
public
void
setUrl
(
String
url
)
{
this
.
url
=
url
;
}
public
int
getResType
()
{
return
resType
;
}
public
void
set
Key
(
String
key
)
{
this
.
key
=
key
;
public
void
set
ResType
(
int
resType
)
{
this
.
resType
=
resType
;
}
public
String
get
BlurKey
()
{
return
blurKey
;
public
String
get
Cover
()
{
return
cover
;
}
public
void
set
BlurKey
(
String
blurKey
)
{
this
.
blurKey
=
blurKey
;
public
void
set
Cover
(
String
cover
)
{
this
.
cover
=
cover
;
}
public
int
getWidth
()
{
...
...
@@ -44,28 +62,26 @@ public class BannerBean implements Serializable {
this
.
height
=
height
;
}
public
String
getUrl
()
{
return
url
;
}
public
void
setUrl
(
String
url
)
{
this
.
url
=
url
;
@Override
public
boolean
equals
(
@Nullable
Object
obj
)
{
if
(!(
obj
instanceof
MediaBean
))
return
false
;
MediaBean
mediaBean
=
(
MediaBean
)
obj
;
return
resType
==
mediaBean
.
resType
&&
width
==
mediaBean
.
width
&&
height
==
mediaBean
.
height
&&
TextUtils
.
equals
(
url
,
mediaBean
.
url
)
&&
TextUtils
.
equals
(
cover
,
mediaBean
.
cover
);
}
public
int
getType
()
{
return
type
;
public
boolean
isNull
()
{
if
(
resType
==
TYPE_IMAGE
)
{
return
TextUtils
.
isEmpty
(
url
)
;
}
public
void
setType
(
int
type
)
{
this
.
type
=
type
;
return
(
width
==
0
)
||
(
height
==
0
)
||
TextUtils
.
isEmpty
(
cover
)
||
TextUtils
.
isEmpty
(
url
)
;
}
@Override
public
String
toString
()
{
return
"BannerBean{"
+
"url='"
+
url
+
'\''
+
", type="
+
type
+
'}'
;
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/player/IPlayTarget.java
0 → 100755
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
.
player
;
public
interface
IPlayTarget
{
//活跃状态 视频可播放
void
onActive
();
//非活跃状态,暂停它
void
inActive
();
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/player/PlayerManager.java
0 → 100755
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
.
player
;
import
android.app.Application
;
import
android.net.Uri
;
import
android.view.LayoutInflater
;
import
com.google.android.exoplayer2.DefaultLoadControl
;
import
com.google.android.exoplayer2.ExoPlayerFactory
;
import
com.google.android.exoplayer2.SimpleExoPlayer
;
import
com.google.android.exoplayer2.source.ExtractorMediaSource
;
import
com.google.android.exoplayer2.source.MediaSource
;
import
com.google.android.exoplayer2.trackselection.DefaultTrackSelector
;
import
com.google.android.exoplayer2.ui.PlayerView
;
import
com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory
;
import
com.google.android.exoplayer2.upstream.FileDataSourceFactory
;
import
com.google.android.exoplayer2.upstream.cache.Cache
;
import
com.google.android.exoplayer2.upstream.cache.CacheDataSinkFactory
;
import
com.google.android.exoplayer2.upstream.cache.CacheDataSource
;
import
com.google.android.exoplayer2.upstream.cache.CacheDataSourceFactory
;
import
com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor
;
import
com.google.android.exoplayer2.upstream.cache.SimpleCache
;
import
com.google.android.exoplayer2.util.Util
;
import
com.widget.imagevideobanner.BaseApplication
;
import
com.widget.imagevideobanner.R
;
public
class
PlayerManager
{
private
final
ExtractorMediaSource
.
Factory
mediaSourceFactory
;
public
SimpleExoPlayer
exoPlayer
;
public
PlayerView
playerView
;
public
String
playUrl
;
private
static
PlayerManager
mInstance
;
public
PlayerManager
()
{
Application
application
=
BaseApplication
.
getApplication
();
//创建http视频资源如何加载的工厂对象
DefaultHttpDataSourceFactory
dataSourceFactory
=
new
DefaultHttpDataSourceFactory
(
Util
.
getUserAgent
(
application
,
application
.
getPackageName
()));
//创建缓存,指定缓存位置,和缓存策略,为最近最少使用原则,最大为200m
Cache
cache
=
new
SimpleCache
(
application
.
getCacheDir
(),
new
LeastRecentlyUsedCacheEvictor
(
1024
*
1024
*
200
));
//把缓存对象cache和负责缓存数据读取、写入的工厂类CacheDataSinkFactory 相关联
CacheDataSinkFactory
cacheDataSinkFactory
=
new
CacheDataSinkFactory
(
cache
,
Long
.
MAX_VALUE
);
/**创建能够 边播放边缓存的 本地资源加载和http网络数据写入的工厂类
* public CacheDataSourceFactory(
* Cache cache, 缓存写入策略和缓存写入位置的对象
* DataSource.Factory upstreamFactory,http视频资源如何加载的工厂对象
* DataSource.Factory cacheReadDataSourceFactory,本地缓存数据如何读取的工厂对象
* @Nullable DataSink.Factory cacheWriteDataSinkFactory,http网络数据如何写入本地缓存的工厂对象
* @CacheDataSource.Flags int flags,加载本地缓存数据进行播放时的策略,如果遇到该文件正在被写入数据,或读取缓存数据发生错误时的策略
* @Nullable CacheDataSource.EventListener eventListener 缓存数据读取的回调
*/
CacheDataSourceFactory
cacheDataSourceFactory
=
new
CacheDataSourceFactory
(
cache
,
dataSourceFactory
,
new
FileDataSourceFactory
(),
cacheDataSinkFactory
,
CacheDataSource
.
FLAG_BLOCK_ON_CACHE
,
null
);
//最后 还需要创建一个 MediaSource 媒体资源 加载的工厂类
//因为由它创建的MediaSource 能够实现边缓冲边播放的效果,
mediaSourceFactory
=
new
ExtractorMediaSource
.
Factory
(
cacheDataSourceFactory
);
//创建exoplayer播放器实例
exoPlayer
=
ExoPlayerFactory
.
newSimpleInstance
(
application
,
//视频的音视频轨道如何加载,使用默认的轨道选择器
new
DefaultTrackSelector
(),
//视频缓存控制逻辑,使用默认的即可
new
DefaultLoadControl
());
//加载咱们布局层级优化之后的能够展示视频画面的View
playerView
=
(
PlayerView
)
LayoutInflater
.
from
(
application
).
inflate
(
R
.
layout
.
layout_exo_player_view
,
null
,
false
);
//别忘记 把播放器实例 和 playerView,controlView相关联
//如此视频画面才能正常显示,播放进度条才能自动更新
playerView
.
setPlayer
(
exoPlayer
);
}
public
static
PlayerManager
getInstance
(){
if
(
mInstance
==
null
)
{
synchronized
(
PlayerManager
.
class
)
{
if
(
mInstance
==
null
)
{
mInstance
=
new
PlayerManager
();
}
}
}
return
mInstance
;
}
public
void
release
()
{
if
(
exoPlayer
!=
null
)
{
exoPlayer
.
setPlayWhenReady
(
false
);
exoPlayer
.
stop
(
true
);
exoPlayer
.
release
();
exoPlayer
=
null
;
}
if
(
playerView
!=
null
)
{
playerView
.
setPlayer
(
null
);
playerView
=
null
;
}
}
/**
* 切换与播放器exoplayer 绑定的exoplayerView。用于页面切换视频无缝续播的场景
* @param newPlayerView
* @param attach
*/
public
void
switchPlayerView
(
PlayerView
newPlayerView
,
boolean
attach
)
{
playerView
.
setPlayer
(
attach
?
null
:
exoPlayer
);
newPlayerView
.
setPlayer
(
attach
?
exoPlayer
:
null
);
}
public
MediaSource
createMediaSource
(
String
url
)
{
return
mediaSourceFactory
.
createMediaSource
(
Uri
.
parse
(
url
));
}
}
\ No newline at end of file
imageVideoBanner/src/main/java/com/widget/imagevideobanner/view/ImageVideoBanner.java
0 → 100644
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
.
view
;
import
android.content.Context
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.v4.view.PagerAdapter
;
import
android.util.AttributeSet
;
import
android.util.Log
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.widget.FrameLayout
;
import
com.widget.imagevideobanner.R
;
import
com.widget.imagevideobanner.bean.MediaBean
;
import
java.util.ArrayList
;
import
java.util.List
;
import
static
com
.
widget
.
imagevideobanner
.
bean
.
MediaBean
.
TYPE_IMAGE
;
import
static
com
.
widget
.
imagevideobanner
.
bean
.
MediaBean
.
TYPE_VIDEO
;
/**
* 图片+视频
* 混播控件
*/
public
class
ImageVideoBanner
extends
FrameLayout
implements
ViewPager
.
OnPageChangeListener
,
OnActionFinishListener
{
private
static
String
TAG
=
"ImageVideoBanner"
;
private
ViewPager
mViewPager
;
private
List
<
View
>
mList
=
new
ArrayList
<>();
private
ViewsPagerAdapter
mAdapter
;
private
int
curIndex
;
private
ImageVideoBanner
(
@NonNull
Context
context
)
{
super
(
context
);
initView
();
}
public
ImageVideoBanner
(
@NonNull
Context
context
,
@Nullable
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
initView
();
}
private
void
initView
(){
View
view
=
LayoutInflater
.
from
(
getContext
()).
inflate
(
R
.
layout
.
banner_imge_video
,
this
,
true
);
mViewPager
=
view
.
findViewById
(
R
.
id
.
view_pager
);
mAdapter
=
new
ViewsPagerAdapter
(
mList
);
mViewPager
.
setAdapter
(
mAdapter
);
mViewPager
.
setOnPageChangeListener
(
this
);
}
public
void
bindData
(
List
<
MediaBean
>
beans
){
if
(
beans
==
null
){
return
;
}
mList
.
clear
();
for
(
MediaBean
mediaBean
:
beans
){
if
(!
mediaBean
.
isNull
()){
if
(
mediaBean
.
getResType
()
==
TYPE_IMAGE
){
mList
.
add
(
new
VpImageView
(
getContext
(),
mediaBean
,
this
));
}
if
(
mediaBean
.
getResType
()
==
TYPE_VIDEO
){
mList
.
add
(
new
VpVideoView
(
getContext
(),
mediaBean
,
this
));
}
}
}
if
(
mList
.
size
()
>
0
){
mAdapter
.
notifyDataSetChanged
();
}
}
@Override
public
void
onPageScrolled
(
int
position
,
float
positionOffset
,
int
positionOffsetPixels
)
{}
@Override
public
void
onPageSelected
(
int
position
)
{
curIndex
=
position
;
}
@Override
public
void
next
()
{
Log
.
e
(
TAG
,
curIndex
+
""
);
if
(
curIndex
==
mList
.
size
()
-
1
)
{
curIndex
=
0
;
}
else
{
curIndex
=
curIndex
+
1
;
}
mViewPager
.
setCurrentItem
(
curIndex
);
}
@Override
public
void
onPageScrollStateChanged
(
int
state
)
{
}
private
static
final
class
ViewsPagerAdapter
extends
PagerAdapter
{
private
List
<
View
>
mList
;
public
ViewsPagerAdapter
(
List
<
View
>
list
)
{
this
.
mList
=
list
;
}
@Override
public
int
getCount
()
{
return
mList
.
size
();
}
@Override
public
boolean
isViewFromObject
(
@NonNull
View
view
,
@NonNull
Object
object
)
{
return
view
==
object
;
}
@NonNull
@Override
public
View
instantiateItem
(
ViewGroup
container
,
int
position
)
{
container
.
addView
(
mList
.
get
(
position
));
return
mList
.
get
(
position
);
}
@Override
public
void
destroyItem
(
ViewGroup
container
,
int
position
,
@NonNull
Object
object
)
{
container
.
removeView
(
mList
.
get
(
position
));
}
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/
banner
/KeyEventCompat.java
→
imageVideoBanner/src/main/java/com/widget/imagevideobanner/
view
/KeyEventCompat.java
View file @
0ef30b50
...
...
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package
com
.
widget
.
imagevideobanner
.
banner
;
package
com
.
widget
.
imagevideobanner
.
view
;
import
android.view.KeyEvent
;
...
...
@@ -95,17 +95,17 @@ public class KeyEventCompat {
static
class
HoneycombKeyEventVersionImpl
implements
KeyEventVersionImpl
{
@Override
public
int
normalizeMetaState
(
int
metaState
)
{
return
com
.
widget
.
imagevideobanner
.
banner
.
KeyEventCompatHoneycomb
.
normalizeMetaState
(
metaState
);
return
com
.
widget
.
imagevideobanner
.
view
.
KeyEventCompatHoneycomb
.
normalizeMetaState
(
metaState
);
}
@Override
public
boolean
metaStateHasModifiers
(
int
metaState
,
int
modifiers
)
{
return
com
.
widget
.
imagevideobanner
.
banner
.
KeyEventCompatHoneycomb
.
metaStateHasModifiers
(
metaState
,
modifiers
);
return
com
.
widget
.
imagevideobanner
.
view
.
KeyEventCompatHoneycomb
.
metaStateHasModifiers
(
metaState
,
modifiers
);
}
@Override
public
boolean
metaStateHasNoModifiers
(
int
metaState
)
{
return
com
.
widget
.
imagevideobanner
.
banner
.
KeyEventCompatHoneycomb
.
metaStateHasNoModifiers
(
metaState
);
return
com
.
widget
.
imagevideobanner
.
view
.
KeyEventCompatHoneycomb
.
metaStateHasNoModifiers
(
metaState
);
}
}
...
...
imageVideoBanner/src/main/java/com/widget/imagevideobanner/
banner
/KeyEventCompatHoneycomb.java
→
imageVideoBanner/src/main/java/com/widget/imagevideobanner/
view
/KeyEventCompatHoneycomb.java
View file @
0ef30b50
...
...
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package
com
.
widget
.
imagevideobanner
.
banner
;
package
com
.
widget
.
imagevideobanner
.
view
;
import
android.view.KeyEvent
;
...
...
imageVideoBanner/src/main/java/com/widget/imagevideobanner/view/OnActionFinishListener.java
0 → 100644
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
.
view
;
public
interface
OnActionFinishListener
{
void
next
();
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/
banner
/ViewPager.java
→
imageVideoBanner/src/main/java/com/widget/imagevideobanner/
view
/ViewPager.java
View file @
0ef30b50
...
...
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package
com
.
widget
.
imagevideobanner
.
banner
;
package
com
.
widget
.
imagevideobanner
.
view
;
import
android.content.Context
;
import
android.content.res.TypedArray
;
...
...
@@ -224,6 +224,7 @@ public class ViewPager extends ViewGroup {
*/
public
void
onPageSelected
(
int
position
);
/**
* Called when the scroll state changes. Useful for discovering when the user
* begins dragging, when the pager is automatically settling to the current page,
...
...
@@ -235,6 +236,8 @@ public class ViewPager extends ViewGroup {
* @see ViewPager#SCROLL_STATE_SETTLING
*/
public
void
onPageScrollStateChanged
(
int
state
);
}
/**
...
...
@@ -1422,144 +1425,144 @@ public class ViewPager extends ViewGroup {
@Override
public
boolean
onTouchEvent
(
MotionEvent
ev
)
{
if
(
mFakeDragging
)
{
// A fake drag is in progress already, ignore this real one
// but still eat the touch events.
// (It is likely that the user is multi-touching the screen.)
return
true
;
}
if
(
ev
.
getAction
()
==
MotionEvent
.
ACTION_DOWN
&&
ev
.
getEdgeFlags
()
!=
0
)
{
// Don't handle edge touches immediately -- they may actually belong to one of our
// descendants.
return
false
;
}
if
(
mAdapter
==
null
||
mAdapter
.
getCount
()
==
0
)
{
// Nothing to present or scroll; nothing to touch.
return
false
;
}
if
(
mVelocityTracker
==
null
)
{
mVelocityTracker
=
VelocityTracker
.
obtain
();
}
mVelocityTracker
.
addMovement
(
ev
);
final
int
action
=
ev
.
getAction
();
boolean
needsInvalidate
=
false
;
switch
(
action
&
MotionEventCompat
.
ACTION_MASK
)
{
case
MotionEvent
.
ACTION_DOWN
:
{
/*
* If being flinged and user touches, stop the fling. isFinished
* will be false if being flinged.
*/
completeScroll
();
// Remember where the motion event started
mLastMotionX
=
mInitialMotionX
=
ev
.
getX
();
mActivePointerId
=
MotionEventCompat
.
getPointerId
(
ev
,
0
);
break
;
}
case
MotionEvent
.
ACTION_MOVE
:
if
(!
mIsBeingDragged
)
{
final
int
pointerIndex
=
MotionEventCompat
.
findPointerIndex
(
ev
,
mActivePointerId
);
final
float
x
=
MotionEventCompat
.
getX
(
ev
,
pointerIndex
);
final
float
xDiff
=
Math
.
abs
(
x
-
mLastMotionX
);
final
float
y
=
MotionEventCompat
.
getY
(
ev
,
pointerIndex
);
final
float
yDiff
=
Math
.
abs
(
y
-
mLastMotionY
);
if
(
DEBUG
)
Log
.
v
(
TAG
,
"Moved x to "
+
x
+
","
+
y
+
" diff="
+
xDiff
+
","
+
yDiff
);
if
(
xDiff
>
mTouchSlop
&&
xDiff
>
yDiff
)
{
if
(
DEBUG
)
Log
.
v
(
TAG
,
"Starting drag!"
);
mIsBeingDragged
=
true
;
mLastMotionX
=
x
;
setScrollState
(
SCROLL_STATE_DRAGGING
);
setScrollingCacheEnabled
(
true
);
}
}
if
(
mIsBeingDragged
)
{
// Scroll to follow the motion event
final
int
activePointerIndex
=
MotionEventCompat
.
findPointerIndex
(
ev
,
mActivePointerId
);
final
float
x
=
MotionEventCompat
.
getX
(
ev
,
activePointerIndex
);
final
float
deltaX
=
mLastMotionX
-
x
;
mLastMotionX
=
x
;
float
oldScrollX
=
getScrollX
();
float
scrollX
=
oldScrollX
+
deltaX
;
final
int
width
=
getWidth
();
final
int
widthWithMargin
=
width
+
mPageMargin
;
final
int
lastItemIndex
=
mAdapter
.
getCount
()
-
1
;
final
float
leftBound
=
Math
.
max
(
0
,
(
mCurItem
-
1
)
*
widthWithMargin
);
final
float
rightBound
=
Math
.
min
(
mCurItem
+
1
,
lastItemIndex
)
*
widthWithMargin
;
if
(
scrollX
<
leftBound
)
{
if
(
leftBound
==
0
)
{
float
over
=
-
scrollX
;
needsInvalidate
=
mLeftEdge
.
onPull
(
over
/
width
);
}
scrollX
=
leftBound
;
}
else
if
(
scrollX
>
rightBound
)
{
if
(
rightBound
==
lastItemIndex
*
widthWithMargin
)
{
float
over
=
scrollX
-
rightBound
;
needsInvalidate
=
mRightEdge
.
onPull
(
over
/
width
);
}
scrollX
=
rightBound
;
}
// Don't lose the rounded component
mLastMotionX
+=
scrollX
-
(
int
)
scrollX
;
scrollTo
((
int
)
scrollX
,
getScrollY
());
pageScrolled
((
int
)
scrollX
);
}
break
;
case
MotionEvent
.
ACTION_UP
:
if
(
mIsBeingDragged
)
{
final
VelocityTracker
velocityTracker
=
mVelocityTracker
;
velocityTracker
.
computeCurrentVelocity
(
1000
,
mMaximumVelocity
);
int
initialVelocity
=
(
int
)
VelocityTrackerCompat
.
getXVelocity
(
velocityTracker
,
mActivePointerId
);
mPopulatePending
=
true
;
final
int
widthWithMargin
=
getWidth
()
+
mPageMargin
;
final
int
scrollX
=
getScrollX
();
final
int
currentPage
=
scrollX
/
widthWithMargin
;
final
float
pageOffset
=
(
float
)
(
scrollX
%
widthWithMargin
)
/
widthWithMargin
;
final
int
activePointerIndex
=
MotionEventCompat
.
findPointerIndex
(
ev
,
mActivePointerId
);
final
float
x
=
MotionEventCompat
.
getX
(
ev
,
activePointerIndex
);
final
int
totalDelta
=
(
int
)
(
x
-
mInitialMotionX
);
int
nextPage
=
determineTargetPage
(
currentPage
,
pageOffset
,
initialVelocity
,
totalDelta
);
setCurrentItemInternal
(
nextPage
,
true
,
true
,
initialVelocity
);
mActivePointerId
=
INVALID_POINTER
;
endDrag
();
needsInvalidate
=
mLeftEdge
.
onRelease
()
|
mRightEdge
.
onRelease
();
}
break
;
case
MotionEvent
.
ACTION_CANCEL
:
if
(
mIsBeingDragged
)
{
setCurrentItemInternal
(
mCurItem
,
true
,
true
);
mActivePointerId
=
INVALID_POINTER
;
endDrag
();
needsInvalidate
=
mLeftEdge
.
onRelease
()
|
mRightEdge
.
onRelease
();
}
break
;
case
MotionEventCompat
.
ACTION_POINTER_DOWN
:
{
final
int
index
=
MotionEventCompat
.
getActionIndex
(
ev
);
final
float
x
=
MotionEventCompat
.
getX
(
ev
,
index
);
mLastMotionX
=
x
;
mActivePointerId
=
MotionEventCompat
.
getPointerId
(
ev
,
index
);
break
;
}
case
MotionEventCompat
.
ACTION_POINTER_UP
:
onSecondaryPointerUp
(
ev
);
mLastMotionX
=
MotionEventCompat
.
getX
(
ev
,
MotionEventCompat
.
findPointerIndex
(
ev
,
mActivePointerId
));
break
;
}
if
(
needsInvalidate
)
{
invalidate
();
}
//
if (mFakeDragging) {
//
// A fake drag is in progress already, ignore this real one
//
// but still eat the touch events.
//
// (It is likely that the user is multi-touching the screen.)
//
return true;
//
}
//
//
if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {
//
// Don't handle edge touches immediately -- they may actually belong to one of our
//
// descendants.
//
return false;
//
}
//
//
if (mAdapter == null || mAdapter.getCount() == 0) {
//
// Nothing to present or scroll; nothing to touch.
//
return false;
//
}
//
//
if (mVelocityTracker == null) {
//
mVelocityTracker = VelocityTracker.obtain();
//
}
//
mVelocityTracker.addMovement(ev);
//
//
final int action = ev.getAction();
//
boolean needsInvalidate = false;
//
//
switch (action & MotionEventCompat.ACTION_MASK) {
//
case MotionEvent.ACTION_DOWN: {
//
/*
//
* If being flinged and user touches, stop the fling. isFinished
//
* will be false if being flinged.
//
*/
//
completeScroll();
//
//
// Remember where the motion event started
//
mLastMotionX = mInitialMotionX = ev.getX();
//
mActivePointerId = MotionEventCompat.getPointerId(ev, 0);
//
break;
//
}
//
case MotionEvent.ACTION_MOVE:
//
if (!mIsBeingDragged) {
//
final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);
//
final float x = MotionEventCompat.getX(ev, pointerIndex);
//
final float xDiff = Math.abs(x - mLastMotionX);
//
final float y = MotionEventCompat.getY(ev, pointerIndex);
//
final float yDiff = Math.abs(y - mLastMotionY);
//
if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);
//
if (xDiff > mTouchSlop && xDiff > yDiff) {
//
if (DEBUG) Log.v(TAG, "Starting drag!");
//
mIsBeingDragged = true;
//
mLastMotionX = x;
//
setScrollState(SCROLL_STATE_DRAGGING);
//
setScrollingCacheEnabled(true);
//
}
//
}
//
if (mIsBeingDragged) {
//
// Scroll to follow the motion event
//
final int activePointerIndex = MotionEventCompat.findPointerIndex(
//
ev, mActivePointerId);
//
final float x = MotionEventCompat.getX(ev, activePointerIndex);
//
final float deltaX = mLastMotionX - x;
//
mLastMotionX = x;
//
float oldScrollX = getScrollX();
//
float scrollX = oldScrollX + deltaX;
//
final int width = getWidth();
//
final int widthWithMargin = width + mPageMargin;
//
//
final int lastItemIndex = mAdapter.getCount() - 1;
//
final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin);
//
final float rightBound =
//
Math.min(mCurItem + 1, lastItemIndex) * widthWithMargin;
//
if (scrollX < leftBound) {
//
if (leftBound == 0) {
//
float over = -scrollX;
//
needsInvalidate = mLeftEdge.onPull(over / width);
//
}
//
scrollX = leftBound;
//
} else if (scrollX > rightBound) {
//
if (rightBound == lastItemIndex * widthWithMargin) {
//
float over = scrollX - rightBound;
//
needsInvalidate = mRightEdge.onPull(over / width);
//
}
//
scrollX = rightBound;
//
}
//
// Don't lose the rounded component
//
mLastMotionX += scrollX - (int) scrollX;
//
scrollTo((int) scrollX, getScrollY());
//
pageScrolled((int) scrollX);
//
}
//
break;
//
case MotionEvent.ACTION_UP:
//
if (mIsBeingDragged) {
//
final VelocityTracker velocityTracker = mVelocityTracker;
//
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
//
int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(
//
velocityTracker, mActivePointerId);
//
mPopulatePending = true;
//
final int widthWithMargin = getWidth() + mPageMargin;
//
final int scrollX = getScrollX();
//
final int currentPage = scrollX / widthWithMargin;
//
final float pageOffset = (float) (scrollX % widthWithMargin) / widthWithMargin;
//
final int activePointerIndex =
//
MotionEventCompat.findPointerIndex(ev, mActivePointerId);
//
final float x = MotionEventCompat.getX(ev, activePointerIndex);
//
final int totalDelta = (int) (x - mInitialMotionX);
//
int nextPage = determineTargetPage(currentPage, pageOffset, initialVelocity,
//
totalDelta);
//
setCurrentItemInternal(nextPage, true, true, initialVelocity);
//
//
mActivePointerId = INVALID_POINTER;
//
endDrag();
//
needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
//
}
//
break;
//
case MotionEvent.ACTION_CANCEL:
//
if (mIsBeingDragged) {
//
setCurrentItemInternal(mCurItem, true, true);
//
mActivePointerId = INVALID_POINTER;
//
endDrag();
//
needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();
//
}
//
break;
//
case MotionEventCompat.ACTION_POINTER_DOWN: {
//
final int index = MotionEventCompat.getActionIndex(ev);
//
final float x = MotionEventCompat.getX(ev, index);
//
mLastMotionX = x;
//
mActivePointerId = MotionEventCompat.getPointerId(ev, index);
//
break;
//
}
//
case MotionEventCompat.ACTION_POINTER_UP:
//
onSecondaryPointerUp(ev);
//
mLastMotionX = MotionEventCompat.getX(ev,
//
MotionEventCompat.findPointerIndex(ev, mActivePointerId));
//
break;
//
}
//
if (needsInvalidate) {
//
invalidate();
//
}
return
true
;
}
...
...
imageVideoBanner/src/main/java/com/widget/imagevideobanner/view/VpImageView.java
0 → 100644
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
.
view
;
import
android.content.Context
;
import
android.graphics.drawable.Drawable
;
import
android.os.Handler
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.support.v7.widget.AppCompatImageView
;
import
android.util.Log
;
import
android.view.View
;
import
com.bumptech.glide.Glide
;
import
com.bumptech.glide.load.DataSource
;
import
com.bumptech.glide.load.engine.GlideException
;
import
com.bumptech.glide.request.RequestListener
;
import
com.bumptech.glide.request.target.Target
;
import
com.widget.imagevideobanner.R
;
import
com.widget.imagevideobanner.bean.MediaBean
;
import
com.widget.imagevideobanner.player.IPlayTarget
;
import
com.widget.imagevideobanner.utils.ThrowableUtils
;
/**
* 5秒自动切换图片
*/
public
class
VpImageView
extends
AppCompatImageView
implements
IPlayTarget
{
private
static
final
String
TAG
=
"VpImageView"
;
private
static
final
int
DEFAULT_SWITCH_PAGE_TIME
=
5000
;
private
MediaBean
mediaBean
;
private
OnActionFinishListener
onPageChangedListener
;
private
Handler
handler
=
new
Handler
();
private
Runnable
pageChangeRunable
=
new
Runnable
()
{
@Override
public
void
run
()
{
if
(
null
!=
onPageChangedListener
)
onPageChangedListener
.
next
();
}
};
public
VpImageView
(
Context
context
)
{
super
(
context
);
}
public
VpImageView
(
Context
context
,
final
MediaBean
mediaBean
,
final
OnActionFinishListener
onPageChangedListener
)
{
super
(
context
);
this
.
mediaBean
=
mediaBean
;
this
.
onPageChangedListener
=
onPageChangedListener
;
}
private
void
setImageUrl
(
String
url
)
{
//todo 待确定是否一定会使用缓存 缺失加载前&加载失败的备用图
Log
.
e
(
TAG
,
url
);
Glide
.
with
(
getContext
()).
load
(
url
)
.
error
(
R
.
drawable
.
load_fail
)
.
placeholder
(
R
.
drawable
.
load_fail
)
.
listener
(
new
RequestListener
<
Drawable
>()
{
@Override
public
boolean
onLoadFailed
(
@Nullable
GlideException
e
,
Object
model
,
Target
<
Drawable
>
target
,
boolean
isFirstResource
)
{
Log
.
e
(
TAG
,
ThrowableUtils
.
getFullStackTrace
(
e
));
return
false
;
}
@Override
public
boolean
onResourceReady
(
Drawable
resource
,
Object
model
,
Target
<
Drawable
>
target
,
DataSource
dataSource
,
boolean
isFirstResource
)
{
Log
.
e
(
TAG
,
"onResourceReady"
);
return
false
;
}
}).
into
(
this
);
}
@Override
protected
void
onVisibilityChanged
(
@NonNull
View
changedView
,
int
visibility
)
{
super
.
onVisibilityChanged
(
changedView
,
visibility
);
Log
.
e
(
TAG
,
"onVisibilityChanged"
);
if
(
visibility
==
VISIBLE
)
onActive
();
if
(
visibility
==
GONE
||
visibility
==
INVISIBLE
)
inActive
();
}
@Override
public
void
onActive
()
{
handler
.
removeCallbacks
(
pageChangeRunable
);
setImageUrl
(
mediaBean
.
getUrl
());
handler
.
postDelayed
(
pageChangeRunable
,
DEFAULT_SWITCH_PAGE_TIME
);
}
@Override
public
void
inActive
()
{
handler
.
removeCallbacks
(
pageChangeRunable
);
}
}
imageVideoBanner/src/main/java/com/widget/imagevideobanner/view/VpVideoView.java
0 → 100644
View file @
0ef30b50
package
com
.
widget
.
imagevideobanner
.
view
;
import
android.content.Context
;
import
android.graphics.drawable.Drawable
;
import
android.support.annotation.NonNull
;
import
android.support.annotation.Nullable
;
import
android.text.TextUtils
;
import
android.util.Log
;
import
android.view.Gravity
;
import
android.view.LayoutInflater
;
import
android.view.View
;
import
android.view.ViewGroup
;
import
android.view.ViewParent
;
import
android.widget.FrameLayout
;
import
android.widget.ImageView
;
import
com.bumptech.glide.Glide
;
import
com.bumptech.glide.load.DataSource
;
import
com.bumptech.glide.load.engine.GlideException
;
import
com.bumptech.glide.request.RequestListener
;
import
com.bumptech.glide.request.target.Target
;
import
com.google.android.exoplayer2.ExoPlaybackException
;
import
com.google.android.exoplayer2.PlaybackParameters
;
import
com.google.android.exoplayer2.Player
;
import
com.google.android.exoplayer2.SimpleExoPlayer
;
import
com.google.android.exoplayer2.Timeline
;
import
com.google.android.exoplayer2.source.MediaSource
;
import
com.google.android.exoplayer2.source.TrackGroupArray
;
import
com.google.android.exoplayer2.trackselection.TrackSelectionArray
;
import
com.google.android.exoplayer2.ui.PlayerControlView
;
import
com.google.android.exoplayer2.ui.PlayerView
;
import
com.widget.imagevideobanner.R
;
import
com.widget.imagevideobanner.bean.MediaBean
;
import
com.widget.imagevideobanner.player.IPlayTarget
;
import
com.widget.imagevideobanner.player.PlayerManager
;
import
com.widget.imagevideobanner.utils.ThrowableUtils
;
import
java.math.BigDecimal
;
import
jp.wasabeef.glide.transformations.BlurTransformation
;
public
class
VpVideoView
extends
FrameLayout
implements
IPlayTarget
,
PlayerControlView
.
VisibilityListener
,
Player
.
EventListener
{
private
static
final
String
TAG
=
"VpVideoView"
;
private
ImageView
cover
,
blur
;
private
String
mVideoUrl
;
private
int
mWidthPx
;
private
int
mHeightPx
;
private
View
bufferView
;
private
OnActionFinishListener
mOnPageChangedListener
;
private
MediaBean
mediaBean
;
private
boolean
isVideoEnded
=
false
;
public
VpVideoView
(
@NonNull
Context
context
,
MediaBean
bean
,
final
OnActionFinishListener
onPageChangedListener
)
{
super
(
context
);
LayoutInflater
.
from
(
context
).
inflate
(
R
.
layout
.
layout_player_view
,
this
,
true
);
//封面view
cover
=
findViewById
(
R
.
id
.
cover
);
//高斯模糊背景图,防止出现两边留嘿
blur
=
findViewById
(
R
.
id
.
blur_background
);
//缓冲转圈圈的view
bufferView
=
findViewById
(
R
.
id
.
buffer_view
);
//播放完成监听
mOnPageChangedListener
=
onPageChangedListener
;
this
.
mediaBean
=
bean
;
bindData
(
mediaBean
.
getWidth
(),
mediaBean
.
getHeight
(),
mediaBean
.
getCover
(),
mediaBean
.
getUrl
());
}
public
void
bindData
(
int
widthPx
,
int
heightPx
,
String
coverUrl
,
String
videoUrl
)
{
mVideoUrl
=
videoUrl
;
mWidthPx
=
widthPx
;
mHeightPx
=
heightPx
;
setCoverImageUrl
(
coverUrl
,
cover
);
setBlurImageUrl
(
coverUrl
,
blur
);
}
private
void
setBlurImageUrl
(
String
blurUrl
,
ImageView
blur
){
Glide
.
with
(
getContext
())
.
load
(
blurUrl
)
.
transform
(
new
BlurTransformation
())
.
dontAnimate
()
.
listener
(
new
RequestListener
<
Drawable
>()
{
@Override
public
boolean
onLoadFailed
(
@Nullable
GlideException
e
,
Object
model
,
Target
<
Drawable
>
target
,
boolean
isFirstResource
)
{
Log
.
e
(
TAG
,
ThrowableUtils
.
getFullStackTrace
(
e
));
return
false
;
}
@Override
public
boolean
onResourceReady
(
Drawable
resource
,
Object
model
,
Target
<
Drawable
>
target
,
DataSource
dataSource
,
boolean
isFirstResource
)
{
Log
.
e
(
TAG
,
"onResourceReady"
);
return
false
;
}
}).
into
(
blur
);
}
//todo 缓存固定尺寸
private
void
setCoverImageUrl
(
String
coverUrl
,
ImageView
cover
){
Glide
.
with
(
getContext
())
.
load
(
coverUrl
)
.
dontAnimate
()
.
listener
(
new
RequestListener
<
Drawable
>()
{
@Override
public
boolean
onLoadFailed
(
@Nullable
GlideException
e
,
Object
model
,
Target
<
Drawable
>
target
,
boolean
isFirstResource
)
{
Log
.
e
(
TAG
,
ThrowableUtils
.
getFullStackTrace
(
e
));
return
false
;
}
@Override
public
boolean
onResourceReady
(
Drawable
resource
,
Object
model
,
Target
<
Drawable
>
target
,
DataSource
dataSource
,
boolean
isFirstResource
)
{
Log
.
e
(
TAG
,
"onResourceReady"
);
return
false
;
}
}).
into
(
cover
);
}
@Override
protected
void
onSizeChanged
(
int
w
,
int
h
,
int
oldw
,
int
oldh
)
{
super
.
onSizeChanged
(
w
,
h
,
oldw
,
oldh
);
//动态设置
int
mHeight
=
getHeight
();
int
mWidth
=
getWidth
();
BigDecimal
viewScale
=
new
BigDecimal
(
mWidth
+
""
)
.
divide
(
new
BigDecimal
(
mHeight
+
""
),
2
,
BigDecimal
.
ROUND_HALF_UP
);
BigDecimal
videoScale
=
new
BigDecimal
(
mWidthPx
+
""
)
.
divide
(
new
BigDecimal
(
mHeightPx
+
""
),
2
,
BigDecimal
.
ROUND_HALF_UP
);
if
(
viewScale
.
compareTo
(
videoScale
)
>=
0
){
//按照高度缩放
BigDecimal
heightScale
=
new
BigDecimal
(
mHeight
+
""
)
.
divide
(
new
BigDecimal
(
mHeightPx
+
""
),
2
,
BigDecimal
.
ROUND_HALF_UP
);
mHeightPx
=
mHeight
;
mWidthPx
=
new
BigDecimal
(
mWidthPx
+
""
).
multiply
(
heightScale
).
intValue
();
}
else
{
//按照宽度缩放
BigDecimal
widthScale
=
new
BigDecimal
(
mWidth
+
""
)
.
divide
(
new
BigDecimal
(
mWidthPx
+
""
),
2
,
BigDecimal
.
ROUND_HALF_UP
);
mWidthPx
=
mWidth
;
mHeightPx
=
new
BigDecimal
(
mHeightPx
+
""
).
multiply
(
widthScale
).
intValue
();
}
FrameLayout
.
LayoutParams
coverParams
=
(
LayoutParams
)
cover
.
getLayoutParams
();
coverParams
.
width
=
mWidthPx
;
coverParams
.
height
=
mHeightPx
;
coverParams
.
gravity
=
Gravity
.
CENTER
;
cover
.
setLayoutParams
(
coverParams
);
//todo onVisibilityChange 和 onSizeChanged哪个先执行
Log
.
e
(
TAG
,
"onSizeChanged"
+
mWidthPx
+
" "
+
mHeightPx
);
}
@Override
public
void
onTimelineChanged
(
Timeline
timeline
,
Object
manifest
,
int
reason
)
{
}
@Override
public
void
onTracksChanged
(
TrackGroupArray
trackGroups
,
TrackSelectionArray
trackSelections
)
{
}
@Override
public
void
onLoadingChanged
(
boolean
isLoading
)
{
}
@Override
public
void
onPlayerStateChanged
(
boolean
playWhenReady
,
int
playbackState
)
{
Log
.
e
(
TAG
,
"onPlayerStateChanged playbackState"
+
playbackState
);
SimpleExoPlayer
exoPlayer
=
PlayerManager
.
getInstance
().
exoPlayer
;
if
(
playbackState
==
Player
.
STATE_READY
&&
exoPlayer
.
getBufferedPosition
()
!=
0
&&
playWhenReady
)
{
cover
.
setVisibility
(
GONE
);
bufferView
.
setVisibility
(
GONE
);
}
else
if
(
playbackState
==
Player
.
STATE_BUFFERING
)
{
cover
.
setVisibility
(
VISIBLE
);
bufferView
.
setVisibility
(
VISIBLE
);
}
if
(
playbackState
==
Player
.
STATE_ENDED
){
if
(!
isVideoEnded
){
isVideoEnded
=
true
;
playNextVideo
();
}
}
}
private
void
playNextVideo
(){
if
(
null
!=
mOnPageChangedListener
){
inActive
();
mOnPageChangedListener
.
next
();
}
}
@Override
public
void
onRepeatModeChanged
(
int
repeatMode
)
{
}
@Override
public
void
onShuffleModeEnabledChanged
(
boolean
shuffleModeEnabled
)
{
}
@Override
public
void
onPlayerError
(
ExoPlaybackException
error
)
{
Log
.
e
(
TAG
,
ThrowableUtils
.
getFullStackTrace
(
error
));
playNextVideo
();
}
@Override
public
void
onPositionDiscontinuity
(
int
reason
)
{
}
@Override
public
void
onPlaybackParametersChanged
(
PlaybackParameters
playbackParameters
)
{
}
@Override
public
void
onSeekProcessed
()
{
}
@Override
public
void
onVisibilityChange
(
int
visibility
)
{
Log
.
e
(
TAG
,
"onVisibilityChange"
);
if
(
visibility
==
ViewGroup
.
VISIBLE
){
onActive
();
}
else
{
inActive
();
}
}
@Override
public
void
onActive
()
{
//todo 这个地方能否得到具体尺寸
//视频播放,或恢复播放
//通过该View所在页面的mCategory(比如首页列表tab_all,沙发tab的tab_video,标签帖子聚合的tag_feed) 字段,
//取出管理该页面的Exoplayer播放器,ExoplayerView播放View,控制器对象PageListPlay
PlayerManager
playerManager
=
PlayerManager
.
getInstance
();
SimpleExoPlayer
exoPlayer
=
playerManager
.
exoPlayer
;
PlayerView
playerView
=
playerManager
.
playerView
;
//此处我们需要主动调用一次 switchPlayerView,把播放器Exoplayer和展示视频画面的View ExoplayerView相关联
//为什么呢?因为在列表页点击视频Item跳转到视频详情页的时候,详情页会复用列表页的播放器Exoplayer,然后和新创建的展示视频画面的View ExoplayerView相关联,达到视频无缝续播的效果
//如果 我们再次返回列表页,则需要再次把播放器和ExoplayerView相关联
playerManager
.
switchPlayerView
(
playerView
,
true
);
ViewParent
parent
=
playerView
.
getParent
();
if
(
parent
!=
this
)
{
//把展示视频画面的View添加到ItemView的容器上
if
(
parent
!=
null
)
{
((
ViewGroup
)
parent
).
removeView
(
playerView
);
}
//视频尺寸参数共用cover
ViewGroup
.
LayoutParams
coverParams
=
cover
.
getLayoutParams
();
this
.
addView
(
playerView
,
1
,
coverParams
);
}
//如果是同一个视频资源,则不需要从重新创建mediaSource。
//但需要onPlayerStateChanged 否则不会触发onPlayerStateChanged()
if
(
TextUtils
.
equals
(
playerManager
.
playUrl
,
mVideoUrl
))
{
onPlayerStateChanged
(
true
,
Player
.
STATE_READY
);
}
else
{
MediaSource
mediaSource
=
playerManager
.
createMediaSource
(
mVideoUrl
);
exoPlayer
.
prepare
(
mediaSource
);
//TODO 如果只有一个视频 模式改为循环播放
exoPlayer
.
setRepeatMode
(
Player
.
REPEAT_MODE_OFF
);
playerManager
.
playUrl
=
mVideoUrl
;
}
exoPlayer
.
addListener
(
this
);
exoPlayer
.
setPlayWhenReady
(
true
);
}
@Override
protected
void
onAttachedToWindow
()
{
super
.
onAttachedToWindow
();
onActive
();
}
@Override
protected
void
onDetachedFromWindow
()
{
super
.
onDetachedFromWindow
();
bufferView
.
setVisibility
(
GONE
);
cover
.
setVisibility
(
VISIBLE
);
}
@Override
public
void
inActive
()
{
PlayerManager
playerManager
=
PlayerManager
.
getInstance
();
playerManager
.
exoPlayer
.
setPlayWhenReady
(
false
);
playerManager
.
exoPlayer
.
removeListener
(
this
);
View
child
=
getChildAt
(
1
);
if
(
child
instanceof
PlayerView
){
PlayerView
playerView
=
(
PlayerView
)
child
;
playerView
.
setPlayer
(
null
);
playerManager
.
playerView
.
setPlayer
(
null
);
this
.
removeViewInLayout
(
child
);
}
}
}
imageVideoBanner/src/main/res/layout/activity_detail.xml
0 → 100644
View file @
0ef30b50
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:layout_height=
"match_parent"
>
<TextView
android:id=
"@+id/tvBack"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:textSize=
"50sp"
android:text=
"返回"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:layout_constraintBottom_toBottomOf=
"parent"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
imageVideoBanner/src/main/res/layout/activity_main.xml
View file @
0ef30b50
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:layout_height=
"match_parent"
>
<com.widget.imagevideobanner.
banner
.ImageVideoBanner
<com.widget.imagevideobanner.
view
.ImageVideoBanner
android:id=
"@+id/banner"
android:layout_width=
"match_parent"
android:layout_height=
"0dp"
app:layout_constraintHeight_percent=
"0.6"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:period=
"5000"
/>
app:layout_constraintTop_toTopOf=
"parent"
/>
<TextView
android:id=
"@+id/tvGoDetail"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"进入第二个页面"
android:textSize=
"40sp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
app:layout_constraintTop_toBottomOf=
"@+id/banner"
/>
</android.support.constraint.ConstraintLayout>
\ No newline at end of file
imageVideoBanner/src/main/res/layout/banner_imge_video.xml
View file @
0ef30b50
...
...
@@ -5,10 +5,10 @@
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
>
<com.widget.imagevideobanner.
banner
.ViewPager
<com.widget.imagevideobanner.
view
.ViewPager
android:id=
"@+id/view_pager"
android:layout_width=
"match_parent"
android:layout_height=
"
wrap_cont
ent"
android:layout_height=
"
match_par
ent"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintEnd_toEndOf=
"parent"
app:layout_constraintStart_toStartOf=
"parent"
...
...
imageVideoBanner/src/main/res/layout/item_video_view.xml
View file @
0ef30b50
...
...
@@ -5,7 +5,7 @@
android:layout_height=
"match_parent"
android:id=
"@+id/cVideoView"
>
<com.widget.imagevideobanner.
banner
.MyVideoView
<com.widget.imagevideobanner.
view
.MyVideoView
android:id=
"@+id/video_view"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
...
...
imageVideoBanner/src/main/res/layout/layout_exo_player_view.xml
0 → 100755
View file @
0ef30b50
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.exoplayer2.ui.PlayerView
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res-auto"
android:id=
"@+id/player_view"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:keepScreenOn=
"true"
app:use_controller=
"false"
app:show_timeout=
"1000"
app:surface_type=
"texture_view"
app:resize_mode=
"zoom"
app:player_layout_id=
"@layout/layout_simple_exo_player_view"
android:orientation=
"vertical"
>
</com.google.android.exoplayer2.ui.PlayerView>
\ No newline at end of file
imageVideoBanner/src/main/res/layout/layout_player_view.xml
0 → 100755
View file @
0ef30b50
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:tools=
"http://schemas.android.com/tools"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
>
<ImageView
android:id=
"@+id/blur_background"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:transitionName=
"blur_image"
android:scaleType=
"centerCrop"
tools:background=
"@drawable/guide_defaut"
/>
<!-- 真正能够播放展示视频画面的view 会动态的添加到这里-->
<ImageView
android:id=
"@+id/cover"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:scaleType=
"centerInside"
android:transitionName=
"cover"
/>
<ProgressBar
android:id=
"@+id/buffer_view"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:layout_gravity=
"center"
android:indeterminate=
"true"
android:indeterminateTint=
"#CAE7DF"
android:transitionName=
"buffer_view"
android:visibility=
"visible"
/>
</merge>
\ No newline at end of file
imageVideoBanner/src/main/res/layout/layout_simple_exo_player_view.xml
0 → 100755
View file @
0ef30b50
<merge
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:id=
"@id/exo_content_frame"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:layout_gravity=
"center"
>
<!-- Video surface will be inserted as the first child of the content frame. -->
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
</merge>
imageVideoBanner/src/main/res/values/styles.xml
View file @
0ef30b50
...
...
@@ -8,6 +8,4 @@
<item
name=
"colorAccent"
>
@color/colorAccent
</item>
</style>
</resources>
imageVideoBanner/src/test/java/com/zxj/imagevideobanner/ExampleUnitTest.java
deleted
100644 → 0
View file @
d75937a0
package
com
.
zxj
.
imagevideobanner
;
import
org.junit.Test
;
import
static
org
.
junit
.
Assert
.*;
/**
* Example local unit test, which will execute on the development machine (host).
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
public
class
ExampleUnitTest
{
@Test
public
void
addition_isCorrect
()
throws
Exception
{
assertEquals
(
4
,
2
+
2
);
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment