技能卡多选 增加声音秀

This commit is contained in:
yitao_hello
2022-02-27 11:38:14 +08:00
parent 1c14350117
commit 1d4932cf2e
96 changed files with 1943 additions and 392 deletions

View File

@@ -93,6 +93,7 @@ import com.yizhuan.erban.ui.im.chat.MsgViewHolderMatch;
import com.yizhuan.erban.ui.im.chat.MsgViewHolderOnline;
import com.yizhuan.erban.ui.im.chat.MsgViewHolderRedPackage;
import com.yizhuan.erban.ui.im.chat.MsgViewHolderRedPacket;
import com.yizhuan.erban.ui.im.chat.MsgViewHolderSkill;
import com.yizhuan.erban.ui.im.chat.MsgViewHolderText;
import com.yizhuan.erban.ui.im.chat.SignInNoticeMsgViewHolder;
import com.yizhuan.erban.ui.im.chat.SysMsgV2ViewHolder;
@@ -147,6 +148,7 @@ import com.yizhuan.xchat_android_core.im.custom.bean.OpenRoomNotiAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.OpenSignInAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.RedPackageAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.RedPacketAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.SkillMsgAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.SysMsgAttachment;
import com.yizhuan.xchat_android_core.im.custom.bean.SysMsgV2Attachment;
import com.yizhuan.xchat_android_core.initial.InitialModel;
@@ -414,6 +416,9 @@ public class MainActivity extends BaseMvpActivity<IMainView, MainPresenter>
NimUIKit.registerMsgItemViewHolder(ChatHintAttachment.class, MsgViewHolderChatHint.class);
NimUIKit.registerMsgItemViewHolder(MatchAttachment.class, MsgViewHolderMatch.class);
//技能卡
NimUIKit.registerMsgItemViewHolder(SkillMsgAttachment.class, MsgViewHolderSkill.class);
NimUIKit.setSessionListener(listener);
NimUIKit.setContactEventListener(listener1);
}

View File

@@ -0,0 +1,103 @@
package com.yizhuan.erban.skill
import com.yizhuan.erban.skill.widget.ItemAttribute
import com.yizhuan.erban.skill.widget.ItemEventListener
import com.yizhuan.erban.skill.widget.SkillAttribute
import com.yizhuan.erban.skill.widget.SkillItem
import com.yizhuan.xchat_android_core.skill.entity.*
object SKillDataParser {
private fun parseItemRecordToAttribute(
cardId: Int,
isSelf: Boolean,
isEdit: Boolean,
itemEventListener: ItemEventListener? = null,
item: PropRecordVoEntity,
audioStatus: Int = 0
): ItemAttribute {
return ItemAttribute(
cardId,
isSelf, isEdit,
item.state, item.isMust,
item.parentId, item.parentVal,
itemEventListener,
item.refPropVos.toMutableList(),
).apply {
this.audioStatus = audioStatus
}
}
fun parseSkillRecordToAttribute(
item: SkillRecordEntity,
itemEventListener: ItemEventListener? = null,
): SkillAttribute {
val list: MutableList<ItemAttribute> = ArrayList()
item.propRecordVo.forEach {
val element = parseItemRecordToAttribute(
item.cardId,
item.isSelf,
item.isEdit,
itemEventListener,
it
).apply {
audioStatus = item.audioStatus
}
list.add(element)
}
return SkillAttribute(
item.id, item.type, item.cardId,
item.icon, item.name,
item.isSelf, item.isEdit,
item.pic, list
)
}
fun parseSkillPropertyToAttribute(
item: SkillPropertyEntity,
itemEventListener: ItemEventListener? = null,
): SkillAttribute {
val list: MutableList<ItemAttribute> = ArrayList()
item.props.forEach {
val element = parseItemRecordToAttribute(
item.cardId,
item.isSelf,
item.isEdit,
itemEventListener,
it.parsePropToRecord()
)
list.add(element)
}
return SkillAttribute(
-1, item.type, item.cardId,
item.icon, item.name,
item.isSelf, item.isEdit,
item.pic, list
)
}
/**
* 转换成服务器数据
*/
fun parseSkillSelectedValue(
recordId: Int,
cardId: Int,
itemList: List<SkillItem>
): SkillPostServerEntity {
return if (itemList.isEmpty()) {
SkillPostServerEntity(propRecordVo = ArrayList(0))
} else {
val list = ArrayList<SKillValueEntity>()
itemList.forEach {
val item = it.getContentEntity()
list.add(
SKillValueEntity(
item.parentId,
it.getContentEntity().selectedProperties
)
)
}
SkillPostServerEntity(recordId, cardId, list)
}
}
}

View File

@@ -0,0 +1,201 @@
package com.yizhuan.erban.skill
import android.annotation.SuppressLint
import com.yizhuan.erban.base.BaseActivity
import com.yizhuan.erban.skill.dialog.SkillSelectionDialog
import com.yizhuan.erban.skill.repository.SkillDataManager
import com.yizhuan.erban.skill.repository.SkillModel
import com.yizhuan.erban.skill.widget.*
import com.yizhuan.xchat_android_core.file.FileModel
import com.yizhuan.xchat_android_core.skill.entity.PropRefEntity
import com.yizhuan.xchat_android_core.skill.entity.PropsEntity
import com.yizhuan.xchat_android_core.skill.entity.SkillPostServerEntity
import com.yizhuan.xchat_android_core.skill.entity.SkillPropertyEntity
import com.yizhuan.xchat_android_core.skill.event.SkillEvent
import com.yizhuan.xchat_android_core.utils.toast
import org.greenrobot.eventbus.EventBus
import java.io.File
class SkillDataDelegate(private val skillView: SkillCardView, private val activity: BaseActivity) :
ItemEventListener {
@SuppressLint("CheckResult")
override fun onItemClick(item: SkillItem) {
onItemSelection(item)
}
override fun onRecordSuccess(audioFile: File?, duration: Int) {
onRecordAudioSuccess(audioFile, duration)
}
override fun onDeleteRecordClick() {
deleteSkill(false)
}
/**
* 上传音频文件
*/
private fun onRecordAudioSuccess(audioFile: File?, duration: Int) {
val sourceItem =
skillView.getItems().find { it.getContentEntity().state == ItemAttribute.STATE_AUDIO }!!
val durationItem =
skillView.getItems()
.find { it.getContentEntity().state == ItemAttribute.STATE_DURATION }!!
with(durationItem.getContentEntity()) {
if (selectedProperties.isEmpty()) {
val entity = PropRefEntity(parentId, duration.toString())
selectedProperties.add(entity)
} else {
selectedProperties[0].propVal = duration.toString()
}
}
audioFile?.absolutePath?.let {
activity.dialogManager.showProgressDialog(activity)
FileModel.get()
.uploadFile(audioFile.absolutePath)
.flatMap {
with(sourceItem.getContentEntity()) {
if (selectedProperties.isEmpty()) {
val entity = PropRefEntity(parentId, it)
selectedProperties.add(entity)
} else {
selectedProperties[0].propVal = it
}
}
SkillModel.instance.saveSkillInfo(
SKillDataParser.parseSkillSelectedValue(
skillView.getAttributes().id,
skillView.getAttributes().cardId,
skillView.getItems()
)
)
}.compose(activity.bindToLifecycle())
.subscribe({
activity.dialogManager.dismissDialog()
EventBus.getDefault().post(SkillEvent())
if (skillView.getAttributes().id == -1) {
activity.finish()
}
}, {
activity.dialogManager.dismissDialog()
"上传失败,请重新录制".toast()
(sourceItem as RecordIResourceItem).setItemByState(RecordIResourceItem.RECORD_STATE_READY)
})
}
}
/**
* 选项
*/
@SuppressLint("CheckResult")
private fun onItemSelection(item: SkillItem) {
val propertyEntity =
SkillDataManager.get().getPropertyEntity(item.getContentEntity().cardId)
if (propertyEntity == null) {
SkillModel.instance.getCardInfoById(item.getContentEntity().cardId)
.compose(activity.bindToLifecycle())
.doOnSubscribe { activity.dialogManager.showProgressDialog(activity) }
.subscribe({
SkillDataManager.get()
.setSkillPropertyEntity(item.getContentEntity().cardId, it)
showSelectionDialog(item, it)
activity.dialogManager.dismissDialog()
}, { th ->
th.printStackTrace()
activity.toast(th.message)
showSelectionDialog(item, null)
activity.dialogManager.dismissDialog()
})
} else {
showSelectionDialog(item, propertyEntity)
}
}
private fun showSelectionValueDialog(item: SkillItem, propertyEntity: PropsEntity?) {
val selectionDialog = SkillSelectionDialog(activity, propertyEntity) {
item.getContentEntity().selectedProperties.clear()
it.forEach { element -> item.getContentEntity().selectedProperties.add(element.parseToRecord()) }
item.invalidate()
}
selectionDialog.openDialog()
}
/**
* 显示选择对话框
*/
private fun showSelectionDialog(item: SkillItem, propertyEntity: SkillPropertyEntity?) {
val props = propertyEntity?.props
val propDictVos = props?.find { it.id == item.getContentEntity().parentId }?.apply {
propDictVos?.forEach { prop ->
//清空已选项
prop.isSelected = false
//设置之前保存的选项,从服务器拉下来的数据
val hasSelectBefore =
item.getContentEntity().selectedProperties.find { record -> record.propId == prop.id }
if (hasSelectBefore != null) {
//表示这个是上次存在服务器的值 设置为true
prop.isSelected = true
}
}
}
showSelectionValueDialog(item, propDictVos)
}
@SuppressLint("CheckResult")
internal fun saveSkill() {
if (!skillView.isValid()) {
activity.toast("请填写或选择带*条目的内容")
return
}
val item = parseSelectedValues()
SkillModel.instance.saveSkillInfo(item).compose(activity.bindToLifecycle())
.doOnSubscribe { activity.dialogManager.showProgressDialog(activity) }
.subscribe(
{
activity.dialogManager.dismissDialog()
val event = SkillEvent()
event.event = SkillEvent.ADD
EventBus.getDefault().post(event)
activity.finish()
},
{ th ->
th.printStackTrace()
activity.toast(th.message)
activity.dialogManager.dismissDialog()
})
}
/**
* 获取SkillView数据转换成提交服务器数据
*/
private fun parseSelectedValues(): SkillPostServerEntity {
val itemList = skillView.getItems()
val skillAttr = skillView.getAttributes()
return SKillDataParser.parseSkillSelectedValue(skillAttr.id, skillAttr.cardId, itemList)
}
/**
* 删除技能卡
*/
@SuppressLint("CheckResult")
fun deleteSkill(needFinishCurrent: Boolean) {
activity.dialogManager.showProgressDialog(activity)
SkillModel.instance.deleteSkill(skillView.getAttributes().id)
.compose(activity.bindToLifecycle())
.subscribe({
activity.toast("删除成功")
val event = SkillEvent()
event.event = SkillEvent.REMOVE
EventBus.getDefault().post(event)
activity.dialogManager.dismissDialog()
if (needFinishCurrent)
activity.finish()
}, { th ->
th.printStackTrace()
activity.toast(th.message)
})
}
}

View File

