Commit 6e1fa956 by 万齐军

Merge branch 'feat/qj/confide_2.0' into 'd/v_confide_2.0'

confide 2.0

See merge request app_android_lib/YDL-Component!17
parents eb8a5a47 59882ebd
/*___Generated_by_IDEA___*/
package com.ydl.component;
/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
public final class BuildConfig {
public final static boolean DEBUG = Boolean.parseBoolean(null);
}
\ No newline at end of file
/*___Generated_by_IDEA___*/
package com.ydl.component;
/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
public final class Manifest {
}
\ No newline at end of file
/*___Generated_by_IDEA___*/
package com.ydl.component;
/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
public final class R {
}
\ No newline at end of file
......@@ -6,7 +6,6 @@ import android.graphics.Color
import android.os.Build
import android.view.View
import android.view.WindowManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import com.alibaba.android.arouter.facade.annotation.Route
......@@ -36,16 +35,12 @@ import com.ydl.webview.NewH5Activity
import com.ydl.ydl_image.listener.YDLImageRecyclerOnScrollListener
import com.ydl.ydlcommon.base.config.HttpConfig.Companion.H5_URL
import com.ydl.ydlcommon.base.config.HttpConfig.Companion.YDL_H5
import com.ydl.ydlcommon.modular.ModularServiceManager
import com.ydl.ydlcommon.mvp.lce.BaseLceActivity
import com.ydl.ydlcommon.utils.BuryPointUtils
import com.ydl.ydlcommon.utils.LogUtil
import com.ydl.ydlcommon.utils.actionutil.ActionCountUtils
import com.ydl.ydlcommon.utils.remind.ToastHelper
import com.ydl.ydlcommon.view.DrawableRightTextView
import com.ydl.ydlcommon.view.WrapContentLinearLayoutManager
import com.yidianling.common.tools.RxImageTool
import com.yidianling.consultant.api.IConsultantService
import com.yidianling.im.api.service.IImService
import kotlinx.android.synthetic.main.confide_home_activity.*
import kotlinx.android.synthetic.main.confide_title_bar.*
......@@ -140,6 +135,7 @@ class ConfideHomeActivity : BaseLceActivity<IConfideHomeContract.View,IConfideHo
//重置页
page = 1
allFiltersBean!!.page = page
clearCacheData()
mPresenter.confideHomeRequest(true)
recommendList(page)
}
......@@ -329,9 +325,8 @@ class ConfideHomeActivity : BaseLceActivity<IConfideHomeContract.View,IConfideHo
override fun confideHomeResponse(list: MutableList<ConfideHomeDataBean>) {
initParam()
hideError()
updateCacheData(list)
cacheList?.addAll(0, list)
updateAdapter()
updateQuickConsultUrl()
setSuspendListener()
v_filterView.refreshView()
}
......@@ -350,18 +345,6 @@ class ConfideHomeActivity : BaseLceActivity<IConfideHomeContract.View,IConfideHo
}
/**
* 更新一键倾诉按钮url
*/
private fun updateQuickConsultUrl() {
//这边用try catch的原因是防止因为接口数据结构变化导致cacheList!![1].body!![1].linkUrl获取失败
quickConsultUrl = try {
cacheList!![1].body!![1].linkUrl
}catch (e: Exception) {
"https://h5.ydl.com/confideMatch"
}
}
/**
* 更新适配器数据
*/
private fun updateAdapter() {
......@@ -391,34 +374,6 @@ class ConfideHomeActivity : BaseLceActivity<IConfideHomeContract.View,IConfideHo
}
/**
* 更新缓存数据
*/
private fun updateCacheData(list: List<ConfideHomeDataBean>) {
clearCacheData()
var soundBean: ConfideHomeDataBean? = null
for (index in 0 until list.size) {
//将听声寻人数据取出 最后加入
when {
//听声寻人
list[index].type == IConfideHomeConfig.TYPE_SOUND -> soundBean = list[index]
//为你推荐
list[index].type == IConfideHomeConfig.TYPE_RECOMMEND -> {
//如果为你推荐的数量大于等于10条 就认为有下一页
hasMore = null != list[index].body && list[index].body!!.size >= 10
list[index].recommendId = page
(cacheList as ArrayList).add(list[index])
}
//其他模块正常添加
else -> (cacheList as ArrayList).add(list[index])
}
}
//听声寻人 放在最后添加
if (null != soundBean) {
(cacheList as ArrayList).add(soundBean)
}
}
/**
* 根据筛选条件 更新为你推荐数据
*/
private fun updateCacheDataByRecommend(bean: ConfideHomeDataBean) {
......
......@@ -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
......
......@@ -87,10 +87,6 @@ interface IConfideHomeContract{
interface Presenter : IPresenter<View> {
/**
* 加载本地缓存
*/
fun loadLocalData(context: Context)
/**
* 倾诉首页数据请求
*/
fun confideHomeRequest(isRefresh:Boolean = true)
......
......@@ -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
......@@ -154,6 +154,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()
}
......
......@@ -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
package com.ydl.confide.home.presenter
import android.content.Context
import android.text.TextUtils
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.ydl.confide.home.bean.ConfideHomeDataBean
import com.ydl.confide.home.contract.IConfideHomeContract
import com.ydl.confide.home.http.ConfideRecommendParam
import com.ydl.confide.home.model.ConfideHomeModelImpl
import com.ydl.confide.home.util.ConfideHomeUtils
import com.ydl.ydlcommon.data.http.RxUtils
import com.ydl.ydlcommon.data.http.ThrowableConsumer
import com.ydl.ydlcommon.mvp.base.BasePresenter
import com.ydl.ydlcommon.mvp.base.IView
import com.ydl.ydlcommon.utils.YDLAsyncUtils
import com.ydl.ydlcommon.utils.YDLCacheUtils
import com.yidianling.common.tools.RxNetTool
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.functions.Consumer
......@@ -26,127 +18,77 @@ import io.reactivex.functions.Consumer
* @Company 壹点灵
* @date 2018/8/10
*/
class ConfideHomePresenterImpl : BasePresenter<IConfideHomeContract.View,IConfideHomeContract.Model>(), IConfideHomeContract.Presenter{
class ConfideHomePresenterImpl : BasePresenter<IConfideHomeContract.View, IConfideHomeContract.Model>(),
IConfideHomeContract.Presenter {
override fun createModel(): IConfideHomeContract.Model {
return ConfideHomeModelImpl()
}
/**
* 加载本地缓存
*/
override fun loadLocalData(context: Context) {
YDLAsyncUtils.asyncAsResult(object : YDLAsyncUtils.AsyncObjecyerResult{
override fun doAsyncAction(): Any {
//读取缓存数据
var cacheData = YDLCacheUtils.getConfideHomeData()
if (cacheData.isEmpty()) {
cacheData = String(ConfideHomeUtils.getAssertsFile(context, "confide_home.json")!!)
}
return cacheData
}
override fun asyncResult(`object`: Any?) {
//如果没有缓存数据,显示加载框
if (`object` !is String || TextUtils.isEmpty(`object`)){
mView.showLoading()
}
if (`object` is String){
val gson = Gson()
val list = gson.fromJson<MutableList<ConfideHomeDataBean>>(`object`, object : TypeToken<MutableList<ConfideHomeDataBean>>() {
}.type)
if(null != list && !list.isEmpty()){
mView.confideHomeResponse(list)
}
}
confideHomeRequest(false)
}
})
}
/**
* 请求首页数据
*/
override fun confideHomeRequest(isRefresh:Boolean) {
if (!RxNetTool.isConnected(mView.getContext())){
override fun confideHomeRequest(isRefresh: Boolean) {
if (!RxNetTool.isConnected(mView.getContext())) {
mView.confideHomeRequestFail()
return
}
val disposable = mModel.confideHomeRequest()
.map { it }
.filter { it != null }
.compose(RxUtils.applySchedulers(mView as IView))
.doFinally { mView.hideLoading() }
.subscribe(Consumer {
mView.confideHomeResponse(it)
updateCache(it)
}, object : ThrowableConsumer() {
override fun accept(msg: String) {
mView.confideHomeRequestFail()
}
})
}
/**
* 更新本地缓存
*/
private fun updateCache(list : MutableList<ConfideHomeDataBean>){
YDLAsyncUtils.async(object : YDLAsyncUtils.AsyncObjecyer{
override fun doAsyncAction() {
var gson = Gson()
var json = gson.toJson(list)
YDLCacheUtils.saveConfideHomeData(json)
}
})
.map { it }
.filter { it != null }
.compose(RxUtils.applySchedulers(mView as IView))
.doFinally { mView.hideLoading() }
.subscribe(Consumer {
mView.confideHomeResponse(it)
}, object : ThrowableConsumer() {
override fun accept(msg: String) {
mView.confideHomeRequestFail()
}
})
}
/**
* 为你推荐列表(筛选调用)
*/
override fun recommendList(param : ConfideRecommendParam) {
if (!RxNetTool.isConnected(mView.getContext())){
override fun recommendList(param: ConfideRecommendParam) {
if (!RxNetTool.isConnected(mView.getContext())) {
mView.showError1("网络不给力")
return
}
val disposable = mModel.recommendList(param)
.map { it }
.filter { it != null }
.compose(RxUtils.applySchedulers(mView as IView))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer {
mView.recommendListResponse(it)
}, object : ThrowableConsumer() {
override fun accept(msg: String) {
mView.showError1(msg)
}
})
.map { it }
.filter { it != null }
.compose(RxUtils.applySchedulers(mView as IView))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer {
mView.recommendListResponse(it)
}, object : ThrowableConsumer() {
override fun accept(msg: String) {
mView.showError1(msg)
}
})
}
/**
* 为你推荐列表(翻页时调用)
*/
override fun recommendListMore(param: ConfideRecommendParam) {
if (!RxNetTool.isConnected(mView.getContext())){
if (!RxNetTool.isConnected(mView.getContext())) {
mView.showError1("网络不给力")
return
}
val disposable = mModel.recommendList(param)
.map { it }
.filter { it != null }
.compose(RxUtils.applySchedulers(mView as IView))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer {
mView.recommendListMoreResponse(it)
}, object : ThrowableConsumer() {
override fun accept(msg: String) {
mView.showError(msg)
mView.showError1(msg)
}
})
.map { it }
.filter { it != null }
.compose(RxUtils.applySchedulers(mView as IView))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer {
mView.recommendListMoreResponse(it)
}, object : ThrowableConsumer() {
override fun accept(msg: String) {
mView.showError(msg)
mView.showError1(msg)
}
})
}
}
\ No newline at end of file
package com.ydl.confide.home.util
import android.content.Context
import androidx.core.content.ContextCompat
import androidx.appcompat.widget.AppCompatTextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import android.text.TextUtils
import android.view.View
import android.view.animation.AnimationUtils
import android.view.animation.LinearInterpolator
import android.widget.ImageView
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.ydl.confide.R
import com.ydl.confide.home.adapter.ConfideHomeAdapter
import com.ydl.confide.home.bean.ConfideHomeAllFiltersBean
......@@ -84,32 +84,12 @@ class ConfideHomeUtils {
* 数据重组
*/
fun resetData(list: MutableList<ConfideHomeDataBean>, filterView: ConfideHomeFilterView, listScrollListener: ConfideHomeRecycleViewListener) {
val recommendList: MutableList<ConfideHomeDataBean> = ArrayList()
var soundDataBean: ConfideHomeDataBean? = null
for ((index, dataBean) in list.withIndex().reversed()) {
for ((index, dataBean) in list.withIndex()) {
//给隐藏的为你推荐筛选view赋值
if (dataBean.type == IConfideHomeConfig.TYPE_RECOMMEND_FILTER) {
listScrollListener.setFilterIndex(index)
filterView.initData(dataBean)
}
//拆分为你推荐数据
if (dataBean.type == IConfideHomeConfig.TYPE_RECOMMEND) {
//如果是为你推荐数据 则进行筛分
recommendList.addAll(getRecommendList(dataBean))
//移除当前 为你推荐数据
list.removeAt(index)
}
//移除听声寻人数据 放在最后
if (dataBean.type == IConfideHomeConfig.TYPE_SOUND) {
soundDataBean = ConfideHomeDataBean(dataBean.type, dataBean.body, dataBean.head, dataBean.footer, dataBean.recommendId)
list.removeAt(index)
}
}
//添加 拆分后的 为你推荐数据
list.addAll(recommendList)
//添加 听声寻人 数据
if (null != soundDataBean) {
list.add(soundDataBean)
}
}
......
......@@ -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
......@@ -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
......@@ -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(300, 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 = true
var isVideo = false
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
......
......@@ -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>
......
......@@ -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="46dp"
android:layout_height="46dp"
android:layout_width="48dp"
android:layout_height="48dp"
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>
/*___Generated_by_IDEA___*/
package com.ydl.ydlcommon;
/* This stub is only used by the IDE. It is NOT the BuildConfig class actually packed into the APK */
public final class BuildConfig {
public final static boolean DEBUG = Boolean.parseBoolean(null);
}
\ No newline at end of file
/*___Generated_by_IDEA___*/
package com.ydl.ydlcommon;
/* This stub is only used by the IDE. It is NOT the Manifest class actually packed into the APK */
public final class Manifest {
}
\ No newline at end of file
/*___Generated_by_IDEA___*/
package com.ydl.ydlcommon;
/* This stub is only used by the IDE. It is NOT the R class actually packed into the APK */
public final class R {
}
\ No newline at end of file
......@@ -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"])
......
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, 6f));//指针的宽度,默认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, 6f));
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 = 0f + 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);
}
}
......@@ -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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment