Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
YDL-Component-Medical
Overview
Overview
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
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
杨凯
YDL-Component-Medical
Commits
fd5ab45f
Commit
fd5ab45f
authored
May 19, 2022
by
万齐军
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 倾诉视频页
parent
5cf363dd
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
388 additions
and
58 deletions
+388
-58
VideoShowAdapter.kt
m-confide/src/main/java/com/ydl/confide/home/adapter/VideoShowAdapter.kt
+1
-1
ConfideHomeEventImpl.kt
m-confide/src/main/java/com/ydl/confide/home/event/ConfideHomeEventImpl.kt
+5
-4
ConfideHomeApi.kt
m-confide/src/main/java/com/ydl/confide/home/http/ConfideHomeApi.kt
+8
-0
ExpertIntroActivity.kt
m-confide/src/main/java/com/ydl/confide/intro/ExpertIntroActivity.kt
+46
-12
IntroAdapter.kt
m-confide/src/main/java/com/ydl/confide/intro/IntroAdapter.kt
+28
-20
ItemIntroHolder.kt
m-confide/src/main/java/com/ydl/confide/intro/ItemIntroHolder.kt
+43
-12
confide_seekbar_style.xml
m-confide/src/main/res/drawable/confide_seekbar_style.xml
+4
-4
item_expert_intro.xml
m-confide/src/main/res/layout/item_expert_intro.xml
+35
-5
build.gradle
ydl-utils/build.gradle
+1
-0
VoicePlayingIcon.java
ydl-utils/src/main/java/com/yidianling/common/view/ui/VoicePlayingIcon.java
+209
-0
attrs.xml
ydl-utils/src/main/res/values/attrs.xml
+8
-0
No files found.
m-confide/src/main/java/com/ydl/confide/home/adapter/VideoShowAdapter.kt
View file @
fd5ab45f
...
...
@@ -29,7 +29,7 @@ class VideoShowAdapter(private val data: List<ConfideHomeBodyBean>?, private val
override
fun
onBindViewHolder
(
holder
:
BindingViewHolder
<
ItemVideoShowBinding
>,
position
:
Int
)
{
val
itemVideoShowViewModel
=
dataList
[
position
]
holder
.
binding
.
item
=
itemVideoShowViewModel
holder
.
itemView
.
setOnClickListener
{
/*event.videoShowClick(position, data)*/
}
holder
.
itemView
.
setOnClickListener
{
event
.
videoShowClick
(
position
,
data
)
}
}
override
fun
getItemCount
()
=
dataList
.
size
...
...
m-confide/src/main/java/com/ydl/confide/home/event/ConfideHomeEventImpl.kt
View file @
fd5ab45f
...
...
@@ -4,12 +4,12 @@ import android.annotation.SuppressLint
import
android.app.Activity
import
android.content.Context
import
android.content.Intent
import
android.graphics.Color
import
android.net.Uri
import
android.opengl.Visibility
import
android.os.Build
import
android.text.TextUtils
import
android.view.*
import
android.view.Gravity
import
android.view.LayoutInflater
import
android.view.MotionEvent
import
android.view.View
import
android.widget.FrameLayout
import
androidx.appcompat.app.AppCompatActivity
import
com.alibaba.android.arouter.launcher.ARouter
...
...
@@ -253,6 +253,7 @@ class ConfideHomeEventImpl(context: Context, var confideHomeView: IConfideHomeCo
override
fun
videoShowClick
(
index
:
Int
,
data
:
List
<
ConfideHomeBodyBean
>?)
{
val
dataJson
=
if
(
data
!=
null
)
JSON
.
toJSONString
(
data
)
else
null
pauseVoice
()
ARouter
.
getInstance
().
build
(
ConfideRoute
.
R_VIDEO_SHOW
).
withInt
(
"initPos"
,
index
)
.
withString
(
"initData"
,
dataJson
).
navigation
()
}
...
...
m-confide/src/main/java/com/ydl/confide/home/http/ConfideHomeApi.kt
View file @
fd5ab45f
...
...
@@ -38,4 +38,11 @@ interface ConfideHomeApi {
// 1=在线 3-通话中 2-离线
@GET
(
"auth/listen/dialchangestatus"
)
fun
getDialStatus
(
@Query
(
"doctorId"
)
doctorId
:
String
):
Observable
<
BaseAPIResponse
<
DialStatus
>>
@GET
fun
recommendDoctor
(
@Url
url
:
String
,
@Query
(
"page"
)
page
:
Int
,
@Query
(
"businessSource"
)
source
:
Int
):
Observable
<
BaseAPIResponse
<
ConfideHomeDataBean
>>
}
\ No newline at end of file
m-confide/src/main/java/com/ydl/confide/intro/ExpertIntroActivity.kt
View file @
fd5ab45f
...
...
@@ -15,28 +15,44 @@ import com.ydl.confide.R
import
com.ydl.confide.api.ConfideRoute
import
com.ydl.confide.databinding.ActivityExpertIntroBinding
import
com.ydl.confide.home.bean.ConfideHomeBodyBean
import
com.ydl.confide.home.bean.ConfideHomeDataBean
import
com.ydl.confide.home.util.ConfideNetworkUtil
import
com.ydl.confide.home.http.ConfideHomeApi
import
com.ydl.confide.router.PhoneCallIn
import
com.ydl.webview.H5Params
import
com.ydl.webview.NewH5Activity
import
com.ydl.ydlcommon.base.config.HttpConfig
import
com.ydl.ydlcommon.base.config.HttpConfig.Companion.YDL_H5
import
com.ydl.ydlcommon.utils.BuryPointUtils
import
com.ydl.ydlcommon.view.dialog.CommonDialog
import
com.ydl.ydlcommon.utils.LogUtil
import
com.ydl.ydlnet.YDLHttpUtils
import
com.yidianling.common.tools.ToastUtil
import
io.reactivex.android.schedulers.AndroidSchedulers
import
io.reactivex.disposables.Disposable
import
io.reactivex.schedulers.Schedulers
@Route
(
path
=
ConfideRoute
.
R_VIDEO_SHOW
)
class
ExpertIntroActivity
:
AppCompatActivity
()
{
companion
object
{
private
const
val
SOURCE_VIDEO
=
10
}
private
val
tag
=
javaClass
.
simpleName
@Autowired
@JvmField
var
initPos
:
Int
=
0
@Autowired
@JvmField
var
initData
:
String
?=
null
var
initData
:
String
?
=
null
private
var
lastSelectPos
=
0
private
lateinit
var
binding
:
ActivityExpertIntroBinding
private
lateinit
var
adapter
:
IntroAdapter
private
var
page
=
1
private
var
disposable
:
Disposable
?
=
null
private
val
confideApi
=
YDLHttpUtils
.
obtainApi
(
ConfideHomeApi
::
class
.
java
)
private
val
data
=
mutableListOf
<
VideoViewModel
>()
...
...
@@ -51,14 +67,14 @@ class ExpertIntroActivity : AppCompatActivity() {
decorView
.
systemUiVisibility
=
option
window
.
statusBarColor
=
Color
.
TRANSPARENT
}
if
(
initData
!=
null
)
{
if
(
initData
!=
null
)
{
val
beans
=
JSON
.
parseArray
(
initData
,
ConfideHomeBodyBean
::
class
.
java
)
val
vms
=
beans
.
map
{
VideoViewModel
().
mapOf
(
it
)
}
data
.
addAll
(
vms
)
}
binding
.
ivBack
.
setOnClickListener
{
onBackPressed
()
}
binding
.
tvConfideRecord
.
setOnClickListener
{
if
(!
PhoneCallIn
.
loginByOneKeyLogin
(
this
,
true
))
{
if
(!
PhoneCallIn
.
loginByOneKeyLogin
(
this
,
true
))
{
return
@setOnClickListener
}
BuryPointUtils
.
getInstance
().
createMap
()
...
...
@@ -84,11 +100,28 @@ class ExpertIntroActivity : AppCompatActivity() {
}
private
fun
loadMore
()
{
// data.add(VideoViewModel())
// data.add(VideoViewModel())
// data.add(VideoViewModel())
// data.add(VideoViewModel())
// data.add(VideoViewModel())
// adapter.notifyItemRangeInserted(data.size - 5, 5)
disposable
=
confideApi
.
recommendDoctor
(
HttpConfig
.
JAVA_BASE_URL
+
"auth/listen/nsearch?"
,
page
,
SOURCE_VIDEO
)
.
subscribeOn
(
Schedulers
.
io
())
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribe
({
resp
->
if
(
resp
.
code
==
"200"
)
{
page
++
val
body
=
resp
.
data
.
body
val
map
=
body
?.
map
{
VideoViewModel
().
mapOf
(
it
)
}
map
?.
let
{
data
.
addAll
(
it
)
adapter
.
notifyItemRangeInserted
(
data
.
size
-
it
.
size
,
it
.
size
)
}
}
else
{
ToastUtil
.
toastShort
(
resp
.
msg
)
}
},
{
throwable
->
LogUtil
.
e
(
tag
,
throwable
.
message
)
})
}
override
fun
onDestroy
()
{
super
.
onDestroy
()
disposable
?.
dispose
()
}
}
\ No newline at end of file
m-confide/src/main/java/com/ydl/confide/intro/IntroAdapter.kt
View file @
fd5ab45f
...
...
@@ -3,7 +3,6 @@ package com.ydl.confide.intro
import
android.app.Activity
import
android.content.Context
import
android.net.Uri
import
android.util.Log
import
android.view.LayoutInflater
import
android.view.ViewGroup
import
androidx.databinding.DataBindingUtil
...
...
@@ -49,8 +48,7 @@ internal class IntroAdapter(
parent
,
false
)
val
holder
=
ItemIntroHolder
(
binding
)
return
holder
return
ItemIntroHolder
(
binding
)
}
override
fun
onBindViewHolder
(
holder
:
ItemIntroHolder
,
position
:
Int
)
{
...
...
@@ -63,10 +61,13 @@ internal class IntroAdapter(
override
fun
onViewAttachedToWindow
(
holder
:
ItemIntroHolder
)
{
val
adapterPosition
=
holder
.
adapterPosition
val
videoView
=
IjkVideoView
(
context
)
if
(
hasAgreePlayWithoutWiFi
||
ConfideNetworkUtil
.
isWifi
(
context
))
{
videoView
.
setVideoURI
(
Uri
.
parse
(
"https://video.ydlcdn.com/2020/04/01/ac2e4bb4e3ac8e2f0eca41e2d49c8484.mp4"
))
}
else
{
data
[
adapterPosition
].
playUrl
?.
let
{
videoView
.
tag
=
it
}
val
playUrl
=
data
[
adapterPosition
].
playUrl
if
(!
playUrl
.
isNullOrBlank
())
{
if
(
hasAgreePlayWithoutWiFi
||
ConfideNetworkUtil
.
isWifi
(
context
))
{
videoView
.
setVideoURI
(
Uri
.
parse
(
playUrl
))
}
else
{
videoView
.
tag
=
playUrl
}
}
videoViews
.
put
(
adapterPosition
,
videoView
)
holder
.
onAttach
(
videoView
)
...
...
@@ -81,8 +82,9 @@ internal class IntroAdapter(
hasAgreePlayWithoutWiFi
=
true
for
(
entry
in
videoViews
.
entries
)
{
val
value
=
entry
.
value
(
value
.
tag
as
?
String
)
?.
let
{
value
.
setVideoURI
(
Uri
.
parse
(
it
))
val
playUrl
=
value
.
tag
as
?
String
if
(!
playUrl
.
isNullOrBlank
())
{
value
.
setVideoURI
(
Uri
.
parse
(
playUrl
))
if
(
curPos
==
entry
.
key
)
{
value
.
start
()
}
...
...
@@ -140,6 +142,21 @@ internal class IntroAdapter(
fun
onSelect
(
position
:
Int
)
{
curPos
=
position
onLoadDialStatus
(
position
)
if
(!
ConfideNetworkUtil
.
isWifi
(
context
)
&&
!
hasAgreePlayWithoutWiFi
)
{
return
}
for
(
entry
in
videoViews
.
entries
)
{
if
(
entry
.
key
==
position
)
{
entry
.
value
.
seekTo
(
0
)
entry
.
value
.
start
()
}
else
{
entry
.
value
.
pause
()
}
}
}
private
fun
onLoadDialStatus
(
position
:
Int
)
{
val
confideApi
=
YDLHttpUtils
.
obtainApi
(
ConfideHomeApi
::
class
.
java
)
val
curUid
=
data
[
position
].
uid
if
(
curUid
!=
null
)
{
...
...
@@ -158,16 +175,6 @@ internal class IntroAdapter(
}
},
{
throwable
->
throwable
.
printStackTrace
()
})
}
if
(!
ConfideNetworkUtil
.
isWifi
(
context
)
&&
!
hasAgreePlayWithoutWiFi
)
{
return
}
for
(
entry
in
videoViews
.
entries
)
{
if
(
entry
.
key
==
position
)
{
entry
.
value
.
seekTo
(
0
)
entry
.
value
.
start
()
}
else
{
entry
.
value
.
pause
()
}
}
}
}
\ No newline at end of file
m-confide/src/main/java/com/ydl/confide/intro/ItemIntroHolder.kt
View file @
fd5ab45f
...
...
@@ -12,6 +12,7 @@ import com.alibaba.android.arouter.launcher.ARouter
import
com.dou361.ijkplayer.widget.IjkVideoView
import
com.ydl.confide.databinding.ItemExpertIntroBinding
import
com.ydl.confide.home.bean.ConfideHomeBodyBean
import
com.ydl.ydlcommon.utils.LogUtil
import
com.ydl.ydlcommon.view.dialog.YDLShareDialog
import
com.yidianling.im.api.service.IImService
import
io.reactivex.Observable
...
...
@@ -29,6 +30,8 @@ internal class ItemIntroHolder(binding: ItemExpertIntroBinding) :
private
var
disposable
:
Disposable
?
=
null
private
var
video
:
IjkVideoView
?
=
null
private
var
vm
:
VideoViewModel
?
=
null
@Volatile
private
var
isTouch
=
false
...
...
@@ -38,6 +41,7 @@ internal class ItemIntroHolder(binding: ItemExpertIntroBinding) :
}
fun
onBind
(
item
:
VideoViewModel
)
{
vm
=
item
binding
.
layoutCall
.
setOnClickListener
{
}
binding
.
btnChat
.
setOnClickListener
{
val
aty
=
it
.
context
as
?
Activity
...
...
@@ -55,12 +59,22 @@ internal class ItemIntroHolder(binding: ItemExpertIntroBinding) :
}
}
binding
.
videoView
.
setOnClickListener
{
video
?.
pause
()
binding
.
ivPlay
.
visibility
=
View
.
VISIBLE
if
(
video
?.
canPause
()
==
true
)
{
video
?.
pause
()
if
(
item
.
isVideo
)
{
binding
.
ivPlay
.
visibility
=
View
.
VISIBLE
}
else
{
binding
.
voicePlay
.
stop
()
}
}
}
binding
.
ivPlay
.
setOnClickListener
{
video
?.
start
()
binding
.
ivPlay
.
visibility
=
View
.
GONE
if
(
item
.
isVideo
)
{
binding
.
ivPlay
.
visibility
=
View
.
GONE
}
else
{
binding
.
voicePlay
.
start
()
}
}
binding
.
seekbar
.
setOnSeekBarChangeListener
(
object
:
SeekBar
.
OnSeekBarChangeListener
{
override
fun
onProgressChanged
(
seekBar
:
SeekBar
?,
progress
:
Int
,
fromUser
:
Boolean
)
{
...
...
@@ -80,6 +94,7 @@ internal class ItemIntroHolder(binding: ItemExpertIntroBinding) :
}
}
})
binding
.
voicePlay
.
visibility
=
if
(
item
.
isVideo
)
View
.
GONE
else
View
.
VISIBLE
}
fun
onAttach
(
videoView
:
IjkVideoView
)
{
...
...
@@ -95,22 +110,36 @@ internal class ItemIntroHolder(binding: ItemExpertIntroBinding) :
}
}
video
?.
setOnInfoListener
{
mp
,
what
,
extra
->
Log
.
d
(
TAG
,
"${what},${extra}"
)
return
@setOnInfoListener
false
Log
.
d
(
TAG
,
"OnInfo:${what},${extra}"
)
return
@setOnInfoListener
true
}
video
?.
setOnCompletionListener
{
video
?.
seekTo
(
0
)
video
?.
start
()
}
video
?.
setOnErrorListener
{
player
,
what
,
extra
->
LogUtil
.
e
(
TAG
,
"onError:$what,$extra"
)
return
@setOnErrorListener
true
}
video
?.
setOnCompletionListener
{
stopTiming
()
}
startTiming
()
if
(
binding
.
voicePlay
.
visibility
==
View
.
VISIBLE
)
{
binding
.
voicePlay
.
start
()
}
}
private
fun
startTiming
()
{
disposable
=
Observable
.
interval
(
3
00
,
TimeUnit
.
MILLISECONDS
)
disposable
=
Observable
.
interval
(
3
,
TimeUnit
.
MILLISECONDS
)
.
observeOn
(
AndroidSchedulers
.
mainThread
())
.
subscribeOn
(
Schedulers
.
computation
())
.
filter
{
!
isTouch
}
.
subscribe
{
val
pos
=
(
video
?.
mMediaPlayer
as
IjkMediaPlayer
?)
?.
currentPosition
?:
0
if
(
pos
>
0
)
{
binding
.
seekbar
.
progress
=
pos
.
toInt
()
// video.bufferPercentage
if
(
vm
?.
isVideo
==
true
)
{
binding
.
ivCover
.
visibility
=
View
.
GONE
}
if
(!
isTouch
)
{
binding
.
seekbar
.
progress
=
pos
.
toInt
()
// video.bufferPercentage
}
}
}
}
...
...
@@ -127,9 +156,9 @@ class VideoViewModel {
val
lineStatus
=
ObservableInt
()
val
intro
=
ObservableField
<
String
>(
""
)
val
tag
=
ObservableField
<
String
>(
""
)
var
playUrl
:
String
?
=
null
//"https://video.ydlcdn.com/2020/04/01/ac2e4bb4e3ac8e2f0eca41e2d49c8484.mp4"
var
playUrl
:
String
?
=
null
var
coverUrl
=
ObservableField
<
String
>(
""
)
var
isVideo
=
tru
e
var
isVideo
=
fals
e
var
uid
:
String
?
=
null
var
linkUrl
:
String
?
=
null
}
...
...
@@ -141,7 +170,9 @@ internal fun VideoViewModel.mapOf(bean: ConfideHomeBodyBean): VideoViewModel {
intro
.
set
(
bean
.
confideContent
)
val
sb
=
StringBuilder
()
bean
.
confidedTag
?.
forEach
{
sb
.
append
(
it
).
append
(
" | "
)
}
sb
.
setLength
(
sb
.
length
-
1
)
if
(
sb
.
isNotEmpty
())
{
sb
.
setLength
(
sb
.
length
-
3
)
}
tag
.
set
(
sb
.
toString
())
if
(
bean
.
videoUrl
!=
null
)
{
playUrl
=
bean
.
videoUrl
...
...
m-confide/src/main/res/drawable/confide_seekbar_style.xml
View file @
fd5ab45f
...
...
@@ -7,14 +7,14 @@
<shape>
<corners
android:radius=
"4dp"
/>
<solid
android:color=
"#59FFFFFF"
/>
<stroke
android:width=
"1dp"
/>
<stroke
android:width=
"1dp"
android:color=
"@color/transparent"
/>
</shape>
</item>
<item>
<shape>
<corners
android:radius=
"4dp"
/>
<solid
android:color=
"#59FFFFFF"
/>
<stroke
android:width=
"
2dp"
/>
<stroke
android:width=
"
3dp"
android:color=
"@color/transparent"
/>
</shape>
</item>
</selector>
...
...
@@ -36,7 +36,7 @@
<shape>
<corners
android:radius=
"4dp"
/>
<solid
android:color=
"@color/white"
/>
<stroke
android:width=
"1dp"
/>
<stroke
android:width=
"1dp"
android:color=
"@color/transparent"
/>
</shape>
</clip>
</item>
...
...
@@ -45,7 +45,7 @@
<shape>
<corners
android:radius=
"4dp"
/>
<solid
android:color=
"@color/white"
/>
<stroke
android:width=
"
2dp"
/>
<stroke
android:width=
"
3dp"
android:color=
"@color/transparent"
/>
</shape>
</clip>
</item>
...
...
m-confide/src/main/res/layout/item_expert_intro.xml
View file @
fd5ab45f
...
...
@@ -27,6 +27,16 @@
app:layout_constraintTop_toTopOf=
"parent"
/>
<ImageView
android:id=
"@+id/ivCover"
android:layout_width=
"0dp"
android:layout_height=
"0dp"
app:imageUrl=
"@{item.coverUrl}"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<ImageView
android:id=
"@+id/ivPlay"
android:layout_width=
"76dp"
android:layout_height=
"76dp"
...
...
@@ -37,6 +47,18 @@
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
/>
<com.yidianling.common.view.ui.VoicePlayingIcon
android:id=
"@+id/voicePlay"
android:layout_width=
"76dp"
android:layout_height=
"76dp"
android:padding=
"24dp"
app:layout_constraintBottom_toBottomOf=
"parent"
app:layout_constraintLeft_toLeftOf=
"parent"
app:layout_constraintRight_toRightOf=
"parent"
app:layout_constraintTop_toTopOf=
"parent"
app:shape=
"@{1}"
app:shapeBg=
"@{0x99000000}"
/>
<View
android:id=
"@+id/vDisableClick"
android:layout_width=
"0dp"
...
...
@@ -117,14 +139,16 @@
<TextView
android:id=
"@+id/tvIntro"
android:layout_width=
"
wrap_content
"
android:layout_width=
"
0dp
"
android:layout_height=
"wrap_content"
android:layout_marginRight=
"40dp"
android:layout_marginBottom=
"8dp"
android:text=
"@{item.intro}"
android:textColor=
"@color/white"
android:textSize=
"15sp"
app:layout_constraintBottom_toTopOf=
"@+id/tvTag"
app:layout_constraintLeft_toLeftOf=
"@+id/tvTag"
app:layout_constraintRight_toLeftOf=
"@+id/btnShare"
tools:text=
"tag|tag|tag"
/>
<TextView
...
...
@@ -143,8 +167,9 @@
android:id=
"@+id/tvConfideCount"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:
textColor=
"@color/white
"
android:
layout_marginLeft=
"8dp
"
android:text=
"@{item.count}"
android:textColor=
"@color/white"
android:textSize=
"13sp"
app:layout_constraintBottom_toBottomOf=
"@+id/tvName"
app:layout_constraintLeft_toRightOf=
"@+id/tvName"
...
...
@@ -196,13 +221,18 @@
app:layout_constraintRight_toRightOf=
"parent"
/>
<ImageView
android:layout_width=
"4
6
dp"
android:layout_height=
"4
6
dp"
android:layout_width=
"4
8
dp"
android:layout_height=
"4
8
dp"
android:layout_marginRight=
"10dp"
android:layout_marginBottom=
"20dp"
android:padding=
"1dp"
app:circle=
"@{true}"
app:imageUrl=
"@{item.avatar}"
app:layout_constraintBottom_toTopOf=
"@+id/btnChat"
app:layout_constraintRight_toRightOf=
"parent"
/>
app:layout_constraintRight_toRightOf=
"parent"
app:shape=
"@{1}"
app:shapeBg=
"@{0x00FFFFFF}"
app:shapeStrokeColor=
"@{0xFFFFFFFF}"
app:shapeStrokeWidth=
"@{1}"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
ydl-utils/build.gradle
View file @
fd5ab45f
...
...
@@ -66,6 +66,7 @@ dependencies {
api
(
rootProject
.
ext
.
dependencies
[
"support-v4"
])
api
(
rootProject
.
ext
.
dependencies
[
"appcompat-v7"
])
api
(
rootProject
.
ext
.
dependencies
[
"design"
])
implementation
(
rootProject
.
ext
.
dependencies
[
"annotations"
])
implementation
(
rootProject
.
ext
.
dependencies
[
"arouter"
])
kapt
(
rootProject
.
ext
.
dependencies
[
"arouter-compiler"
])
compileOnly
(
rootProject
.
ext
.
dependencies
[
"systembartint"
])
...
...
ydl-utils/src/main/java/com/yidianling/common/view/ui/VoicePlayingIcon.java
0 → 100644
View file @
fd5ab45f
package
com
.
yidianling
.
common
.
view
.
ui
;
import
android.content.Context
;
import
android.content.res.TypedArray
;
import
android.graphics.Canvas
;
import
android.graphics.Color
;
import
android.graphics.Paint
;
import
android.graphics.RectF
;
import
android.util.AttributeSet
;
import
android.view.View
;
import
androidx.annotation.Nullable
;
import
com.yidianling.common.R
;
import
java.util.ArrayList
;
import
java.util.List
;
import
java.util.Random
;
public
class
VoicePlayingIcon
extends
View
{
//画笔
private
Paint
paint
;
//跳动指针的集合
private
List
<
Pointer
>
pointers
;
//跳动指针的数量
private
int
pointerNum
;
//逻辑坐标 原点
private
float
basePointX
;
private
float
basePointY
;
//指针间的间隙 默认5dp
private
float
pointerPadding
;
//每个指针的宽度 默认3dp
private
float
pointerWidth
;
//指针的颜色
private
int
pointerColor
=
Color
.
RED
;
//控制开始/停止
private
boolean
isPlaying
=
false
;
//子线程
private
Thread
myThread
;
//指针波动速率
private
int
pointerSpeed
;
private
Random
random
;
private
RectF
rectF
;
private
float
radius
;
public
VoicePlayingIcon
(
Context
context
)
{
super
(
context
);
init
();
}
public
VoicePlayingIcon
(
Context
context
,
@Nullable
AttributeSet
attrs
)
{
super
(
context
,
attrs
);
//取出自定义属性
TypedArray
ta
=
context
.
obtainStyledAttributes
(
attrs
,
R
.
styleable
.
VoicePlayingIcon
);
pointerColor
=
ta
.
getColor
(
R
.
styleable
.
VoicePlayingIcon_pointer_color
,
Color
.
WHITE
);
pointerNum
=
ta
.
getInt
(
R
.
styleable
.
VoicePlayingIcon_pointer_num
,
3
);
//指针的数量,默认为4
pointerWidth
=
dp2px
(
getContext
(),
ta
.
getFloat
(
R
.
styleable
.
VoicePlayingIcon_pointer_width
,
6
f
));
//指针的宽度,默认5dp
pointerSpeed
=
ta
.
getInt
(
R
.
styleable
.
VoicePlayingIcon_pointer_speed
,
40
);
ta
.
recycle
();
init
();
}
public
VoicePlayingIcon
(
Context
context
,
@Nullable
AttributeSet
attrs
,
int
defStyleAttr
)
{
super
(
context
,
attrs
,
defStyleAttr
);
TypedArray
ta
=
context
.
obtainStyledAttributes
(
attrs
,
R
.
styleable
.
VoicePlayingIcon
);
pointerColor
=
ta
.
getColor
(
R
.
styleable
.
VoicePlayingIcon_pointer_color
,
Color
.
RED
);
pointerNum
=
ta
.
getInt
(
R
.
styleable
.
VoicePlayingIcon_pointer_num
,
3
);
pointerWidth
=
dp2px
(
getContext
(),
ta
.
getFloat
(
R
.
styleable
.
VoicePlayingIcon_pointer_width
,
6
f
));
pointerSpeed
=
ta
.
getInt
(
R
.
styleable
.
VoicePlayingIcon_pointer_speed
,
40
);
ta
.
recycle
();
init
();
}
/**
* 初始化画笔与指针的集合
*/
private
void
init
()
{
paint
=
new
Paint
();
paint
.
setAntiAlias
(
true
);
paint
.
setColor
(
pointerColor
);
radius
=
pointerWidth
/
2
;
pointers
=
new
ArrayList
<>();
for
(
int
i
=
0
;
i
<
pointerNum
;
i
++)
{
pointers
.
add
(
new
Pointer
());
}
random
=
new
Random
();
rectF
=
new
RectF
();
}
@Override
protected
void
onLayout
(
boolean
changed
,
int
left
,
int
top
,
int
right
,
int
bottom
)
{
super
.
onLayout
(
changed
,
left
,
top
,
right
,
bottom
);
//获取逻辑原点的,也就是画布左下角的坐标。这里减去了paddingBottom的距离
basePointY
=
getHeight
()
-
getPaddingBottom
();
for
(
int
i
=
0
;
i
<
pointerNum
;
i
++)
{
//创建指针对象,利用0~1的随机数 乘以 可绘制区域的高度。作为每个指针的初始高度。
Pointer
pointer
=
pointers
.
get
(
i
);
if
(
pointer
!=
null
)
{
pointer
.
height
=
(
float
)
(
0.1
*
(
random
.
nextInt
(
10
)
+
1
)
*
(
getHeight
()
-
getPaddingBottom
()
-
getPaddingTop
()));
}
}
//计算每个指针之间的间隔 总宽度 - 左右两边的padding - 所有指针占去的宽度 然后再除以间隔的数量
pointerPadding
=
(
getWidth
()
-
getPaddingLeft
()
-
getPaddingRight
()
-
pointerWidth
*
pointerNum
)
/
(
pointerNum
-
1
);
}
@Override
protected
void
onDraw
(
Canvas
canvas
)
{
super
.
onDraw
(
canvas
);
//将x坐标移动到逻辑原点,也就是左下角
basePointX
=
0
f
+
getPaddingLeft
();
//循环绘制每一个指针。
for
(
int
i
=
0
;
i
<
pointers
.
size
();
i
++)
{
//绘制指针,也就是绘制矩形
rectF
.
set
(
basePointX
,
basePointY
-
pointers
.
get
(
i
).
height
,
basePointX
+
pointerWidth
,
basePointY
);
canvas
.
drawRoundRect
(
rectF
,
radius
,
radius
,
paint
);
basePointX
+=
(
pointerPadding
+
pointerWidth
);
}
}
/**
* 开始播放
*/
public
void
start
()
{
if
(!
isPlaying
)
{
if
(
myThread
==
null
)
{
//开启子线程
myThread
=
new
Thread
(
new
MyRunnable
());
myThread
.
start
();
}
isPlaying
=
true
;
//控制子线程中的循环
}
}
/**
* 停止子线程,并刷新画布
*/
public
void
stop
()
{
isPlaying
=
false
;
invalidate
();
}
@Override
protected
void
onDetachedFromWindow
()
{
super
.
onDetachedFromWindow
();
if
(
myThread
!=
null
)
{
myThread
.
interrupt
();
myThread
=
null
;
}
}
/**
* 子线程,循环改变每个指针的高度
*/
public
class
MyRunnable
implements
Runnable
{
@Override
public
void
run
()
{
for
(
float
i
=
0
;
i
<
Integer
.
MAX_VALUE
;
)
{
//创建一个死循环,每循环一次i+0.1
try
{
for
(
int
j
=
0
;
j
<
pointers
.
size
();
j
++)
{
//循环改变每个指针高度
float
rate
=
(
float
)
Math
.
abs
(
Math
.
sin
(
i
+
j
));
//利用正弦有规律的获取0~1的数。
pointers
.
get
(
j
).
height
=
(
basePointY
-
getPaddingTop
())
*
rate
;
//rate 乘以 可绘制高度,来改变每个指针的高度
}
Thread
.
sleep
(
pointerSpeed
);
//休眠一下下,可自行调节
if
(
isPlaying
)
{
//控制开始/暂停
postInvalidate
();
i
+=
0.1
;
}
}
catch
(
InterruptedException
e
)
{
//ignore
break
;
}
}
}
}
/**
* 指针对象
*/
private
static
class
Pointer
{
private
float
height
;
}
static
int
dp2px
(
Context
context
,
float
dipValue
)
{
final
float
scale
=
context
.
getResources
().
getDisplayMetrics
().
density
;
return
(
int
)
(
dipValue
*
scale
+
0.5f
);
}
}
ydl-utils/src/main/res/values/attrs.xml
View file @
fd5ab45f
...
...
@@ -318,4 +318,11 @@
<attr
name=
"ratingTitleVisible"
format=
"boolean"
/>
<attr
name=
"ratingCenterColor"
format=
"color"
/>
</declare-styleable>
<declare-styleable
name=
"VoicePlayingIcon"
>
<attr
name=
"pointer_color"
format=
"color"
/>
<attr
name=
"pointer_num"
format=
"integer"
/>
<attr
name=
"pointer_width"
format=
"float"
/>
<attr
name=
"pointer_speed"
format=
"integer"
/>
</declare-styleable>
</resources>
\ 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