@@ -1,5 +1,6 @@
package com.yizhuan.erban.skill.activity
import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
@@ -8,22 +9,23 @@ import com.netease.nim.uikit.StatusBarUtil
import com.yizhuan.erban.R
import com.yizhuan.erban.base.BaseBindingActivity
import com.yizhuan.erban.databinding.ActivitySkillEditBinding
import com.yizhuan.xchat_android_core.skill.entity.PropRecordVoEntity
import com.yizhuan.xchat_android_core.skill.entity.SkillPropertyEntity
import com.yizhuan.xchat_android_core.skill.entity.SkillRecordEntity
import com.yizhuan.erban.skill.repository.SkillDataManager
import com.yizhuan.erban.skill.repository.SkillModel
import com.yizhuan.erban.skill.widget.CARD_TYPE_AUDIO
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
@ActLayoutRes(R.layout.activity_skill_edit)
class AddSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
private lateinit var delegate: SkillEditableDelegate
private var cardId = -1
private var cardType = -1
override fun init() {
delegate = SkillEditableDelegate(this)
initTitleBar("添加技能")
mBinding.click = this
mBinding.btnDelete.visibility = View.GONE
val cardId = intent?.getIntExtra(ITEM, -1) ?: -1
cardId = intent?.getIntExtra(CARD_ID, cardId) ?: cardId
if (cardId > 0) {
queryCardInfo(cardId)
}
@@ -33,34 +35,52 @@ class AddSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
private fun queryCardInfo(cardId: Int) {
val propertyEntity = SkillDataManager.get().getPropertyEntity(cardId)
if (propertyEntity != null) {
if (propertyEntity.type == CARD_TYPE_AUDIO) {
dealAudioView(propertyEntity)
}
setSkillViewData(propertyEntity)
} else {
SkillModel.instance.getCardInfoById(cardId)
.compose(bindToLifecycle())
.subscribe( {
.subscribe({
SkillDataManager.get().setSkillPropertyEntity(cardId, it)
setSkillViewData(it)
}, { th -> th.printStackTrace()
when (it.type) {
CARD_TYPE_AUDIO -> dealAudioView(it)
else -> setSkillViewData(it)
}
}, { th ->
th.printStackTrace()
toast(th.message)
})
}
}
private fun dealAudioView(propertyEntity: SkillPropertyEntity) {
checkPermissionAndDeal(propertyEntity)
mBinding.btnSave.visibility = View.GONE
cardType = propertyEntity.type
}
private fun setSkillViewData(it: SkillPropertyEntity) {
//转换
val propList = ArrayList<PropRecordVoEntity>()
it.props.forEach { prop ->
propList.add(
PropRecordVoEntity(
prop.id, prop.propVal, -1, null, prop.state, prop.isMust
)
)
}
val entity = SkillRecordEntity(
it.cardId, null, it.icon, -1, it.name,
it.pic, propList, it.type, null
it.isSelf = true
it.isEdit = true
delegate.setSkillViewData(it)
}
@SuppressLint("CheckResult")
private fun checkPermissionAndDeal(it: SkillPropertyEntity) {
checkPermission(
Manifest.permission.RECORD_AUDIO,
Manifest.permission.WRITE_EXTERNAL_STORAGE
)
delegate.setSkillViewData(isSelf = true, isEdit = true, entity = entity)
.subscribe { aBoolean: Boolean ->
if (aBoolean) {
setSkillViewData(it)
} else {
toast("请给予应用必要权限,让程序可正常工作。")
finish()
}
}
}
@@ -68,28 +88,31 @@ class AddSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
delegate.onLeftClick()
}
override fun onClick(v: View?) {
super.onClick(v)
when (v?.id) {
R.id.btn_save -> delegate.saveSkill(mBinding)
R.id.btn_save -> delegate.saveSkill()
}
}
companion object {
private const val ITEM = "item"
fun start(context: Context, type: Int) {
private const val CARD_ID = "card_id"
fun start(context: Context, cardId: Int) {
val intent = Intent(context, AddSkillActivity::class.java)
intent.putExtra(ITEM, type)
intent.putExtra(CARD_ID, cardId)
context.startActivity(intent)
}
}
override fun onBackPressed() {
if (cardType == CARD_TYPE_AUDIO) {
super.onBackPressed()
return
}
delegate.onLeftClick()
}
override fun needSteepStateBar()=true
override fun needSteepStateBar() = true
override fun setStatusBar() {
super.setStatusBar()
StatusBarUtil.transparencyBar(this)

View File

@@ -8,11 +8,9 @@ import com.netease.nim.uikit.StatusBarUtil
import com.yizhuan.erban.R
import com.yizhuan.erban.base.BaseBindingActivity
import com.yizhuan.erban.databinding.ActivitySkillEditBinding
import com.yizhuan.xchat_android_core.skill.event.SkillEvent
import com.yizhuan.erban.skill.repository.SkillModel
import com.yizhuan.erban.ui.widget.dialog.CommonTipDialog
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
import org.greenrobot.eventbus.EventBus
@ActLayoutRes(R.layout.activity_skill_edit)
class EditSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
@@ -33,7 +31,7 @@ class EditSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
when (v.id) {
R.id.btn_delete -> showDeleteDialog()
R.id.btn_save -> {
mDelegate.saveSkill(mBinding)
mDelegate.saveSkill()
}
}
}
@@ -48,7 +46,7 @@ class EditSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
deleteTipDialog?.setTipMsg(resources.getString(R.string.tip_delete_skill))
deleteTipDialog?.setOnActionListener(object : CommonTipDialog.OnActionListener {
override fun onOk() {
deleteSkill(mBinding.skillView.getSavedValues().id)
mDelegate.deleteSkill()
deleteTipDialog?.dismiss()
}
@@ -63,32 +61,18 @@ class EditSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
deleteTipDialog?.show()
}
@SuppressLint("CheckResult")
private fun deleteSkill(id: Int) {
SkillModel.instance.deleteSkill(id)
.compose(bindToLifecycle())
.subscribe( {
toast("删除成功")
val event = SkillEvent()
event.event = SkillEvent.REMOVE
EventBus.getDefault().post(event)
finish()
}, { th ->
th.printStackTrace()
toast(th.message)
})
}
@SuppressLint("CheckResult")
private fun loadData() {
SkillModel.instance.getSkillDetailById(recordId)
.compose(bindToLifecycle())
.doOnSubscribe { dialogManager.showProgressDialog(this) }
.subscribe( {
mDelegate.setSkillViewData(isSelf = true, isEdit = true, entity = it)
.subscribe({
it.isSelf = true
it.isEdit = true
mDelegate.setSkillViewData(it)
dialogManager.dismissDialog()
}, { th ->
}, { th ->
th.printStackTrace()
toast(th.message)
dialogManager.dismissDialog()
@@ -107,7 +91,8 @@ class EditSkillActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
override fun onBackPressed() {
mDelegate.onLeftClick()
}
override fun needSteepStateBar()=true
override fun needSteepStateBar() = true
override fun setStatusBar() {
super.setStatusBar()
StatusBarUtil.transparencyBar(this)

View File

@@ -34,10 +34,12 @@ class SkillDetailActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
SkillModel.instance.getSkillDetailById(recordId)
.compose(bindToLifecycle())
.doOnSubscribe { dialogManager.showProgressDialog(this) }
.subscribe( {
mDelegate.setSkillViewData(isSelf = false, isEdit = false, entity = it)
.subscribe({
it.isSelf = false
it.isEdit = false
mDelegate.setSkillViewData(it)
dialogManager.dismissDialog()
}, { th ->
}, { th ->
th.printStackTrace()
toast(th.message)
dialogManager.dismissDialog()
@@ -52,7 +54,8 @@ class SkillDetailActivity : BaseBindingActivity<ActivitySkillEditBinding>() {
context.startActivity(intent)
}
}
override fun needSteepStateBar()=true
override fun needSteepStateBar() = true
override fun setStatusBar() {
super.setStatusBar()
StatusBarUtil.transparencyBar(this)

View File

@@ -1,20 +1,12 @@
package com.yizhuan.erban.skill.activity
import android.annotation.SuppressLint
import com.yizhuan.erban.R
import com.yizhuan.erban.base.BaseBindingActivity
import com.yizhuan.erban.databinding.ActivitySkillEditBinding
import com.yizhuan.erban.skill.dialog.SkillSelectionDialog
import com.yizhuan.xchat_android_core.skill.entity.PropsEntity
import com.yizhuan.xchat_android_core.skill.entity.SkillPropertyEntity
import com.yizhuan.xchat_android_core.skill.entity.SkillRecordEntity
import com.yizhuan.xchat_android_core.skill.event.SkillEvent
import com.yizhuan.erban.skill.repository.SkillDataManager
import com.yizhuan.erban.skill.repository.SkillModel
import com.yizhuan.erban.skill.widget.SelectionItem
import com.yizhuan.erban.skill.widget.SkillItem
import com.yizhuan.erban.skill.SKillDataParser
import com.yizhuan.erban.skill.SkillDataDelegate
import com.yizhuan.erban.ui.widget.dialog.CommonTipDialog
import org.greenrobot.eventbus.EventBus
import com.yizhuan.xchat_android_core.skill.entity.*
/**
* 编辑-添加 界面
@@ -52,78 +44,42 @@ class SkillEditableDelegate(
saveTipDialog.show()
}
private fun showSelectionValueDialog(item: SkillItem, propertyEntity: PropsEntity?) {
val selectionDialog = SkillSelectionDialog(activity, propertyEntity) {
item.getContentEntity().propId = it.id
item.getContentEntity().propVal = it.propVal
item.invalidate()
}
selectionDialog.openDialog()
/**
* 保存 添加
*/
internal fun saveSkill() {
SkillDataDelegate(activity.mBinding.skillView, activity).saveSkill()
}
@SuppressLint("CheckResult")
internal fun saveSkill(binding: ActivitySkillEditBinding) {
if (!binding.skillView.isValid()) {
activity.toast("请填写或选择带*条目的内容")
return
}
val item = binding.skillView.getSavedValues()
SkillModel.instance.saveSkillInfo(item).compose(activity.bindToLifecycle())
.doOnSubscribe { activity.dialogManager.showProgressDialog(activity) }
.subscribe(
{
activity.dialogManager.dismissDialog()
val event = SkillEvent()
event.event = SkillEvent.ADD
EventBus.getDefault().post(event)
activity.finish()
},
{ th ->
th.printStackTrace()
activity.toast(th.message)
activity.dialogManager.dismissDialog()
})
/**
* 删除
*/
internal fun deleteSkill() {
SkillDataDelegate(activity.mBinding.skillView, activity).deleteSkill(true)
}
internal fun setSkillViewData(isSelf: Boolean, isEdit: Boolean, entity: SkillRecordEntity) {
entity.isSelf = isSelf
entity.isEdit = isEdit
val listener = if (isSelf && isEdit) DefaultItemSelectionListenerImp(entity) else null
activity.mBinding.skillView.setItems(entity, listener)
/**
* 记录
*/
internal fun setSkillViewData(entity: SkillRecordEntity) {
val onItemCall =
if (entity.isEdit && entity.isSelf)
SkillDataDelegate(activity.mBinding.skillView, activity)
else null
val attr = SKillDataParser.parseSkillRecordToAttribute(entity, onItemCall)
activity.mBinding.skillView.initView(attr)
}
private fun setSelectionData(item: SkillItem, propertyEntity: SkillPropertyEntity?) {
val props = propertyEntity?.props
val propDictVos = props?.find {
it.id == item.getContentEntity().parentId
}
showSelectionValueDialog(item, propDictVos)
}
inner class DefaultItemSelectionListenerImp(private val entity: SkillRecordEntity) :
SelectionItem.OnItemSelectionListener {
@SuppressLint("CheckResult")
override fun onItemSelection(item: SkillItem) {
val propertyEntity = SkillDataManager.get().getPropertyEntity(entity.cardId)
if (propertyEntity == null) {
SkillModel.instance.getCardInfoById(entity.cardId)
.compose(activity.bindToLifecycle())
.doOnSubscribe { activity.dialogManager.showProgressDialog(activity) }
.subscribe( {
SkillDataManager.get().setSkillPropertyEntity(entity.cardId, it)
setSelectionData(item, it)
activity.dialogManager.dismissDialog()
}, { th ->
th.printStackTrace()
activity.toast(th.message)
setSelectionData(item, null)
activity.dialogManager.dismissDialog()
})
} else {
setSelectionData(item, propertyEntity)
}
}
/**
* 初值属性
*/
internal fun setSkillViewData(entity: SkillPropertyEntity) {
val onItemCall = if (entity.isEdit && entity.isSelf) SkillDataDelegate(
activity.mBinding.skillView,
activity
) else null
val attr = SKillDataParser.parseSkillPropertyToAttribute(entity, onItemCall)
activity.mBinding.skillView.initView(attr)
}
companion object {

View File

@@ -14,12 +14,15 @@ import com.yizhuan.erban.R
import com.yizhuan.erban.base.BaseBindingActivity
import com.yizhuan.erban.common.EmptyViewHelper
import com.yizhuan.erban.databinding.ActivitySkillHomeBinding
import com.yizhuan.erban.skill.SKillDataParser
import com.yizhuan.erban.skill.adapter.MineSkillCardAdapter
import com.yizhuan.erban.skill.decoration.SkillLinearVerticalDecoration
import com.yizhuan.erban.skill.dialog.AddSkillCardDialog
import com.yizhuan.xchat_android_core.skill.event.SkillEvent
import com.yizhuan.erban.skill.repository.SkillDataManager
import com.yizhuan.erban.skill.repository.SkillModel
import com.yizhuan.erban.skill.widget.CARD_TYPE_AUDIO
import com.yizhuan.erban.skill.widget.SkillAttribute
import com.yizhuan.erban.ui.widget.magicindicator.buildins.UIUtil
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
import org.greenrobot.eventbus.EventBus
@@ -39,11 +42,13 @@ class SkillHomeActivity : BaseBindingActivity<ActivitySkillHomeBinding>() {
userId = intent.getLongExtra(USER_ID, 0)
EventBus.getDefault().register(this)
mBinding.recyclerView.layoutManager = LinearLayoutManager(this)
adapter = MineSkillCardAdapter(pageType ==PAGE_TYPE_SELF)
adapter = MineSkillCardAdapter(pageType == PAGE_TYPE_SELF, this)
mBinding.recyclerView.adapter = adapter
mBinding.recyclerView.addItemDecoration(SkillLinearVerticalDecoration(this, 6, 16))
adapter.setOnItemClickListener { _, _, position ->
if (pageType != PAGE_TYPE_SELF) return@setOnItemClickListener
val item = adapter.getItem(position) ?: return@setOnItemClickListener
if (item == null || item.type == CARD_TYPE_AUDIO) return@setOnItemClickListener
adapter.getItem(position)?.let {
EditSkillActivity.start(this, it.id)
}
@@ -54,6 +59,7 @@ class SkillHomeActivity : BaseBindingActivity<ActivitySkillHomeBinding>() {
mBinding.refreshLayout.setOnRefreshListener {
loadUserSkillList(userId)
}
mBinding.refreshLayout.isEnabled=false//禁用下拉刷新
loadUserSkillList(userId)
}
@@ -163,7 +169,8 @@ class SkillHomeActivity : BaseBindingActivity<ActivitySkillHomeBinding>() {
fun onDataChangedEvent(event: SkillEvent) {
loadUserSkillList(userId)
}
override fun needSteepStateBar()=true
override fun needSteepStateBar() = true
override fun setStatusBar() {
super.setStatusBar()
StatusBarUtil.transparencyBar(this)

View File

@@ -3,18 +3,36 @@ package com.yizhuan.erban.skill.adapter
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.BaseViewHolder
import com.yizhuan.erban.R
import com.yizhuan.erban.base.BaseActivity
import com.yizhuan.erban.skill.SKillDataParser
import com.yizhuan.erban.skill.SkillDataDelegate
import com.yizhuan.erban.skill.widget.CARD_TYPE_AUDIO
import com.yizhuan.xchat_android_core.skill.entity.SkillRecordEntity
import com.yizhuan.erban.skill.widget.SkillCardView
/**
* 用户所有技能卡 显示
*/
class MineSkillCardAdapter(private val isSelf: Boolean) :
class MineSkillCardAdapter(private val isSelf: Boolean, private val activity: BaseActivity) :
BaseQuickAdapter<SkillRecordEntity, BaseViewHolder>(R.layout.item_mine_skill_card) {
override fun convert(helper: BaseViewHolder, item: SkillRecordEntity) {
val itemView = helper.itemView as SkillCardView
item.isSelf = isSelf
item.isEdit = false
itemView.setItems(item, null)
when (item.type) {
CARD_TYPE_AUDIO -> {
//声音秀没有编辑选项
item.isSelf = false
itemView.initView(
SKillDataParser.parseSkillRecordToAttribute(
item,
SkillDataDelegate(itemView, activity)
)
)
}
else -> {
item.isSelf = isSelf
itemView.initView(SKillDataParser.parseSkillRecordToAttribute(item))
}
}
}
}

View File

@@ -5,30 +5,99 @@ import android.graphics.Color
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.BaseViewHolder
import com.yizhuan.erban.R
import com.yizhuan.xchat_android_core.skill.entity.PropDictVosEntity
import com.yizhuan.xchat_android_core.skill.entity.PropDictVo
import com.yizhuan.xchat_android_core.utils.toast
class SkillSelectionAdapter(private val context: Context) :
BaseQuickAdapter<PropDictVosEntity, BaseViewHolder>(R.layout.item_skill_selection) {
private var selectedPosition = -1
override fun convert(helper: BaseViewHolder, item: PropDictVosEntity) {
class SkillSelectionAdapter(
private val context: Context,
private val itemState: Int,
private var maxSelectNumber: Int
) :
BaseQuickAdapter<PropDictVo, BaseViewHolder>(R.layout.item_skill_selection) {
init {
//-1 不限制
maxSelectNumber =
if (maxSelectNumber == 0 || maxSelectNumber == -1) Int.MAX_VALUE
else maxSelectNumber
}
private var refreshByUser = false//标志是否是用户选择后刷新Adapter在用户选择后设置为true
private var selectedCount = 0
override fun convert(helper: BaseViewHolder, item: PropDictVo) {
helper.setText(R.id.tv_item, item.propVal)
val position = helper.adapterPosition
helper.setBackgroundRes(
R.id.tv_item,
if (selectedPosition == position) R.drawable.bg_round_ffbc51_8
if (item.isSelected) R.drawable.bg_round_ffbc51_8
else R.drawable.bg_f1f1fa_8
)
helper.setTextColor(
R.id.tv_item,
if (selectedPosition == position) context.resources.getColor(R.color.color_333333)
else Color.parseColor("#FFBC51")
if (item.isSelected) Color.parseColor("#FFBC51")
else context.resources.getColor(R.color.color_333333)
)
if (item.isSelected && !refreshByUser) {
selectedCount++
}
}
fun select(position: Int) {
if (position == selectedPosition) return
selectedPosition = position
refreshByUser = true
getItem(position)?.let {
when (itemState) {
//0 单选 1 输入 2多选 3 音频 4 音频时长
0 -> singleSelect(it)
2 -> multiSelect(it)
}
}
notifyDataSetChanged()
}
fun getSelectedItems(): List<PropDictVo> {
return data.filter { it.isSelected }
}
private fun singleSelect(item: PropDictVo) {
data.forEach { it.isSelected = false }
selectedCount = 1
item.isSelected = true
}
private fun multiSelect(item: PropDictVo) {
item.isSelected = !item.isSelected
when (item.refIsOnlyCheck) {
0 -> {
when (item.isSelected) {
false -> {
selectedCount--
}
true -> {
selectedCount++
if (selectedCount > maxSelectNumber) {
"最多只能选择${maxSelectNumber}".toast()
selectedCount--
item.isSelected = false
return
} else {
data.find { e -> e.refIsOnlyCheck == 1 }?.let {
if (it.isSelected) {
it.isSelected = false
selectedCount--
}
}
}
}
}
}
1 -> {
if (item.isSelected) {
data.forEach { that -> that.isSelected = false }
item.isSelected = true
selectedCount = 1
} else {
selectedCount = 0
}
}
}
}
}

View File

@@ -2,6 +2,7 @@ package com.yizhuan.erban.skill.dialog
import android.content.Context
import android.view.Gravity
import android.view.View
import androidx.recyclerview.widget.GridLayoutManager
import com.yizhuan.erban.R
import com.yizhuan.erban.databinding.DialogAddSkillItemBinding
@@ -30,6 +31,7 @@ class AddSkillCardDialog(
private lateinit var adapter: AddSkillCardAdapter
override fun init() {
binding.btnEnsure.visibility = View.GONE
binding.recyclerView.layoutManager = GridLayoutManager(context, 2)
binding.recyclerView.addItemDecoration(SkillGridDecoration(context, 26, 15))
adapter = AddSkillCardAdapter()

View File

@@ -8,16 +8,16 @@ import com.yizhuan.erban.R
import com.yizhuan.erban.databinding.DialogAddSkillItemBinding
import com.yizhuan.erban.skill.adapter.SkillSelectionAdapter
import com.yizhuan.erban.skill.decoration.SkillGridDecoration
import com.yizhuan.xchat_android_core.skill.entity.PropDictVosEntity
import com.yizhuan.xchat_android_core.skill.entity.PropsEntity
import com.yizhuan.erban.treasure_box.widget.dialog.BaseBindingDialog
import com.yizhuan.erban.ui.widget.magicindicator.buildins.UIUtil
import com.yizhuan.xchat_android_core.skill.entity.PropDictVo
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
@ActLayoutRes(R.layout.dialog_add_skill_item)
class SkillSelectionDialog(
context: Context, var item: PropsEntity?,
val callback: (value: PropDictVosEntity) -> Unit
val callback: (value: List<PropDictVo>) -> Unit
) :
BaseBindingDialog<DialogAddSkillItemBinding>(context) {
private lateinit var adapter: SkillSelectionAdapter
@@ -26,16 +26,18 @@ class SkillSelectionDialog(
height = UIUtil.dip2px(context, 406.0)
gravity = Gravity.BOTTOM
binding.tvTitle.visibility = View.VISIBLE
binding.tvTitle.text = item?.propVal?:""
adapter = SkillSelectionAdapter(context)
binding.tvTitle.text = item?.propVal ?: ""
adapter = SkillSelectionAdapter(context, item?.state ?: 0,item?.checkLimitNum?:-1)
adapter.setOnItemClickListener { _, _, position ->
adapter.select(position)
callback(adapter.getItem(position)!!)
closeDialog()
}
binding.recyclerView.layoutManager = GridLayoutManager(context, 2)
binding.recyclerView.addItemDecoration(SkillGridDecoration(context, 30, 15))
binding.recyclerView.adapter = adapter
adapter.setNewData(item?.propDictVos)
binding.btnEnsure.setOnClickListener {
callback.invoke(adapter.getSelectedItems())
closeDialog()
}
}
}

View File

@@ -1,35 +1,55 @@
package com.yizhuan.erban.skill.widget
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import android.widget.TextView
import com.yizhuan.erban.R
import com.yizhuan.xchat_android_core.skill.entity.PropRecordVoEntity
import com.yizhuan.xchat_android_core.skill.entity.PropRefEntity
import com.yizhuan.xchat_android_core.utils.TextUtils
class EditItem(
private val isSelf: Boolean,
private val isEditable: Boolean,
entity: PropRecordVoEntity,
context: Context
) :
SkillItem(entity, context) {
class EditItem(private val itemAttr: ItemAttribute) : SkillItem {
private lateinit var contentView: EditText
private lateinit var titleVIew: TextView
private fun initItem() {
if (isContentEditable()) {
SkillItemHelper.enableEdit(contentView)
} else {
SkillItemHelper.disableEdit(contentView)
contentView.setCompoundDrawables(null, null, null, null)
}
SkillItemHelper.setTitleText(titleVIew, itemAttr.parentVol, itemAttr.isMust)
itemAttr.selectedProperties.getOrNull(0)?.propVal?.let { contentView.setText(it) }
}
init {
override fun getContentEntity(): ItemAttribute {
itemAttr.selectedProperties.apply {
if (isEmpty()) {
val entity = PropRefEntity(itemAttr.parentId, null)
add(entity)
}
get(0).propVal = contentView.text.toString()
}
return itemAttr
}
override fun createItem(context: Context): View {
val view = LayoutInflater.from(context).inflate(R.layout.layout_skill_edit, null, false)
contentView = view.findViewById(R.id.edit_content)
titleVIew = view.findViewById(R.id.title_view)
initItem()
return view
}
override fun getContentEntity(): PropRecordVoEntity {
entity.propVal = getContentView().text.toString()
entity.propId = entity.parentId
return entity
}
override fun invalidate() {}
override fun isValid() =
if (entity.isMust == 0) true else !TextUtils.isEmptyText(getContentView().text.toString())
if (itemAttr.isMust == 0) true else {
itemAttr.selectedProperties.isNotEmpty() &&
!TextUtils.isEmptyText(itemAttr.selectedProperties[0].propVal)
}
override fun getRightRes() = R.drawable.icon_edit_black
override fun isContentEditable(): Boolean {
return isSelf && isEditable
}
override fun isRightVisitable() = isSelf && isEditable
private fun isContentEditable() = itemAttr.isSelf && itemAttr.editable
}

View File

@@ -0,0 +1,36 @@
package com.yizhuan.erban.skill.widget
import com.yizhuan.xchat_android_core.skill.entity.PropRefEntity
import java.io.File
data class ItemAttribute(
val cardId: Int,
val isSelf: Boolean = false,
val editable: Boolean = false,
val state: Int = STATE_SINGLE_CHOICE,//0 单选 1 编辑 2 多选 3 音频
val isMust: Int = 0,
val parentId: Int,
val parentVol: String,
val itemEventListener: ItemEventListener? = null,
var selectedProperties: MutableList<PropRefEntity>
) {
companion object {
const val STATE_SINGLE_CHOICE = 0
const val STATE_EDIT = 1
const val STATE_MULTIPLE_CHOICE = 2
const val STATE_AUDIO = 3
const val STATE_DURATION = 4
}
var audioDuration: String? = null
var audioStatus: Int = 0//声音秀审核状态
}
interface ItemEventListener {
fun onItemClick(item: SkillItem) {}
fun onRecordSuccess(audioFile: File?, duration: Int) {}
fun onDeleteRecordClick() {}
}

View File

@@ -0,0 +1,21 @@
package com.yizhuan.erban.skill.widget
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.Space
class RecordDurationItem(private val attr: ItemAttribute) : SkillItem {
override fun createItem(context: Context): View {
val view = Space(context)
val layoutParams = ViewGroup.LayoutParams(1, 1)
view.layoutParams = layoutParams
return view
}
override fun invalidate() {}
override fun isValid() = true
override fun getContentEntity() = attr
}

View File

@@ -0,0 +1,201 @@
package com.yizhuan.erban.skill.widget
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import com.netease.nim.uikit.common.util.sys.ScreenUtil
import com.netease.nimlib.sdk.media.record.RecordType
import com.yizhuan.erban.R
import com.yizhuan.erban.audio.helper.AudioPlayerHelper
import com.yizhuan.erban.audio.helper.OnPlayListener
import com.yizhuan.erban.databinding.LayoutSkillAudioBinding
import com.yizhuan.erban.ui.widget.dialog.CommonTipDialog
import com.yizhuan.xchat_android_core.utils.TextUtils
import com.yizhuan.xchat_android_core.utils.toast
import java.io.File
class RecordIResourceItem(private val itemAttribute: ItemAttribute) : SkillItem,
TimerRecorderView.RecordListener, View.OnClickListener {
private lateinit var binding: LayoutSkillAudioBinding
private lateinit var context: Context
private var audioLength = 0
private var playState = PLAY_STATE_READY
//重新录制
private val reStartTipDialog by lazy {
CommonTipDialog(context).apply {
itemAttribute
setTipMsg(
if (itemAttribute.audioStatus == RECORD_STATE_JUDGE)
"您录制的声音正在审核中,确定要重新录制吗?"
else "确定要重新录制吗?"
)
setCancelText("")
setOkText("")
setBold(true)
setTextSize(ScreenUtil.dip2px(16f))
setOnActionListener(object : CommonTipDialog.OnActionListener {
override fun onCancel() {}
override fun onOk() {
if (AudioPlayerHelper.get().isPlaying) {
stopAudio()
}
binding.recordState = RECORD_STATE_READY
}
})
}
}
//删除录制
private val deleteTipDialog by lazy {
CommonTipDialog(context).apply {
setTipMsg("确定要删除该声音吗?")
setCancelText("确定")
setBold(true)
setTextSize(ScreenUtil.dip2px(16f))
setOkText("取消")
setOnActionListener(object : CommonTipDialog.OnActionListener {
override fun onCancel() {
if (AudioPlayerHelper.get().isPlaying) {
stopAudio()
}
itemAttribute.itemEventListener?.onDeleteRecordClick()
}
override fun onOk() {}
})
}
}
override fun createItem(context: Context): View {
this.context = context
val inflater = LayoutInflater.from(context)
binding = LayoutSkillAudioBinding.inflate(inflater)
initItem()
return binding.root
}
private fun initItem() {
binding.recordView.recordListener = this
binding.recordView.recordDuration = MAX_RECORD_DURATION
binding.recordState = itemAttribute.audioStatus
itemAttribute.audioDuration?.let { binding.duration = it }
binding.btnCancel.text = if (itemAttribute.isSelf) "取消录制" else "取消重新录制"
binding.click = this
}
override fun invalidate() {}
override fun isValid() = true
override fun getContentEntity() = itemAttribute
override fun onRecordTimeUpdate(remain: Int) {
audioLength = binding.recordView.recordDuration - remain
}
override fun onRecordStart(file: File?, recordType: RecordType?) {
binding.recordState = RECORD_STATE_RECORDING
}
override fun onRecordCancel() {
//审核通过后 重新录制取消 要设置为之前审核通过的状态
binding.recordState = itemAttribute.audioStatus
}
override fun onRecordSuccess(file: File?) {
if (audioLength < 3) {
"录制时间不能少于3s哦".toast()
binding.recordState = RECORD_STATE_READY
return
}
itemAttribute.itemEventListener?.onRecordSuccess(file, audioLength)
}
override fun onRecordFail() {
"录制失败,请重试".toast()
setItemByState(RECORD_STATE_READY)
}
/**
* 根据状态设置View
*/
fun setItemByState(state: Int) {
binding.recordState = state
}
private fun playAudio(url: String?) {
if (playState == PLAY_STATE_PLAYING || TextUtils.isEmptyText(url)) return
playState = PLAY_STATE_PLAYING
binding.palyState = playState
AudioPlayerHelper.get().playInThread(url, object : OnPlayListener {
override fun onError(error: String?) {
"播放出错,请重试".toast()
playState = PLAY_STATE_READY
binding.palyState = playState
}
override fun onPrepared() {}
override fun onPlaying(currDuration: Long) {}
override fun onCompletion() {
playState = PLAY_STATE_READY
binding.palyState = playState
}
})
}
private fun stopAudio() {
if (playState == PLAY_STATE_READY) return
AudioPlayerHelper.get().endPlay()
playState = PLAY_STATE_READY
binding.palyState = playState
}
companion object {
//录制
const val MAX_RECORD_DURATION = 15//最大录音时长
const val RECORD_STATE_READY = 0//初态 即将录制
const val RECORD_STATE_RECORDING = 5//正在录制
const val RECORD_STATE_AGREE = 1//上传成功 审核通过
const val RECORD_STATE_JUDGE = 2//上传成功 正在审核
const val RECORD_STATE_REFUSE = 3//上传成功 审核不通过
//播放
const val PLAY_STATE_PLAYING = 10
const val PLAY_STATE_READY = 11//播放出错失败按完成处理,设置为初态
}
override fun onClick(v: View) {
when (v.id) {
R.id.btn_cancel -> {
binding.recordView.endAudioRecord(true)
}
R.id.tv_sound, R.id.iv_play -> {
if (playState == PLAY_STATE_READY) {
playAudio(itemAttribute.selectedProperties.getOrNull(0)?.propVal)
} else {
stopAudio()
}
}
R.id.btn_delete -> {
if (deleteTipDialog.isShowing) {
deleteTipDialog.dismiss()
}
deleteTipDialog.show()
}
R.id.btn_restart -> {
if (reStartTipDialog.isShowing) {
reStartTipDialog.dismiss()
}
reStartTipDialog.show()
}
}
}
}

View File

@@ -1,37 +1,59 @@
package com.yizhuan.erban.skill.widget
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import androidx.constraintlayout.widget.ConstraintLayout
import com.yizhuan.erban.R
import com.yizhuan.xchat_android_core.skill.entity.PropRecordVoEntity
import com.yizhuan.xchat_android_core.utils.TextUtils
import java.lang.StringBuilder
class SelectionItem(
private val isSelf: Boolean,
private val isEditable: Boolean,
private val selectionListener: OnItemSelectionListener?,
entity: PropRecordVoEntity, context: Context
) :
SkillItem(entity, context) {
init {
class SelectionItem(private val itemAttr: ItemAttribute) : SkillItem {
private lateinit var contentView: TextView
private lateinit var titleVIew: TextView
private lateinit var contentFrame: ConstraintLayout
override fun getContentEntity() = itemAttr
override fun createItem(context: Context): View {
val view =
LayoutInflater.from(context).inflate(R.layout.layout_skill_selection, null, false)
contentView = view.findViewById(R.id.edit_content)
titleVIew = view.findViewById(R.id.title_view)
contentFrame = view.findViewById(R.id.fl_content)
initItem()
return view
}
override fun getContentEntity() = entity
private fun initItem() {
if (!isContentEditable()) {
contentView.setCompoundDrawables(null, null, null, null)
} else {
contentFrame.setOnClickListener {
itemAttr.itemEventListener?.onItemClick(this)
}
}
SkillItemHelper.setTitleText(titleVIew, itemAttr.parentVol, itemAttr.isMust)
setContent()
}
private fun setContent() {
val builder = StringBuilder()
itemAttr.selectedProperties.forEach {
if (!TextUtils.isEmptyText(it.propVal)) {
builder.append(it.propVal).append("")
}
}
if (builder.isNotEmpty()) builder.deleteCharAt(builder.length - 1)
contentView.text = builder.toString()
builder.delete(0, builder.length)
}
override fun invalidate() {
setContent()
}
override fun isValid() =
entity.isMust == 0 || entity.propId != -1
itemAttr.isMust == 0 || itemAttr.selectedProperties.isNotEmpty()
override fun getRightRes() = R.drawable.arrow_right
override fun isContentEditable() = false
override fun isRightVisitable() = isSelf && isEditable//需要优化代码
init {
getContentView().setOnClickListener {
selectionListener?.onItemSelection(this)
entity.propVal?.let { getContentView().setText(it) }
}
}
interface OnItemSelectionListener {
fun onItemSelection(item: SkillItem)
}
private fun isContentEditable() = itemAttr.isSelf && itemAttr.editable
}

View File

@@ -0,0 +1,17 @@
package com.yizhuan.erban.skill.widget
const val CARD_TYPE_GAME = 1
const val CARD_TYPE_ART = 2
const val CARD_TYPE_AUDIO = 3
data class SkillAttribute(
val id: Int,
val type: Int,
val cardId: Int,
val skillRes: String?,
val skillName: String,
val isSelf: Boolean = false,
val isEdit: Boolean = false,
val background: String?,
val itemAttributes: List<ItemAttribute>
)

View File

@@ -10,10 +10,6 @@ import android.widget.LinearLayout
import android.widget.TextView
import androidx.annotation.Nullable
import com.yizhuan.erban.R
import com.yizhuan.xchat_android_core.skill.entity.PropRecordVoEntity
import com.yizhuan.xchat_android_core.skill.entity.SKillValueEntity
import com.yizhuan.xchat_android_core.skill.entity.SkillPostServerEntity
import com.yizhuan.xchat_android_core.skill.entity.SkillRecordEntity
import com.yizhuan.erban.ui.utils.ImageLoadUtils
import com.yizhuan.erban.ui.utils.ImageLoadUtilsV2
import com.yizhuan.erban.ui.widget.magicindicator.buildins.UIUtil
@@ -30,12 +26,11 @@ class SkillCardView(
constructor(context: Context) : this(context, null)
constructor(context: Context, @Nullable attrs: AttributeSet?) : this(context, attrs, 0)
private var itemList: ArrayList<SkillItem>
private var rootEntity: SkillRecordEntity? = null
private var itemList: MutableList<SkillItem> = emptyList<SkillItem>().toMutableList()
private lateinit var skillAttr: SkillAttribute
init {
orientation = VERTICAL
itemList = ArrayList()
}
//标题
@@ -66,7 +61,7 @@ class SkillCardView(
lineParams.leftMargin = lineMargin
lineParams.rightMargin = lineMargin
for ((index, item) in itemList.withIndex()) {
val itemView = item.createItem()
val itemView = item.createItem(context)
addView(itemView, layoutParams)
if (index != itemList.size - 1) {
val lineView = View(context)
@@ -76,18 +71,18 @@ class SkillCardView(
}
}
private fun createItemView(
isSelf: Boolean,
isEdit: Boolean,
selectionListener: SelectionItem.OnItemSelectionListener?,
entity: PropRecordVoEntity
): SkillItem {
return when (entity.state) {
1 -> EditItem(isSelf, isEdit, entity, context)
else -> SelectionItem(isSelf, isEdit,selectionListener, entity, context)
//根据state创建Item视图
private fun createItemView(item: ItemAttribute): SkillItem {
return when (item.state) {
1 -> EditItem(item)
0, 2 -> SelectionItem(item)
3 -> RecordIResourceItem(item)
else -> RecordDurationItem(item)
}
}
//设置SkillView背景
private fun setBackgroundImg(url: String?) {
if (TextUtils.isEmpty(url)) {
return
@@ -95,54 +90,35 @@ class SkillCardView(
ImageLoadUtils.loadRoundBackground(context, url, this, 8, R.drawable.bg_corner_shadow_12)
}
fun setItems(
rootEntity: SkillRecordEntity,
selectionListener: SelectionItem.OnItemSelectionListener?
) {
this.rootEntity = rootEntity
this.itemList.clear()
rootEntity.propRecordVo?.forEach {
this.itemList.add(
createItemView(
rootEntity.isSelf,
rootEntity.isEdit,
selectionListener,
it
)
)
fun initView(attr: SkillAttribute) {
this.skillAttr = attr
itemList.clear()
attr.itemAttributes.forEach {
this.itemList.add(createItemView(it))
}
setBackgroundImg(rootEntity.pic)
//暂时处理
SkillItemHelper.wrapAudio(attr)
setBackgroundImg(attr.background)
removeAllViews()
setSkillTitle(rootEntity.icon, rootEntity.name, rootEntity.isSelf, rootEntity.isEdit)
setSkillTitle(attr.skillRes, attr.skillName, attr.isSelf, attr.isEdit)
setChildItems()
requestLayout()
}
fun isValid(): Boolean {
var isValid = true
itemList.forEach {
if (!it.isValid()) {
isValid = false
return@forEach
run outer@{
itemList.forEach {
if (!it.isValid()) {
isValid = false
return@outer
}
}
}
return isValid
}
fun getItems(): List<SkillItem> = itemList
fun getSavedValues(): SkillPostServerEntity {
return if (rootEntity == null) {
SkillPostServerEntity(-1, -1, ArrayList(0))
} else {
val list = ArrayList<SKillValueEntity>()
itemList.forEach {
val item = it.getContentEntity()
list.add(SKillValueEntity(item.parentId, item.propId, item.propVal))
}
SkillPostServerEntity(rootEntity!!.id, rootEntity!!.cardId, list)
}
}
fun getAttributes() = skillAttr
}

View File

@@ -1,98 +1,11 @@
package com.yizhuan.erban.skill.widget
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.view.LayoutInflater
import android.view.View
import android.widget.EditText
import android.widget.TextView
import com.yizhuan.erban.R
import com.yizhuan.xchat_android_core.skill.entity.*
import com.yizhuan.erban.ui.widget.magicindicator.buildins.UIUtil
abstract class SkillItem(
protected var entity: PropRecordVoEntity,
context: Context
) {
private var titleView: TextView
private var contentView: EditText
private var itemView: View =
LayoutInflater.from(context).inflate(R.layout.layout_skill_item, null, false)
private var rightDrawable: Drawable? = null
init {
titleView = itemView.findViewById(R.id.title_view)
contentView = itemView.findViewById(R.id.edit_content)
contentView.compoundDrawablePadding = UIUtil.dip2px(context, 10.0)
if (getRightRes() != -1) {
rightDrawable = context.getDrawable(getRightRes())
}
}
protected fun initItem() {
if (isContentEditable()) {
enableEdit()
} else {
disableEdit()
}
entity.parentVal?.let { setTitleText(it) }
entity.propVal?.let { contentView.setText(it) }
if (isContentEditable()) {
contentView.hint = "请输入"
}
if (isRightVisitable()) {
rightDrawable?.let {
it.setBounds(0, 0, it.minimumWidth, it.minimumHeight)
contentView.setCompoundDrawables(null, null, it, null)
}
}
}
private fun setTitleText(title: String) {
if (entity.isMust == 0) {
titleView.text = title
return
}
val spannableStringBuilder = SpannableStringBuilder(title)
val length = spannableStringBuilder.length
spannableStringBuilder.append("*")
spannableStringBuilder.setSpan(
ForegroundColorSpan(Color.parseColor("#FF2222")),
length,
spannableStringBuilder.length,
SpannableStringBuilder.SPAN_INCLUSIVE_EXCLUSIVE
)
titleView.text = spannableStringBuilder
}
internal fun createItem(): View = itemView
internal fun invalidate() {
contentView.setText(getContentEntity().propVal ?: "")
}
private fun disableEdit() {
contentView.isFocusableInTouchMode = false
contentView.isFocusable = false
contentView.isCursorVisible = false
}
private fun enableEdit() {
contentView.isFocusableInTouchMode = true
contentView.isFocusable = true
contentView.isCursorVisible = true
}
internal abstract fun getContentEntity(): PropRecordVoEntity
internal abstract fun isValid(): Boolean
internal abstract fun getRightRes(): Int
internal abstract fun isContentEditable(): Boolean
internal abstract fun isRightVisitable(): Boolean
protected fun getContentView(): EditText = contentView
interface SkillItem {
fun createItem(context: Context): View
fun invalidate()
fun isValid(): Boolean
fun getContentEntity(): ItemAttribute
}

View File

@@ -0,0 +1,58 @@
package com.yizhuan.erban.skill.widget
import android.graphics.Color
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.widget.EditText
import android.widget.TextView
import com.yizhuan.erban.skill.widget.ItemAttribute.Companion.STATE_AUDIO
import com.yizhuan.erban.skill.widget.ItemAttribute.Companion.STATE_DURATION
object SkillItemHelper {
/**
* 设置Item标题
*/
fun setTitleText(titleView: TextView, title: String, isMust: Int) {
if (isMust == 0) {
titleView.text = title
return
}
val spannableStringBuilder = SpannableStringBuilder(title)
val length = spannableStringBuilder.length
spannableStringBuilder.append("*")
spannableStringBuilder.setSpan(
ForegroundColorSpan(Color.parseColor("#FF2222")),
length,
spannableStringBuilder.length,
SpannableStringBuilder.SPAN_INCLUSIVE_EXCLUSIVE
)
titleView.text = spannableStringBuilder
}
/**
* 禁用EditText编辑
*/
fun disableEdit(contentView: EditText) {
contentView.isFocusableInTouchMode = false
contentView.isFocusable = false
contentView.isCursorVisible = false
}
/**
* 启用EditText编辑
*/
fun enableEdit(contentView: EditText) {
contentView.isFocusableInTouchMode = true
contentView.isFocusable = true
contentView.isCursorVisible = true
}
fun wrapAudio(attr: SkillAttribute) {
if (attr.type == CARD_TYPE_AUDIO) {
val sourceItem = attr.itemAttributes.find { it.state == STATE_AUDIO }
val durationItem = attr.itemAttributes.find { it.state == STATE_DURATION }
sourceItem?.audioDuration = durationItem?.selectedProperties?.getOrNull(0)?.propVal
}
}
}

View File

@@ -0,0 +1,180 @@
package com.yizhuan.erban.skill.widget
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.view.animation.LinearInterpolator
import androidx.annotation.Nullable
import androidx.appcompat.widget.AppCompatImageView
import com.netease.nim.uikit.impl.NimUIKitImpl
import com.netease.nimlib.sdk.media.record.AudioRecorder
import com.netease.nimlib.sdk.media.record.IAudioRecordCallback
import com.netease.nimlib.sdk.media.record.RecordType
import com.yizhuan.erban.R
import java.io.File
private const val STATE_PAUSED = 0
private const val STATE_PLAYED = 1
class TimerRecorderView(context: Context, @Nullable attrs: AttributeSet?, defStyleAttr: Int) :
AppCompatImageView(context, attrs, defStyleAttr), IAudioRecordCallback {
private var progressWidth = 0
private var outerWidth = 0
private var progressColor = 0
private val progressPaint by lazy {
Paint(Paint.ANTI_ALIAS_FLAG)
}
private var state = STATE_PAUSED
private var animator: ValueAnimator? = null
private var animatedPercent = 0f
internal var recordDuration = 15
private var audioMessageHelper: AudioRecorder? = null
var recordListener: RecordListener? = null
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
val array = context.obtainStyledAttributes(attrs, R.styleable.TimerRecorderView)
progressWidth = array.getDimension(R.styleable.TimerRecorderView_progressWidth, 0f).toInt()
outerWidth = array.getDimension(R.styleable.TimerRecorderView_outerWidth, 0f).toInt()
progressColor = array.getColor(R.styleable.TimerRecorderView_progressColor, 0).toInt()
progressPaint.style = Paint.Style.STROKE
progressPaint.strokeWidth = progressWidth.toFloat()
progressPaint.color = progressColor
array.recycle()
setOnClickListener {
when (state) {
STATE_PAUSED -> {
startRecord()
}
STATE_PLAYED -> {
endAudioRecord(false)
}
}
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
val width = MeasureSpec.getSize(widthMeasureSpec)
val height = MeasureSpec.getSize(heightMeasureSpec)
setMeasuredDimension(width, height)
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (state == STATE_PLAYED) {
val rectF = RectF()
val swipe = animatedPercent * 360
rectF.left = outerWidth.toFloat()
rectF.top = outerWidth.toFloat()
rectF.right = measuredWidth.toFloat() - outerWidth
rectF.bottom = measuredHeight.toFloat() - outerWidth
canvas.drawArc(rectF, -90f, swipe, false, progressPaint)
}
}
//开始录制
private fun startRecord() {
if (audioMessageHelper == null) {
val options = NimUIKitImpl.getOptions()
options.audioRecordMaxTime = recordDuration
audioMessageHelper = AudioRecorder(
context, options.audioRecordType,
options.audioRecordMaxTime, this
)
}
setImageResource(R.drawable.ic_skill_audio_recording)
audioMessageHelper!!.startRecord()
}
/**
* 结束语音录制
*
* @param cancel -- true 取消 重新录制 false 录制完成
*/
fun endAudioRecord(cancel: Boolean) {
state = STATE_PAUSED
audioMessageHelper?.completeRecord(cancel)
setImageResource(R.drawable.ic_skill_audio_ready)
clearRecordAnim()
}
private fun playRecordAnim() {
animator = ValueAnimator.ofFloat(0f, 360f)
?.apply {
duration = (recordDuration * 1000).toLong()
addUpdateListener {
animatedPercent = it.animatedFraction
recordListener?.onRecordTimeUpdate((recordDuration * (1 - animatedPercent)).toInt())
invalidate()
}
interpolator = LinearInterpolator()
start()
}
}
private fun clearRecordAnim() {
animator?.let {
it.removeAllUpdateListeners()
if (it.isRunning) it.end()
}
animator = null
}
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
clearRecordAnim()
audioMessageHelper?.destroyAudioRecorder()
}
override fun onRecordReady() {}
override fun onRecordStart(audioFile: File?, recordType: RecordType?) {
state = STATE_PLAYED
playRecordAnim()
recordListener?.onRecordStart(audioFile, recordType)
}
override fun onRecordSuccess(audioFile: File?, audioLength: Long, recordType: RecordType?) {
state = STATE_PAUSED
clearRecordAnim()
recordListener?.onRecordSuccess(audioFile)
}
override fun onRecordFail() {
state = STATE_PAUSED
recordListener?.onRecordFail()
clearRecordAnim()
}
override fun onRecordCancel() {
state = STATE_PAUSED
recordListener?.onRecordCancel()
clearRecordAnim()
}
override fun onRecordReachedMaxTime(maxTime: Int) {
state = STATE_PAUSED
clearRecordAnim()
audioMessageHelper?.handleEndRecord(true, maxTime)
}
interface RecordListener {
fun onRecordTimeUpdate(remain: Int)
fun onRecordStart(file: File?, recordType: RecordType?)
fun onRecordCancel()
fun onRecordSuccess(file: File?)
fun onRecordFail()
}
}

View File

@@ -0,0 +1,53 @@
package com.yizhuan.erban.ui.im.chat;
import android.widget.TextView;
import com.netease.nim.uikit.business.session.viewholder.MsgViewHolderBase;
import com.netease.nim.uikit.common.ui.recyclerview.adapter.BaseMultiItemFetchLoadAdapter;
import com.netease.nimlib.sdk.msg.attachment.MsgAttachment;
import com.yizhuan.erban.R;
import com.yizhuan.xchat_android_core.im.custom.bean.SkillMsgAttachment;
import com.yizhuan.xchat_android_core.skill.entity.SkillNotifyEntity;
/**
* <p> 文字消息holder </p>
*
* @author jiahui
* @date 2018/1/10
*/
public class MsgViewHolderSkill extends MsgViewHolderBase {
private TextView mTvTitle;
private TextView mTvContent;
private SkillMsgAttachment matchAttachment;
public MsgViewHolderSkill(BaseMultiItemFetchLoadAdapter adapter) {
super(adapter);
}
@Override
protected int getContentResId() {
return R.layout.layout_msg_view_holder_skill;
}
@Override
protected void inflateContentView() {
mTvTitle = findViewById(R.id.tv_title);
mTvContent = findViewById(R.id.tv_content);
}
@Override
protected void bindContentView() {
CharSequence title = "";
CharSequence content = "";
MsgAttachment attachment = message.getAttachment();
if (attachment instanceof SkillMsgAttachment) {
matchAttachment = (SkillMsgAttachment) attachment;
SkillNotifyEntity entity = matchAttachment.getEntity();
title = entity.getLayout().getTitle().getContent();
if (!entity.getLayout().getContents().isEmpty()) {
content = entity.getLayout().getContents().get(0).getContent();
}
}
mTvTitle.setText(title);
mTvContent.setText(content);
}
}

View File

@@ -17,6 +17,8 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.RecyclerView.ItemDecoration
import com.netease.nim.uikit.support.glide.GlideApp
import com.yizhuan.erban.R
import com.yizhuan.erban.audio.helper.AudioPlayerHelper
import com.yizhuan.erban.audio.helper.OnPlayListener
import com.yizhuan.erban.base.BaseViewBindingFragment
import com.yizhuan.erban.databinding.FragmentUserinfoUserinfoBinding
import com.yizhuan.erban.module_hall.HallDataManager
@@ -30,6 +32,7 @@ import com.yizhuan.erban.ui.user.adapter.GiftAdapter
import com.yizhuan.erban.ui.user.adapter.SkillCardAdapter
import com.yizhuan.erban.ui.user.decorationsend.UserInfoSkillDecoration
import com.yizhuan.erban.ui.user.viewmodel.UserInfoViewModel
import com.yizhuan.erban.ui.utils.ImageLoadUtilsV2
import com.yizhuan.erban.ui.widget.magicindicator.buildins.UIUtil
import com.yizhuan.xchat_android_core.auth.AuthModel
import com.yizhuan.xchat_android_core.module_hall.hall.HallModel
@@ -40,6 +43,7 @@ import com.yizhuan.xchat_android_core.user.bean.UserDetailInfo.DataBean.UserGift
import com.yizhuan.xchat_android_core.user.bean.UserInfoSkillEntity
import com.yizhuan.xchat_android_core.utils.net.BeanObserver
import com.yizhuan.xchat_android_core.utils.net.RxHelper
import com.yizhuan.xchat_android_core.utils.toast
import com.yizhuan.xchat_android_library.annatation.ActLayoutRes
@@ -50,6 +54,7 @@ class UserInfoInfoFragment : BaseViewBindingFragment<FragmentUserinfoUserinfoBin
private var giftAdapter: GiftAdapter? = null
private var skillAdapter: SkillCardAdapter? = null
private val vm: UserInfoViewModel by activityViewModels()
private var audioPlaying = false
@SuppressLint("SetTextI18n")
private fun initClanAndHall(clanAndHallInfo: ClanAndHallInfo) {
@@ -140,6 +145,22 @@ class UserInfoInfoFragment : BaseViewBindingFragment<FragmentUserinfoUserinfoBin
* 技能卡
*/
private fun initSkillCardList(list: List<UserInfoSkillEntity>) {
val audio = list.find { it.cardId == 8 }
var newList: MutableList<UserInfoSkillEntity> = list.toMutableList()
if (audio != null) {
binding.llAudio.visibility = View.VISIBLE
ImageLoadUtilsV2.loadImage(binding.ivAudioIcon, audio?.icon)
audio?.name?.let { binding.tvAudioName.text = it }
binding.livUser.stop()
binding.llAudio.setOnClickListener { toggleAudio(audio.propVals) }
newList.remove(audio)
if (newList.isEmpty()) {//只有声音秀
binding.recyclerSkill.visibility = View.GONE
return
}
} else {
binding.llAudio.visibility = View.GONE
}
if (skillAdapter == null) {
skillAdapter = SkillCardAdapter(mContext)
skillAdapter?.setHeaderAndEmpty(true)
@@ -153,12 +174,10 @@ class UserInfoInfoFragment : BaseViewBindingFragment<FragmentUserinfoUserinfoBin
LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false)
binding.recyclerSkill.adapter = skillAdapter
}
if (list.isEmpty()) {
skillAdapter?.setNewData(ArrayList(0))
if (newList.isEmpty()) {
skillAdapter?.emptyView = createSkillEmptyView()
} else {
skillAdapter?.setNewData(list)
}
skillAdapter?.setNewData(newList)
}
override fun onClick(v: View) {
@@ -216,4 +235,42 @@ class UserInfoInfoFragment : BaseViewBindingFragment<FragmentUserinfoUserinfoBin
}
binding.tvGiftDetail.setOnClickListener(this)
}
private fun toggleAudio(list: List<String>) {
val url = list.find { it.contains("http") } ?: return
if (!audioPlaying) {
playAudio(url)
} else {
stopAudio()
}
}
private fun playAudio(url: String) {
if (audioPlaying) return
audioPlaying = true
binding.livUser.start()
binding.ivAudioControl.setImageResource(R.drawable.ic_skill_play)
AudioPlayerHelper.get().playInThread(url, object : OnPlayListener {
override fun onError(error: String?) {
"播放出错,请重试".toast()
stopAudio()
}
override fun onPrepared() {}
override fun onPlaying(currDuration: Long) {}
override fun onCompletion() {
stopAudio()
}
})
}
private fun stopAudio() {
if (!audioPlaying) return
audioPlaying = false
binding.livUser.stop()
binding.ivAudioControl.setImageResource(R.drawable.ic_skill_pause)
AudioPlayerHelper.get().endPlay()
}
}

View File

@@ -26,22 +26,26 @@ public class LivingIconView extends AppCompatImageView {
private AnimationDrawable drawable;
private int drawableId;
private int dpWidth = 0;
private int dpHeight = 0;
public LivingIconView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
int dp_9 = UIUtil.dip2px(context, 9);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LivingIconView);
drawableId = a.getResourceId(R.styleable.LivingIconView_cus_drawable, R.drawable.living_icon_animation);
dpWidth = (int) a.getDimension(R.styleable.LivingIconView_cus_dp_width, dp_9);
dpHeight = (int) a.getDimension(R.styleable.LivingIconView_cus_dp_height, dp_9);
a.recycle();
init(context);
}
private void init(Context context){
private void init(Context context) {
drawable = (AnimationDrawable) context.getResources().getDrawable(drawableId);
drawable.setOneShot(false);
setScaleType(ScaleType.FIT_CENTER);
setImageDrawable(drawable);
int dp_9 = UIUtil.dip2px(context, 9);
setLayoutParams(new ViewGroup.LayoutParams(dp_9, dp_9));
setLayoutParams(new ViewGroup.LayoutParams(dpWidth, dpHeight));
}
public void setColor(int color) {

View File

@@ -1,10 +1,17 @@
package com.yizhuan.erban.ui.widget.dialog;
import static android.graphics.Typeface.BOLD;
import android.content.Context;
import android.os.Bundle;
import android.view.Window;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.style.AbsoluteSizeSpan;
import android.text.style.StyleSpan;
import android.widget.TextView;
import androidx.annotation.ColorRes;
import com.yizhuan.erban.R;
import com.yizhuan.xchat_android_core.utils.TextUtils;
@@ -18,6 +25,10 @@ public class CommonTipDialog extends BaseDialog {
private String okText = "确定";
private String cancelText = "取消";
private boolean isBold;
private @ColorRes
int color = -1;
private int textSize = -1;
public CommonTipDialog(Context context) {
super(context, R.style.dialog);
@@ -36,7 +47,19 @@ public class CommonTipDialog extends BaseDialog {
TextView tip = findViewById(R.id.message);
tip.setText(tipMsg);
if (color != -1) {
tip.setTextColor(getContext().getResources().getColor(color));
}
SpannableStringBuilder builder = new SpannableStringBuilder(tipMsg);
if (textSize != -1) {
AbsoluteSizeSpan sizeSpan = new AbsoluteSizeSpan(textSize);
builder.setSpan(sizeSpan, 0, builder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
if (isBold) {
StyleSpan boldSpan = new StyleSpan(BOLD);
builder.setSpan(boldSpan, 0, builder.length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE);
}
tip.setText(builder);
TextView ok = findViewById(R.id.btn_ok);
ok.setText(okText);
ok.setOnClickListener(v -> {
@@ -59,6 +82,18 @@ public class CommonTipDialog extends BaseDialog {
this.tipMsg = tipMsg;
}
public void setBold(boolean isBold) {
this.isBold = isBold;
}
public void setTextColor(@ColorRes int color) {
this.color = color;
}
public void setTextSize(int textSize) {
this.textSize = textSize;
}
private OnActionListener l;
public void setOkText(String okText) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 756 B

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dp_20" />
<solid android:color="@color/color_30_ffce4e" />
</shape>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#FFBC51" />
<solid android:color="#1AFFBC51" />
<corners android:radius="@dimen/dp_8" />
</shape>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="@dimen/dp_15" />
<stroke android:color="#8CA1FF" android:width="1dp"/>
</shape>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:bottomLeftRadius="@dimen/dp_4"
android:bottomRightRadius="@dimen/dp_19"
android:topLeftRadius="@dimen/dp_19"
android:topRightRadius="@dimen/dp_4" />
<gradient
android:endColor="#B3daf8fc"
android:startColor="#B3f4f5ef"
android:angle="180"
android:type="linear"/>
</shape>

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8"?>
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
android:oneshot="true">
<item
android:drawable="@drawable/ic_skill_audio_anim_01"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_02"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_03"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_04"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_05"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_06"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_07"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_08"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_09"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_10"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_11"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_12"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_13"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_14"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_15"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_16"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_17"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_18"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_19"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_20"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_21"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_22"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_23"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_24"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_25"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_26"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_27"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_28"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_29"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_30"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_31"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_32"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_33"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_34"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_35"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_36"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_37"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_38"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_39"
android:duration="@integer/skill_icon_anim_duration" />
<item
android:drawable="@drawable/ic_skill_audio_anim_40"
android:duration="@integer/skill_icon_anim_duration" />
</animation-list>

View File

@@ -20,16 +20,31 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.appcompat.widget.AppCompatButton
android:layout_width="175dp"
android:layout_height="@dimen/dp_40"
android:id="@+id/btn_ensure"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="@dimen/dp_20"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:textColor="@color/white"
android:background="@drawable/common_btn_bg"
android:text="@string/text_ok"
/>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginBottom="20dp"
app:layout_constraintBottom_toTopOf="@id/btn_ensure"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginTop="@dimen/dp_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tv_title"
app:layout_goneMarginTop="@dimen/dp_16" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -281,6 +281,52 @@
android:textColor="@color/color_333333"
android:textSize="@dimen/sp_14" />
<LinearLayout
android:id="@+id/ll_audio"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_40"
android:layout_marginTop="@dimen/dp_8"
android:background="@drawable/bg_userinfo_skill_audio"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone">
<ImageView
android:id="@+id/iv_audio_icon"
android:layout_width="@dimen/dp_32"
android:layout_height="@dimen/dp_32"
android:layout_marginLeft="@dimen/dp_13"
tools:src="@mipmap/app_logo" />
<TextView
android:id="@+id/tv_audio_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_8"
android:textColor="@color/color_333333"
android:textSize="@dimen/sp_16"
tools:text="声音秀" />
<ImageView
android:id="@+id/iv_audio_control"
android:layout_width="@dimen/dp_18"
android:layout_height="@dimen/dp_18"
android:layout_marginLeft="@dimen/dp_8"
android:src="@drawable/ic_skill_pause" />
<com.yizhuan.erban.ui.widget.LivingIconView
android:id="@+id/liv_user"
android:layout_width="@dimen/dp_40"
android:layout_height="15dp"
android:layout_marginLeft="@dimen/dp_8"
android:background="@color/transparent"
app:cus_drawable="@drawable/skill_audio_animation"
app:cus_dp_height="15dp"
app:cus_dp_width="@dimen/dp_40" />
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_skill"
android:layout_width="match_parent"
@@ -297,8 +343,8 @@
android:background="@drawable/bg_secondary_radius_10"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintVertical_bias="0"
app:layout_constraintTop_toBottomOf="@id/ll_skill">
app:layout_constraintTop_toBottomOf="@id/ll_skill"
app:layout_constraintVertical_bias="0">
<TextView
android:id="@+id/tv_gift_detail"
@@ -314,8 +360,8 @@
android:id="@+id/rv_gift"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="@dimen/dp_14"
android:layout_gravity="center_horizontal"
android:layout_marginTop="@dimen/dp_14"
android:layout_marginBottom="@dimen/dp_15"
android:clipToPadding="false"
android:paddingStart="2dp"

View File

@@ -3,11 +3,11 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="142dp"
android:paddingTop="@dimen/dp_8"
android:paddingBottom="@dimen/dp_8"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/dp_5"
android:paddingTop="@dimen/dp_8"
android:paddingRight="@dimen/dp_5"
android:layout_height="wrap_content">
android:paddingBottom="@dimen/dp_8">
<ImageView
android:id="@+id/iv_icon"
@@ -30,15 +30,19 @@
<TextView
android:id="@+id/tv_desc"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_5"
android:ellipsize="end"
app:layout_constrainedWidth="true"
android:fontFamily="sans-serif"
android:singleLine="true"
android:textColor="@color/color_333333"
android:textSize="@dimen/sp_12"
android:fontFamily="sans-serif"
app:layout_constraintStart_toStartOf="@id/tv_title"
app:layout_constraintLeft_toLeftOf="@id/tv_title"
app:layout_constraintTop_toBottomOf="@id/tv_title"
android:layout_marginTop="@dimen/dp_5"
tools:text="英雄联盟手游" />
app:layout_constraintRight_toRightOf="parent"
tools:text="盟手游英雄联盟手游" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="@dimen/dp_5">
<TextView
android:id="@+id/tv_title"
android:maxWidth="215dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/color_333333"
android:textSize="@dimen/sp_15"
android:textStyle="bold"
tools:text="恭喜您,获得抽奖机会,点我抽奖>>" />
<TextView
android:id="@+id/tv_content"
android:maxWidth="215dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:lineSpacingMultiplier="1.2"
android:textColor="@color/color_999999"
android:textSize="@dimen/sp_12"
tools:text="恭喜您,获得抽奖机会,点我抽奖>>" />
</LinearLayout>

View File

@@ -5,8 +5,9 @@
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:background="@drawable/shape_white_20dp_round"
tools:background="@color/black_transparent_10"
android:orientation="vertical">
android:orientation="vertical"
tools:background="@color/black_transparent_10">
<View
android:layout_width="0dp"
android:layout_height="@dimen/dp_26" />
@@ -15,19 +16,20 @@
android:id="@+id/message"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/dp_32"
android:layout_weight="1"
android:gravity="center"
android:paddingEnd="20dp"
android:lineSpacingMultiplier="1.2"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:textColor="@color/color_333333"
android:textSize="14sp"
android:layout_marginBottom="@dimen/dp_32"
tools:text="购买成功是否立即驾驶sd水电费水电费sfasdfasdfasdfasdfasdf" />
<LinearLayout
android:layout_width="match_parent"
android:layout_marginBottom="24dp"
android:layout_height="wrap_content"
android:layout_marginBottom="24dp"
android:gravity="center_horizontal"
android:orientation="horizontal">
@@ -35,21 +37,21 @@
android:id="@+id/btn_cancel"
android:layout_width="120dp"
android:layout_height="38dp"
android:layout_marginEnd="@dimen/dp_16"
android:background="@drawable/bg_common_cancel"
android:gravity="center"
android:text="@string/cancel"
android:textColor="@color/color_7154EE"
android:layout_marginEnd="@dimen/dp_16"
android:background="@drawable/bg_common_cancel"
android:textSize="15sp" />
<TextView
android:id="@+id/btn_ok"
android:layout_width="120dp"
android:layout_height="38dp"
android:background="@drawable/bg_common_confirm"
android:gravity="center"
android:text="@string/text_ok"
android:textColor="@color/white"
android:background="@drawable/bg_common_confirm"
android:textSize="15sp" />
</LinearLayout>

View File

@@ -0,0 +1,164 @@
<?xml version="1.0" encoding="utf-8"?>
<layout>
<data>
<import
alias="RI"
type="com.yizhuan.erban.skill.widget.RecordIResourceItem" />
<import type="android.view.View" />
<variable
name="recordState"
type="Integer" />
<variable
name="palyState"
type="Integer" />
<variable
name="duration"
type="String" />
<variable
name="click"
type="android.view.View.OnClickListener" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/dp_25"
android:paddingBottom="@dimen/dp_25">
<com.yizhuan.erban.skill.widget.TimerRecorderView
android:id="@+id/record_view"
android:layout_width="167dp"
android:layout_height="167dp"
android:src="@drawable/ic_skill_audio_ready"
android:visibility="@{(recordState==RI.RECORD_STATE_READY||recordState==RI.RECORD_STATE_RECORDING)?View.VISIBLE:View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:outerWidth="@dimen/dp_20"
app:progressColor="#5C8AFF"
app:progressWidth="@dimen/dp_10" />
<TextView
android:id="@+id/tv_record_tip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:text="@{recordState==RI.RECORD_STATE_READY?@string/skill_record_tip_ready:@string/skill_record_tip_recording}"
android:textColor="@color/black_transparent_50"
android:visibility="@{(recordState==RI.RECORD_STATE_READY||recordState==RI.RECORD_STATE_RECORDING)?View.VISIBLE:View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/record_view" />
<Button
android:id="@+id/btn_cancel"
android:layout_width="144dp"
android:layout_height="40dp"
android:layout_marginTop="@dimen/dp_10"
android:background="@drawable/bg_ffce4e_trans30_20dp"
android:onClick="@{click}"
android:paddingLeft="@dimen/dp_20"
android:paddingRight="@dimen/dp_20"
android:textColor="@color/color_7154EE"
android:visibility="@{recordState==RI.RECORD_STATE_RECORDING?View.VISIBLE:View.GONE}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:textSize="@dimen/sp_14"
app:layout_constraintTop_toBottomOf="@id/tv_record_tip"
android:text="取消重新录制" />
<!--录制完成 审核中-->
<TextView
android:id="@+id/tv_sound"
android:layout_width="150dp"
android:layout_height="@dimen/dp_30"
android:layout_marginBottom="@dimen/dp_20"
android:background="@drawable/bg_skill_record_sound"
android:gravity="center"
android:onClick="@{click}"
android:textColor="#8CA1FF"
android:textSize="@dimen/sp_12"
android:visibility="@{(recordState==RI.RECORD_STATE_READY||recordState==RI.RECORD_STATE_RECORDING)?View.GONE:View.VISIBLE}"
app:layout_constraintBottom_toTopOf="@id/bottom_button_barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:text="@{@string/skill_audio_duration(duration)}"
tools:text="10s" />
<ImageView
android:id="@+id/iv_play"
android:layout_width="@dimen/dp_24"
android:layout_height="@dimen/dp_24"
android:layout_marginLeft="@dimen/dp_4"
android:onClick="@{click}"
android:src="@{palyState==RI.PLAY_STATE_PLAYING?@drawable/ic_skill_play:@drawable/ic_skill_pause}"
android:visibility="@{(recordState==RI.RECORD_STATE_READY||recordState==RI.RECORD_STATE_RECORDING)?View.GONE:View.VISIBLE}"
app:layout_constraintBottom_toBottomOf="@id/tv_sound"
app:layout_constraintLeft_toLeftOf="@id/tv_sound"
app:layout_constraintTop_toTopOf="@id/tv_sound" />
<TextView
android:id="@+id/tv_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_5"
android:gravity="center"
android:textSize="@dimen/sp_10"
android:visibility="@{(recordState==RI.RECORD_STATE_READY||recordState==RI.RECORD_STATE_RECORDING)?View.GONE:View.VISIBLE}"
app:layout_constraintBottom_toBottomOf="@id/tv_sound"
app:layout_constraintStart_toEndOf="@id/tv_sound"
app:layout_constraintTop_toTopOf="@id/tv_sound"
android:text="@{recordState==RI.RECORD_STATE_JUDGE?@string/skill_record_judging:recordState==RI.RECORD_STATE_AGREE?@string/skill_record_agree:@string/skill_record_fail}" />
<Button
android:id="@+id/btn_delete"
android:layout_width="64dp"
android:layout_height="20dp"
android:layout_marginBottom="@dimen/dp_27"
android:onClick="@{click}"
android:background="@drawable/bg_ffce4e_trans30_20dp"
android:textColor="@color/color_7154EE"
android:visibility="@{recordState==RI.RECORD_STATE_AGREE?View.VISIBLE:View.GONE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/btn_restart"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
android:textSize="@dimen/sp_10"
android:text="删除声音" />
<Button
android:id="@+id/btn_restart"
android:layout_width="64dp"
android:layout_height="20dp"
android:layout_marginLeft="@dimen/dp_20"
android:layout_marginBottom="@dimen/dp_27"
android:onClick="@{click}"
android:background="@drawable/common_btn_bg"
android:visibility="@{(recordState==RI.RECORD_STATE_AGREE||recordState==RI.RECORD_STATE_REFUSE||recordState==RI.RECORD_STATE_JUDGE)?View.VISIBLE:View.GONE}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/btn_delete"
app:layout_goneMarginLeft="0dp"
android:textColor="@color/white"
android:textSize="@dimen/sp_10"
android:text="重新录制" />
<androidx.constraintlayout.widget.Barrier
android:id="@+id/bottom_button_barrier"
android:layout_width="0dp"
android:layout_height="0dp"
app:barrierDirection="top"
app:constraint_referenced_ids="btn_restart,btn_delete" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -2,6 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
@@ -23,18 +24,20 @@
<EditText
android:id="@+id/edit_content"
tools:text="1122"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/dp_10"
android:background="@null"
android:drawableRight="@drawable/icon_skill_edit"
android:ellipsize="end"
android:gravity="right|center_vertical"
android:maxWidth="160dp"
android:maxLength="30"
android:maxLines="3"
android:minWidth="@dimen/dp_60"
android:textColor="@color/color_333333"
android:textSize="@dimen/sp_16"
app:layout_constrainedWidth="true"
android:drawablePadding="@dimen/dp_10"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/title_view"
app:layout_constraintRight_toRightOf="parent" />

View File

@@ -0,0 +1,51 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="@dimen/dp_16"
android:paddingTop="@dimen/dp_24"
android:paddingRight="@dimen/dp_16"
android:paddingBottom="@dimen/dp_10">
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/title_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="@color/color_333333"
android:textSize="@dimen/sp_14"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/fl_content"
app:layout_constraintTop_toTopOf="@+id/fl_content" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/fl_content"
android:layout_width="200dp"
android:layout_height="wrap_content"
android:minWidth="60dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@id/title_view"
app:layout_constraintRight_toRightOf="parent">
<TextView
android:id="@+id/edit_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@null"
android:drawableRight="@drawable/arrow_right"
android:drawablePadding="@dimen/dp_10"
android:ellipsize="end"
android:maxLines="5"
android:textColor="@color/color_333333"
android:textSize="@dimen/sp_16"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -329,6 +329,8 @@
<declare-styleable name="LivingIconView">
<attr name="cus_drawable" format="reference" />
<attr name="cus_dp_width" format="dimension" />
<attr name="cus_dp_height" format="dimension" />
</declare-styleable>
<declare-styleable name="CodeEditText">
<attr name="strokeLength" format="integer" />
@@ -368,4 +370,11 @@
<attr name="rollviewpager_hint_alpha" format="integer" />
</declare-styleable>
<!--倒计时录音控件-->
<declare-styleable name="TimerRecorderView">
<attr name="progressWidth" format="dimension" />
<attr name="outerWidth" format="dimension" />
<attr name="progressColor" format="color" />
</declare-styleable>
</resources>

View File

@@ -598,5 +598,6 @@
<color name="color_40_333333">#66333333</color>
<color name="color_30_ffce4e">#4DFFCE4E</color>
</resources>

View File

@@ -3,5 +3,6 @@
<integer name="tab_anim_duration">15</integer>
<integer name="loading_anim_duration">70</integer>
<integer name="living_icon_anim_duration">60</integer>
<integer name="skill_icon_anim_duration">30</integer>
<integer name="dy_like_anim_duration">37</integer>
</resources>

View File

@@ -911,7 +911,14 @@
<string name="home_like_hint">关注的人和收藏的房间都会出现在这里\n暂无关注的人和收藏的房间哦</string>
<string name="avatar_auditing">头像已送审,审核成功后自动应用头像</string>
<string name="vip_time_desc">剩余时间以30天为周期以获得新的贵族等级次日起算30日后的中午12:00为截止时间周期内贵族等级出现变化则剩余时间自动重置为30天。</string>
<!--技能卡-->
<string name="delete_skill">删除技能卡</string>
<string name="tip_save_skill">您填写的技能卡未保存,是否直接返\n回上一页</string>
<string name="tip_delete_skill">确定要删除该技能卡?</string>
<string name="skill_record_judging">审核中</string>
<string name="skill_record_agree">审核通过</string>
<string name="skill_record_fail">审核不通过</string>
<string name="skill_record_tip_ready"> 点击录制语音15S展示才艺技能</string>
<string name="skill_record_tip_recording">录音开始不少于3s哦</string>
<string name="skill_audio_duration">%ss</string>
</resources>

View File

@@ -556,6 +556,9 @@ public class CustomAttachParser implements MsgAttachmentParser {
case CustomAttachment.ANCHOR_ROOM_AUDIENCE_UPMIC:
attachment = new RequestUpmicAttachment(CustomAttachment.ANCHOR_ROOM_AUDIENCE_UPMIC, 0);
break;
case CustomAttachment.SKILL_MSG_AUDIO:
attachment = new SkillMsgAttachment(second);
break;
default:
break;
}

View File

@@ -425,7 +425,8 @@ public class CustomAttachment implements MsgAttachment {
///个播房观众点击空坑位,房主收到请求上麦提示
public static final int ANCHOR_ROOM_AUDIENCE_UPMIC = 86;
//技能卡审核
public static final int SKILL_MSG_AUDIO = 87;
public CustomAttachment() {
}

View File

@@ -0,0 +1,42 @@
package com.yizhuan.xchat_android_core.im.custom.bean;
import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.yizhuan.xchat_android_core.skill.entity.SkillNotifyEntity;
public class SkillMsgAttachment extends CustomAttachment {
private SkillNotifyEntity entity;
@Override
protected void parseData(JSONObject data) {
//不服不行
if (data != null) {
String json = data.toJSONString();
if (json.contains("\\")) {
json = json.replace("\\", "");
if (json.contains("\"{")) {
json = json.replace("\"{", "{");
}
if (json.contains("}\"")) {
json = json.replace("}\"", "}");
}
}
entity = new Gson().fromJson(json, SkillNotifyEntity.class);
}
}
public SkillMsgAttachment(int second) {
super(SKILL_MSG_AUDIO, second);
}
@Override
protected JSONObject packData() {
String jsonStr = new Gson().toJson(entity);
return JSONObject.parseObject(jsonStr);
}
public SkillNotifyEntity getEntity() {
return entity;
}
}

View File

@@ -0,0 +1,16 @@
package com.yizhuan.xchat_android_core.skill.entity
data class SkillNotifyEntity(val layout: LayoutBean)
data class LayoutBean(
val title: ContentBean,
val time: ContentBean,
val contents: List<ContentBean>
)
data class ContentBean(
val content: String,
val fontSize: Int,
val fontColor: String,
val fontBold: Boolean
)

View File

@@ -5,9 +5,9 @@ package com.yizhuan.xchat_android_core.skill.entity
* (⊙o⊙)…
*/
data class SkillPostServerEntity(
var id: Int,
var cardId: Int,
val id: Int =-1,
val cardId: Int=-1,
var propRecordVo: List<SKillValueEntity>
)
data class SKillValueEntity(var parentId: Int, var propId: Int, var propVal: String?)
data class SKillValueEntity(var parentId: Int, var refPropVos:List<PropRefEntity>)

View File

@@ -1,5 +1,7 @@
package com.yizhuan.xchat_android_core.skill.entity
import com.google.gson.annotations.SerializedName
data class SkillPropertyEntity(
val cardId: Int,
val name: String,
@@ -7,7 +9,13 @@ data class SkillPropertyEntity(
val pic: String,
val type: Int,
val props: List<PropsEntity>
)
) {
@Transient
var isSelf = false//主态还是客态
@Transient
var isEdit = false//编辑还是展示
}
data class PropsEntity(
var id: Int,
@@ -15,11 +23,25 @@ data class PropsEntity(
var propVal: String,
val state: Int,
val isMust: Int,
val propDictVos: List<PropDictVosEntity>
)
var propDictVos: List<PropDictVo>,
val checkLimitNum: Int
) {
fun parsePropToRecord(): PropRecordVoEntity = PropRecordVoEntity(
id, propVal, emptyList(), checkLimitNum, state, isMust
)
}
data class PropDictVosEntity(
val id: Int,
val propVal: String
)
data class PropDictVo(
// 两个数据结构一致只是id的名字不一样
@SerializedName("id")
var id: Int,
var propVal: String?,//输入 选项 时长
val refIsOnlyCheck: Int//所有属性都有0-多选的选项/1表示此属性只能单选互斥
) {
@Transient
var isSelected = false
fun parseToRecord(): PropRefEntity = PropRefEntity(
id, propVal, refIsOnlyCheck
)
}

View File

@@ -1,5 +1,7 @@
package com.yizhuan.xchat_android_core.skill.entity
import com.google.gson.annotations.SerializedName
data class SkillRecordEntity(
val cardId: Int,
val createTime: String?,
@@ -8,11 +10,14 @@ data class SkillRecordEntity(
val name: String,
val pic: String,
val propRecordVo: List<PropRecordVoEntity>,
val type: Int,
val updateTime: String?
val type: Int,//技能卡类型 1游戏类 2才艺类 3声音秀
val updateTime: String?,
@SerializedName("status")
val audioStatus:Int//声音秀审核状态
) {
@Transient
var isSelf = false//主态还是客态
@Transient
var isEdit = false//编辑还是展示
}
@@ -20,8 +25,14 @@ data class SkillRecordEntity(
data class PropRecordVoEntity(
val parentId: Int,
val parentVal: String,
var propId: Int,
var propVal: String?,
var refPropVos: List<PropRefEntity>,//技能卡关联子属性集合
val checkLimitNum: Int,//技能卡多选数量限制
val state: Int,
val isMust: Int
)
data class PropRefEntity(
var propId: Int,
var propVal: String?,//输入 选项 时长
val refIsOnlyCheck: Int = 0//所有属性都有0-多选的选项/1表示此属性只能单选互斥
)

View File

@@ -23,5 +23,5 @@ only_arm64=false
channel_file=channel.txt
version_name=4.2.0
version_code=420
version_name=4.3.0
version_code=430