diff --git a/app/src/main/assets/effects/coupon_float.pag b/app/src/main/assets/effects/coupon_float.pag new file mode 100644 index 0000000..897da2d Binary files /dev/null and b/app/src/main/assets/effects/coupon_float.pag differ diff --git a/app/src/main/assets/effects/red_packet_float.pag b/app/src/main/assets/effects/red_packet_float.pag new file mode 100644 index 0000000..f2b4a2d Binary files /dev/null and b/app/src/main/assets/effects/red_packet_float.pag differ diff --git a/app/src/main/java/com/cheng/blzb/common/EventConstants.kt b/app/src/main/java/com/cheng/blzb/common/EventConstants.kt index f0ad930..03faae1 100644 --- a/app/src/main/java/com/cheng/blzb/common/EventConstants.kt +++ b/app/src/main/java/com/cheng/blzb/common/EventConstants.kt @@ -126,4 +126,28 @@ object EventConstants { const val EXIT_APP = "client.exit.app" //退出APP const val SHOW_DIALOG = "client.show.dialog" //弹出退出app的弹框 + + const val COUPON_ANIMATION_PLAY = "client.coupon.animation.play" //播放优惠券动画 + + const val COUPON_ANIMATION_CLOSE = "client.coupon.animation.close" //关闭优惠券动画 + + const val COUPON_RECEIVE = "client.coupon.receive" //领取优惠券 + + const val COUPON_REDEEM_ENABLE = "client.coupon.redeem.enable" //优惠券兑换按钮点击 + + const val COUPON_REDEEM_INFO = "client.coupon.redeem.info" //优惠券兑换详情 + + const val COUPON_REDEEM = "client.coupon.redeem" //优惠券兑换 + + const val COUPON_REDEEM_SUCCESS = "client.coupon.redeem.success" //优惠券兑换成功 + + const val COUPON_REDEEM_SUCCESS_CONFIRM = "client.coupon.redeem.success.confirm" //优惠券兑换成功 + + const val COUPON_VIEW = "client.coupon.view" //查看优惠券 + + const val COUPON_DIALOG_CHECK = "client.coupon.dialog.check" //切换优惠券 + + const val COUPON_DIALOG_CLOSE = "client.coupon.dialog.close" //关闭优惠券 + + const val COUPON_DIALOG_CONFIRM = "client.coupon.dialog.confirm" //确认优惠券 } \ No newline at end of file diff --git a/app/src/main/java/com/cheng/blzb/event/RedPacketActivityEvent.kt b/app/src/main/java/com/cheng/blzb/event/RedPacketActivityEvent.kt new file mode 100644 index 0000000..92af5aa --- /dev/null +++ b/app/src/main/java/com/cheng/blzb/event/RedPacketActivityEvent.kt @@ -0,0 +1,5 @@ +package com.cheng.blzb.event + +import com.ylqh.cube.bean.CouponActivityEntity + +class RedPacketActivityEvent(var redPacket: CouponActivityEntity, var type: Int)// 0 首页, 1 enter, 2 exit \ No newline at end of file diff --git a/app/src/main/java/com/cheng/blzb/manager/UserConfigManager.kt b/app/src/main/java/com/cheng/blzb/manager/UserConfigManager.kt index 661564b..a433012 100644 --- a/app/src/main/java/com/cheng/blzb/manager/UserConfigManager.kt +++ b/app/src/main/java/com/cheng/blzb/manager/UserConfigManager.kt @@ -1,6 +1,5 @@ package com.cheng.blzb.manager -import android.R import android.os.Build import android.text.TextUtils import androidx.lifecycle.MutableLiveData @@ -358,6 +357,17 @@ object UserConfigManager { return MMKVUtils.getString("guide_countdown_time") } + /** + * 是否显示商品降价动效 + */ + fun hideGoodsCouponAnim() { + MMKVUtils.put("show_goods_coupon", false) + } + + fun isShowGoodsCouponAnim(): Boolean { + return MMKVUtils.getBoolean("show_goods_coupon", true) + } + /** * 保存个推cid */ diff --git a/app/src/main/java/com/cheng/blzb/ui/activity/MainActivity.kt b/app/src/main/java/com/cheng/blzb/ui/activity/MainActivity.kt index 9026e6c..fc7d1c4 100644 --- a/app/src/main/java/com/cheng/blzb/ui/activity/MainActivity.kt +++ b/app/src/main/java/com/cheng/blzb/ui/activity/MainActivity.kt @@ -8,20 +8,36 @@ import android.window.OnBackInvokedDispatcher import androidx.activity.addCallback import androidx.core.os.BuildCompat import androidx.fragment.app.Fragment +import androidx.lifecycle.lifecycleScope import com.cheng.blzb.R +import com.cheng.blzb.bean.CouponEntity import com.cheng.blzb.common.EventConstants +import com.cheng.blzb.event.CouponActivityEvent import com.cheng.blzb.event.LogoutSuccessEvent import com.cheng.blzb.event.PushMessageEvent +import com.cheng.blzb.event.RedPacketActivityEvent import com.cheng.blzb.manager.EventReportManager +import com.cheng.blzb.manager.UserConfigManager +import com.cheng.blzb.net.ApiFactory import com.cheng.blzb.ui.fragment.main.MainFragment import com.example.base.common.ActivityManager import com.example.base.common.RxBus import com.example.base.extensions.toast import com.example.base.ui.BaseActivity import com.example.base.utils.ClipboardUtils +import com.example.base.utils.L +import kotlinx.coroutines.launch class MainActivity : BaseActivity() { + companion object { + var hasCheckedEnterCouponActivity = false + var hasCheckedExitCouponActivity = false + var hasCheckedVipEnterCouponActivity = false + var hasCheckedVipReturnCouponActivity = false + var hasCheckedVipGuideReturnCouponActivity = false + } + private var lastBackClickTime = 0L override fun getFragment(): Fragment { @@ -58,15 +74,79 @@ class MainActivity : BaseActivity() { } } + override fun onWindowFocusChanged(hasFocus: Boolean) { + super.onWindowFocusChanged(hasFocus) + if (hasFocus) { + checkEnterCouponActivity() + } + } + + private fun checkEnterCouponActivity() { + if (hasCheckedEnterCouponActivity || UserConfigManager.userInfo?.vip == "3") { + return + } else { + lifecycleScope.launch { + try { + val response = ApiFactory.apiService.couponActivityList("cli_app_enter") + if (response.status) { + val list = response.data + if (list.isNotEmpty()) { + if (list.size == 1 && list[0].activity_type == CouponEntity.TYPE_CASH) { + RxBus.defaultInstance.post(RedPacketActivityEvent(list[0], 1)) + } else { + RxBus.defaultInstance.post(CouponActivityEvent(list, 1)) + } + } + } + hasCheckedEnterCouponActivity = true + } catch (e: Exception) { + hasCheckedEnterCouponActivity = true + L.d(e) + } + } + } + } + + private fun checkExitCouponActivity() { + if (hasCheckedExitCouponActivity || UserConfigManager.userInfo?.vip == "3") { + exitApp() + } else { + lifecycleScope.launch { + try { + val response = ApiFactory.apiService.couponActivityList("cli_app_exit") + if (response.status) { + val list = response.data + if (list.isNotEmpty()) { + if (list.size == 1 && list[0].activity_type == CouponEntity.TYPE_CASH) { + RxBus.defaultInstance.post(RedPacketActivityEvent(list[0], 2)) + } else { + RxBus.defaultInstance.post(CouponActivityEvent(list, 2)) + } + } else { + exitApp() + } + } else { + exitApp() + } + hasCheckedExitCouponActivity = true + } catch (e: Exception) { + hasCheckedExitCouponActivity = true + L.d(e) + exitApp() + } + } + } + } + @SuppressLint("UnsafeOptInUsageError") private fun backApp() { if (BuildCompat.isAtLeastT()) { onBackInvokedDispatcher.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT) { - exitApp() + checkExitCouponActivity() } } else { onBackPressedDispatcher.addCallback(this) { - exitApp() + checkExitCouponActivity() } } } diff --git a/app/src/main/java/com/cheng/blzb/ui/dialog/CouponActivityDialog.kt b/app/src/main/java/com/cheng/blzb/ui/dialog/CouponActivityDialog.kt new file mode 100644 index 0000000..06db093 --- /dev/null +++ b/app/src/main/java/com/cheng/blzb/ui/dialog/CouponActivityDialog.kt @@ -0,0 +1,274 @@ +package com.cheng.blzb.ui.dialog + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder +import android.annotation.SuppressLint +import android.app.Dialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.LinearInterpolator +import android.widget.TextView +import androidx.core.animation.addListener +import androidx.core.view.marginStart +import androidx.fragment.app.DialogFragment +import androidx.recyclerview.widget.LinearLayoutManager +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chad.library.adapter.base.viewholder.BaseViewHolder +import com.cheng.blzb.R +import com.cheng.blzb.bean.CouponEntity +import com.cheng.blzb.common.Constants +import com.cheng.blzb.databinding.DialogCouponActivityBinding +import com.cheng.blzb.manager.DialogEnum +import com.example.base.extensions.onClick +import com.example.base.extensions.visible +import com.example.base.utils.DensityUtils +import com.example.base.utils.ScreenUtils +import com.example.base.utils.SpanUtils +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import com.ylqh.cube.bean.CouponActivityEntity +import java.text.DecimalFormat +import kotlin.math.pow + +class CouponActivityDialog : DialogFragment() { + private val needOpenAnim by lazy { arguments?.getBoolean("needAnim") ?: true } + private val mAdapter by lazy { ActivityCouponAdapter() } + + private var mOnBackListener: ((DialogEnum) -> Unit)? = null //回调事件 + + lateinit var binding: DialogCouponActivityBinding + + override fun onStart() { + super.onStart() + val window = dialog?.window + val windowParams = window?.attributes + windowParams?.dimAmount = 0.7f + windowParams?.width = ScreenUtils.getWindowSize().x + windowParams?.height = ScreenUtils.getWindowSize().y + dialog?.window?.attributes = windowParams + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + dialog?.setCancelable(false) + return super.onCreateView(inflater, container, savedInstanceState) + } + + @SuppressLint("SetTextI18n") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val view = layoutInflater.inflate(R.layout.dialog_coupon_activity, null) + + binding = DialogCouponActivityBinding.bind(view) + binding.tvTitle.typeface = Constants.almmsht + + binding.mRecyclerView.adapter = mAdapter + + val listStr = arguments?.getString("list") + if (!TextUtils.isEmpty(listStr)) { + val list = Gson().fromJson>(listStr, object : TypeToken>() {}.type) + list.forEach { it.enableAnim = needOpenAnim } + mAdapter.setList(list) + } + + binding.tvTitle.text = "${ DecimalFormat("0.##").format(mAdapter.data.sumOf { it.activity_value.toDouble() }) }元大促优惠券" + + binding.ivBtn.onClick { + mOnBackListener?.invoke(DialogEnum.CLICK_OK) + dismiss() + } + + binding.ivClose.onClick { + mOnBackListener?.invoke(DialogEnum.CLICK_CANCEL) + dismiss() + } + + startDefaultAnim() + + val dialog = Dialog(requireContext()) + dialog.setContentView(view) + return dialog + } + + private fun startDefaultAnim() { + val scaleX0_1Holder = PropertyValuesHolder.ofFloat("scaleX", 0f, 1f) + val scaleY0_1Holder = PropertyValuesHolder.ofFloat("scaleY", 0f, 1f) + + val bg1ScaleAnim = ObjectAnimator.ofPropertyValuesHolder(binding.ivBg1, scaleX0_1Holder, scaleY0_1Holder).setDuration(1000) + + val bg1RotateAnim = ObjectAnimator.ofFloat(binding.ivBg1, "rotation", 0f, 360f) + bg1RotateAnim.repeatCount = ObjectAnimator.INFINITE + bg1RotateAnim.duration = 5000 + bg1RotateAnim.interpolator = LinearInterpolator() + bg1RotateAnim.addListener(onStart = { + binding.ivBg1.visible() + }) + + val bg2ScaleAnim = ObjectAnimator.ofPropertyValuesHolder(binding.ivBg2, scaleX0_1Holder, scaleY0_1Holder).setDuration(1000) + + val bg2RotateAnim = ObjectAnimator.ofFloat(binding.ivBg2, "rotation", 0f, 360f) + bg2RotateAnim.repeatCount = ObjectAnimator.INFINITE + bg2RotateAnim.duration = 5000 + bg2RotateAnim.interpolator = LinearInterpolator() + bg2RotateAnim.addListener(onStart = { + binding.ivBg2.visible() + }) + + val titleTransYHolder = PropertyValuesHolder.ofFloat( + "translationY", + -DensityUtils.dp2px(200f).toFloat(), + DensityUtils.dp2px(15f).toFloat(), + -DensityUtils.dp2px(15f).toFloat(), + DensityUtils.dp2px(10f).toFloat(), + -DensityUtils.dp2px(10f).toFloat(), + DensityUtils.dp2px(5f).toFloat(), + -DensityUtils.dp2px(5f).toFloat(), + 0f) + val titleScaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 1f, 1.2f, 1f) + val titleScaleYHolder = PropertyValuesHolder.ofFloat( "scaleY", 1f, 1.2f, 1f) + + val titleTransYAnim = ObjectAnimator.ofPropertyValuesHolder(binding.tvTitle, titleTransYHolder).setDuration(1200) + titleTransYAnim.addListener(onStart = { + binding.tvTitle.visible() + }) + + val titleZoomAnim = ObjectAnimator.ofPropertyValuesHolder(binding.tvTitle, titleScaleXHolder, titleScaleYHolder) + titleZoomAnim.repeatCount = ObjectAnimator.INFINITE + titleZoomAnim.duration = 500 + titleZoomAnim.startDelay = if (needOpenAnim) 1200 else 0 + + val closeTransAnim = ObjectAnimator.ofFloat(binding.ivClose, "translationX", DensityUtils.dp2px(50f).toFloat(), 0f) + closeTransAnim.duration = 800 + closeTransAnim.addListener(onStart = { + binding.ivClose.visible() + }) + + val btnScaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.8f, 1f) + val btnScaleYHolder = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.8f, 1f) + + val btnScaleAnim = ObjectAnimator.ofPropertyValuesHolder(binding.ivBtn, scaleX0_1Holder, scaleY0_1Holder) + btnScaleAnim.duration = 800 + btnScaleAnim.addListener(onStart = { + binding.ivBtn.visible() + }) + + val btnZoomAnim = ObjectAnimator.ofPropertyValuesHolder(binding.ivBtn, btnScaleXHolder, btnScaleYHolder) + btnZoomAnim.repeatCount = ObjectAnimator.INFINITE + btnZoomAnim.duration = 500 + btnZoomAnim.startDelay = if (needOpenAnim) 800 else 0 + btnZoomAnim.addListener(onStart = { + binding.ivBtn.visible() + }) + + val animSet = AnimatorSet() + if (needOpenAnim) { + animSet.playTogether(bg1ScaleAnim, bg1RotateAnim, bg2ScaleAnim, bg2RotateAnim, titleTransYAnim, titleZoomAnim, btnScaleAnim, btnZoomAnim, closeTransAnim) + } else { + binding.ivClose.visible() + animSet.playTogether(bg1RotateAnim, bg2RotateAnim, titleZoomAnim, btnZoomAnim) + } + animSet.start() + } + + fun setOnBackListener(listener: ((DialogEnum) -> Unit)) { + mOnBackListener = listener + } + + companion object { + fun newInstance(list: List, needAnim: Boolean = true): CouponActivityDialog { + val arg = Bundle() + arg.putSerializable("list", Gson().toJson(list)) + arg.putBoolean("needAnim", needAnim) + val fragment = CouponActivityDialog() + fragment.arguments = arg + return fragment + } + } + + class ActivityCouponAdapter : BaseQuickAdapter(R.layout.listitem_activity_coupon) { + + override fun convert(holder: BaseViewHolder, item: CouponActivityEntity) { + holder.getView(R.id.tv_discount_amount).typeface = Constants.dDIN_PRO_M + + holder.setText(R.id.tv_type, item.activity_type_name) + + when (item.activity_type) { + CouponEntity.TYPE_CASH -> { + SpanUtils.with(holder.getView(R.id.tv_discount_amount)) + .append("¥") + .setFontSize(12, true) + .append(DecimalFormat("0.##").format(item.activity_value.toFloat())) + .create() + if (!TextUtils.isEmpty(item.activity_threshold) && item.activity_threshold.toFloat() > 0) { + holder.setText(R.id.tv_threshold_amount, "满${DecimalFormat("0.##").format(item.activity_threshold.toFloat())}可用") + } else { + holder.setText(R.id.tv_threshold_amount, "无门槛") + } + } + + CouponEntity.TYPE_DISCOUNT -> { + SpanUtils.with(holder.getView(R.id.tv_discount_amount)) + .append(DecimalFormat("0.##").format(item.activity_value.toFloat() * 10)) + .append("折") + .setFontSize(12, true) + .create() + if (!TextUtils.isEmpty(item.activity_threshold) && item.activity_threshold.toFloat() > 0) { + holder.setText(R.id.tv_threshold_amount, "满${DecimalFormat("0.##").format(item.activity_threshold.toFloat())}可用") + } else { + holder.setText(R.id.tv_threshold_amount, "无门槛") + } + } + } + + if (data.size <= 3) { + if (item.enableAnim) { + holder.itemView.elevation = 3f - holder.layoutPosition * DensityUtils.dp2px(10f) + holder.itemView.postDelayed({ + startTransAnim(holder.itemView, holder.layoutPosition, data.size) + item.enableAnim = false + }, 100) + } else { + holder.itemView.visible() + } + } else { + item.enableAnim = false + holder.itemView.visible() + } + } + + private fun startTransAnim(view: View, position: Int, total: Int) { + val originStart = view.measuredWidth * (total - 1) / 2 + view.marginStart * (total - 1) + val start = if (total == 2) ((originStart * (-1.0).pow(position))).toInt() else -originStart * (position - 1) + + val scaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 1f, 0.8f, 1f, 0.8f, 1f) + val scaleYHolder = PropertyValuesHolder.ofFloat("scaleY", 1f, 0.8f, 1f, 0.8f, 1f) + + val animator1 = ObjectAnimator.ofFloat(view, "translationX", 0f, start.toFloat()).setDuration(0) + val animator2 = ObjectAnimator.ofPropertyValuesHolder(view, scaleXHolder, scaleYHolder).setDuration(800) + val animator3 = ObjectAnimator.ofFloat(view, "translationX", start.toFloat(), 0f).setDuration(500) + animator1.addListener(onStart = { view.visibility = View.VISIBLE }, onEnd = { animator2.start() }) + animator2.addListener(onEnd = { animator3.start() }) + animator3.addListener(onEnd = { if (position == 0) { startZoomAnim(0) } }) + animator1.start() + } + + private fun startZoomAnim(position: Int) { + val lm = recyclerView.layoutManager as LinearLayoutManager + val itemView = lm.findViewByPosition(position) + + val scaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 1.05f, 1.1f, 1.15f, 1.2f, 1.15f, 1.1f, 1.05f, 1f) + val scaleYHolder = PropertyValuesHolder.ofFloat("scaleY", 1.05f, 1.1f, 1.15f, 1.2f, 1.15f, 1.1f, 1.05f, 1f) + + val zoomAnim = ObjectAnimator.ofPropertyValuesHolder(itemView, scaleXHolder, scaleYHolder) + zoomAnim.duration = 300 + zoomAnim.interpolator = LinearInterpolator() + zoomAnim.addListener(onEnd = { startZoomAnim((position + 1) % data.size) }) + zoomAnim.start() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cheng/blzb/ui/dialog/GoodsCouponDialog.kt b/app/src/main/java/com/cheng/blzb/ui/dialog/GoodsCouponDialog.kt new file mode 100644 index 0000000..ba88084 --- /dev/null +++ b/app/src/main/java/com/cheng/blzb/ui/dialog/GoodsCouponDialog.kt @@ -0,0 +1,208 @@ +package com.cheng.blzb.ui.dialog + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder +import android.animation.ValueAnimator +import android.annotation.SuppressLint +import android.app.Dialog +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.animation.addListener +import androidx.fragment.app.DialogFragment +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chad.library.adapter.base.viewholder.BaseViewHolder +import com.cheng.blzb.R +import com.cheng.blzb.bean.VipGoodsEntity +import com.cheng.blzb.databinding.DialogGoodsCouponBinding +import com.cheng.blzb.manager.DialogEnum +import com.example.base.extensions.gone +import com.example.base.extensions.visible +import com.example.base.utils.DensityUtils +import com.example.base.utils.ScreenUtils +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken + +class GoodsCouponDialog : DialogFragment() { + private val mAdapter by lazy { ActivityCouponAdapter() } + + private var mOnBackListener: ((DialogEnum) -> Unit)? = null //回调事件 + + lateinit var binding: DialogGoodsCouponBinding + + override fun onStart() { + super.onStart() + val window = dialog?.window + val windowParams = window?.attributes + windowParams?.dimAmount = 0.7f + windowParams?.width = ScreenUtils.getWindowSize().x + windowParams?.height = ScreenUtils.getWindowSize().y + dialog?.window?.attributes = windowParams + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + dialog?.setCancelable(false) + return super.onCreateView(inflater, container, savedInstanceState) + } + + @SuppressLint("SetTextI18n") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val view = layoutInflater.inflate(R.layout.dialog_goods_coupon, null) + + binding = DialogGoodsCouponBinding.bind(view) + + val listStr = arguments?.getString("list") + if (!TextUtils.isEmpty(listStr)) { + val list = Gson().fromJson>(listStr, object : TypeToken>() {}.type) + mAdapter.setList(list) + } + + startAnim1() + + val dialog = Dialog(requireContext()) + dialog.setContentView(view) + return dialog + } + + private fun startAnim1() { + val scaleXHolder = PropertyValuesHolder.ofFloat("scaleX", 0.85f, 1.15f, 0.9f, 1.1f, 0.95f, 1f) + val scaleYHolder = PropertyValuesHolder.ofFloat("scaleY", 0.85f, 1.15f, 0.9f, 1.1f, 0.95f, 1f) + + val descScaleAnim = ObjectAnimator.ofPropertyValuesHolder(binding.ivEffect1Desc, scaleXHolder, scaleYHolder) + descScaleAnim.duration = 500 + descScaleAnim.addListener(onStart = { + binding.ivEffect1Desc.visible() + }) + + val descTransAnim = ObjectAnimator.ofFloat(binding.ivEffect1Desc, "translationY", 0f, -DensityUtils.dp2px(200f).toFloat()) + descTransAnim.duration = 1000 + descTransAnim.addListener(onStart = { + + }) + + val bgTransAnim = ObjectAnimator.ofFloat(binding.ivEffect1Bg, "translationX", ScreenUtils.getScreenWidth().toFloat(), 0f) + bgTransAnim.duration = 500 + bgTransAnim.addListener(onStart = { + binding.ivEffect1Bg.visible() + }) + + val fgTransAnim = ObjectAnimator.ofFloat(binding.ivEffect1Fg, "translationX", -ScreenUtils.getScreenWidth().toFloat(), 0f) + fgTransAnim.duration = 500 + fgTransAnim.addListener(onStart = { + binding.ivEffect1Fg.visible() + }, onEnd = { + descScaleAnim.start() + }) + + val starRotateAnim = ValueAnimator.ofFloat( 0f, 1f) + starRotateAnim.duration = 1500 + starRotateAnim.addUpdateListener { animation: ValueAnimator -> + val value = animation.getAnimatedValue() as Float + val transX = DensityUtils.dp2px(50f) * value + val alpha = 0.5f + 0.5f * value + val rotate = 360f * value + + binding.ivEffect1Light1.translationX = transX + binding.ivEffect1Light2.translationX = transX + binding.ivEffect1Light3.translationX = transX + binding.ivEffect1Light4.translationX = transX + + binding.ivEffect1Star1.translationX = transX + binding.ivEffect1Star2.translationX = transX + binding.ivEffect1Star3.translationX = transX + binding.ivEffect1Star4.translationX = transX + + binding.ivEffect1Light1.alpha = alpha + binding.ivEffect1Light2.alpha = alpha + binding.ivEffect1Light3.alpha = alpha + binding.ivEffect1Light4.alpha = alpha + + binding.ivEffect1Star1.alpha = alpha + binding.ivEffect1Star2.alpha = alpha + binding.ivEffect1Star3.alpha = alpha + binding.ivEffect1Star4.alpha = alpha + + binding.ivEffect1Star1.rotation = rotate + binding.ivEffect1Star2.rotation = rotate + binding.ivEffect1Star3.rotation = rotate + binding.ivEffect1Star4.rotation = rotate + } + starRotateAnim.addListener(onEnd = { + binding.ivEffect1Bg.gone() + binding.ivEffect1Fg.gone() + binding.ivEffect1Light1.gone() + binding.ivEffect1Light2.gone() + binding.ivEffect1Light3.gone() + binding.ivEffect1Light4.gone() + binding.ivEffect1Star1.gone() + binding.ivEffect1Star2.gone() + binding.ivEffect1Star3.gone() + binding.ivEffect1Star4.gone() + + descTransAnim.start() + }) + + val starTransAnim = ValueAnimator.ofFloat(ScreenUtils.getScreenWidth().toFloat(), 0f) + starTransAnim.duration = 500 + starTransAnim.addUpdateListener { animation: ValueAnimator -> + val value = animation.getAnimatedValue() as Float + binding.ivEffect1Light1.translationX = value + binding.ivEffect1Light2.translationX = value + binding.ivEffect1Light3.translationX = value + binding.ivEffect1Light4.translationX = value + + binding.ivEffect1Star1.translationX = value + binding.ivEffect1Star2.translationX = value + binding.ivEffect1Star3.translationX = value + binding.ivEffect1Star4.translationX = value + } + starTransAnim.addListener(onStart = { + binding.ivEffect1Light1.setVisibility(View.VISIBLE) + binding.ivEffect1Light2.setVisibility(View.VISIBLE) + binding.ivEffect1Light3.setVisibility(View.VISIBLE) + binding.ivEffect1Light4.setVisibility(View.VISIBLE) + + binding.ivEffect1Star1.setVisibility(View.VISIBLE) + binding.ivEffect1Star2.setVisibility(View.VISIBLE) + binding.ivEffect1Star3.setVisibility(View.VISIBLE) + binding.ivEffect1Star4.setVisibility(View.VISIBLE) + }, onEnd = { + starRotateAnim.start() + }) + + val animSet = AnimatorSet() + animSet.playTogether( + bgTransAnim, + fgTransAnim, + starTransAnim + ) + animSet.start() + } + + fun setOnBackListener(listener: ((DialogEnum) -> Unit)) { + mOnBackListener = listener + } + + companion object { + fun newInstance(list: List): GoodsCouponDialog { + val arg = Bundle() + arg.putSerializable("list", Gson().toJson(list)) + val fragment = GoodsCouponDialog() + fragment.arguments = arg + return fragment + } + } + + class ActivityCouponAdapter : BaseQuickAdapter(R.layout.listitem_goods_coupon) { + + override fun convert(holder: BaseViewHolder, item: VipGoodsEntity) { + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cheng/blzb/ui/dialog/RedPacketActivityDialog.kt b/app/src/main/java/com/cheng/blzb/ui/dialog/RedPacketActivityDialog.kt new file mode 100644 index 0000000..34e5d6d --- /dev/null +++ b/app/src/main/java/com/cheng/blzb/ui/dialog/RedPacketActivityDialog.kt @@ -0,0 +1,226 @@ +package com.cheng.blzb.ui.dialog + +import android.animation.AnimatorSet +import android.animation.ObjectAnimator +import android.animation.PropertyValuesHolder +import android.animation.ValueAnimator +import android.annotation.SuppressLint +import android.app.Dialog +import android.content.res.AssetFileDescriptor +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.media.MediaPlayer +import android.os.Build +import android.os.Bundle +import android.text.TextUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.animation.addListener +import androidx.fragment.app.DialogFragment +import com.cheng.blzb.R +import com.cheng.blzb.common.Constants +import com.cheng.blzb.databinding.DialogRedPacketActivityBinding +import com.cheng.blzb.manager.DialogEnum +import com.example.base.extensions.onClick +import com.example.base.extensions.visible +import com.example.base.utils.DensityUtils +import com.example.base.utils.L +import com.example.base.utils.ScreenUtils +import com.ylqh.cube.bean.CouponActivityEntity + +class RedPacketActivityDialog : DialogFragment() { + private val needOpenAnim by lazy { arguments?.getBoolean("needAnim") ?: true } + + private var redPacketActivity: CouponActivityEntity? = null + + private var mOnBackListener: ((DialogEnum) -> Unit)? = null //回调事件 + + lateinit var binding: DialogRedPacketActivityBinding + + override fun onStart() { + super.onStart() + val window = dialog?.window + val windowParams = window?.attributes + windowParams?.dimAmount = 0.7f + windowParams?.width = ScreenUtils.getWindowSize().x + windowParams?.height = ScreenUtils.getWindowSize().y + dialog?.window?.attributes = windowParams + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) + dialog?.setCancelable(false) + return super.onCreateView(inflater, container, savedInstanceState) + } + + @SuppressLint("SetTextI18n") + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + val view = layoutInflater.inflate(R.layout.dialog_red_packet_activity, null) + + binding = DialogRedPacketActivityBinding.bind(view) + + binding.tvDiscountAmount.setNumber("00.00", false) + binding.tvDiscountAmount.setScrollVelocity(80) + binding.tvDiscountAmount.setTextFont(Constants.dDIN_PRO_M) + + redPacketActivity = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + arguments?.getSerializable("red_packet", CouponActivityEntity::class.java) + } else { + arguments?.getSerializable("red_packet") as CouponActivityEntity + } + binding.tvThresholdAmount.text = + if (TextUtils.isEmpty(redPacketActivity?.activity_threshold) || redPacketActivity?.activity_threshold == "0") { + "无门槛" + } else { + "满${redPacketActivity?.activity_threshold}元可用" + } + + binding.ivBtnNext.onClick { + mOnBackListener?.invoke(DialogEnum.CLICK_OK) + dismiss() + } + + binding.ivClose.onClick { + mOnBackListener?.invoke(DialogEnum.CLICK_CANCEL) + dismiss() + } + +// binding.tvDiscountAmount.setNumber("00.00") + if (!needOpenAnim) { + binding.layoutContent.visible() + } + startAnim() + + val dialog = Dialog(requireContext()) + dialog.setContentView(view) + return dialog + } + + private fun startAnim() { + val pvhScaleXBg = PropertyValuesHolder.ofFloat("ScaleX", 0.8f, 1f) + val pvhScaleYBg = PropertyValuesHolder.ofFloat("ScaleY", 0.8f, 1f) + val pvhScaleX1_11 = PropertyValuesHolder.ofFloat("ScaleX", 1f, 1.1f) + val pvhScaleY1_11 = PropertyValuesHolder.ofFloat("ScaleY", 1f, 1.1f) + val pvhScaleXContent = PropertyValuesHolder.ofFloat("ScaleX", 0f, 1f) + val pvhScaleYContent = PropertyValuesHolder.ofFloat("ScaleY", 0f, 1f) + + val bgScaleAnim = ObjectAnimator.ofPropertyValuesHolder(binding.ivBg, pvhScaleXBg, pvhScaleYBg) + bgScaleAnim.duration = 450 + bgScaleAnim.startDelay = if (needOpenAnim) 750 else 0 + bgScaleAnim.repeatCount = ObjectAnimator.INFINITE + bgScaleAnim.repeatMode = ObjectAnimator.REVERSE + bgScaleAnim.addListener(onStart = { binding.ivBg.visible() }) + + val contentScaleAnim = ObjectAnimator.ofPropertyValuesHolder(binding.layoutContent, pvhScaleXContent, pvhScaleYContent) + contentScaleAnim.duration = 600 + contentScaleAnim.startDelay = 200 + contentScaleAnim.addListener(onStart = { + binding.layoutContent.visible() + playOpenAudio() + }, onEnd = { + binding.tvDiscountAmount.setNumber(String.format("%.2f", redPacketActivity!!.activity_value.toFloat())) + }) + + val coinValueAnim = ValueAnimator.ofFloat(0f, 1f) + coinValueAnim.duration = 500 + coinValueAnim.startDelay = if (needOpenAnim) 1200 else 0 + coinValueAnim.addUpdateListener { animation: ValueAnimator -> + val i = animation.getAnimatedValue() as Float + binding.ivCoin1.translationX = (1 - i) * -DensityUtils.dp2px(100f) + binding.ivCoin1.translationY = (1 - i) * -DensityUtils.dp2px(100f) + binding.ivCoin1.setAlpha(i) + binding.ivCoin2.translationX = (1 - i) * DensityUtils.dp2px(100f) + binding.ivCoin2.translationY = (1 - i) * -DensityUtils.dp2px(100f) + binding.ivCoin2.setAlpha(i) + binding.ivCoin3.translationX = (1 - i) * DensityUtils.dp2px(100f) + binding.ivCoin3.translationY = (1 - i) * DensityUtils.dp2px(100f) + binding.ivCoin3.setAlpha(i) + binding.ivCoin4.translationX = (1 - i) * -DensityUtils.dp2px(100f) + binding.ivCoin4.translationY = (1 - i) * DensityUtils.dp2px(100f) + binding.ivCoin4.setAlpha(i) + } + coinValueAnim.addListener(onStart = { + binding.ivCoin1.setVisibility(View.VISIBLE) + binding.ivCoin2.setVisibility(View.VISIBLE) + binding.ivCoin3.setVisibility(View.VISIBLE) + binding.ivCoin4.setVisibility(View.VISIBLE) + }) + + val coinTransAnim = ValueAnimator.ofFloat(0f, DensityUtils.dp2px(10f).toFloat(), 0f, -DensityUtils.dp2px(10f).toFloat(), 0f) + coinTransAnim.repeatCount = ObjectAnimator.INFINITE + coinTransAnim.duration = 800 + coinTransAnim.startDelay = if (needOpenAnim) 1700 else 0 + coinTransAnim.addUpdateListener { animation: ValueAnimator -> + val i = animation.getAnimatedValue() as Float + binding.ivCoin1.translationY = i + binding.ivCoin2.translationY = i + binding.ivCoin3.translationY = i + binding.ivCoin4.translationY = i + } + coinTransAnim.addListener(onStart = { + binding.ivCoin1.setVisibility(View.VISIBLE) + binding.ivCoin2.setVisibility(View.VISIBLE) + binding.ivCoin3.setVisibility(View.VISIBLE) + binding.ivCoin4.setVisibility(View.VISIBLE) + }) + + val btnScaleAnim = ObjectAnimator.ofPropertyValuesHolder(binding.ivBtnNext, pvhScaleX1_11, pvhScaleY1_11) + btnScaleAnim.repeatCount = ObjectAnimator.INFINITE + btnScaleAnim.repeatMode = ObjectAnimator.REVERSE + btnScaleAnim.duration = 250 + + val animSet = AnimatorSet() + if (needOpenAnim) { + animSet.playTogether( + bgScaleAnim, + contentScaleAnim, + coinValueAnim, + coinTransAnim, + btnScaleAnim + ) + } else { + animSet.playTogether( + bgScaleAnim, + coinTransAnim, + btnScaleAnim + ) + } + animSet.start() + } + + private fun playOpenAudio() { + var afd: AssetFileDescriptor? = null + try { + afd = requireContext().assets.openFd("open_red_packet.wav") + val mediaPlayer = MediaPlayer() + mediaPlayer.setDataSource(afd) + mediaPlayer.setOnCompletionListener { it.release() } + mediaPlayer.prepare() + mediaPlayer.start() + } catch (e : Exception) { + L.d(e) + } finally { + try { + afd?.close() + } catch (e: Exception) { + L.d(e) + } + } + } + + fun setOnBackListener(listener: ((DialogEnum) -> Unit)) { + mOnBackListener = listener + } + + companion object { + fun newInstance(redPacketActivity: CouponActivityEntity, needAnim: Boolean = true): RedPacketActivityDialog { + val arg = Bundle() + arg.putSerializable("red_packet", redPacketActivity) + arg.putBoolean("needAnim", needAnim) + val fragment = RedPacketActivityDialog() + fragment.arguments = arg + return fragment + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cheng/blzb/ui/dialog/SelectCouponDialog.kt b/app/src/main/java/com/cheng/blzb/ui/dialog/SelectCouponDialog.kt index deac0f6..196f6df 100644 --- a/app/src/main/java/com/cheng/blzb/ui/dialog/SelectCouponDialog.kt +++ b/app/src/main/java/com/cheng/blzb/ui/dialog/SelectCouponDialog.kt @@ -12,21 +12,26 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.fragment.app.DialogFragment +import androidx.lifecycle.lifecycleScope import com.chad.library.adapter.base.BaseQuickAdapter import com.chad.library.adapter.base.viewholder.BaseViewHolder +import com.cheng.blzb.R +import com.cheng.blzb.bean.CouponEntity +import com.cheng.blzb.common.Constants +import com.cheng.blzb.databinding.DialogSelectCouponBinding +import com.cheng.blzb.net.ApiFactory import com.example.base.extensions.getColor import com.example.base.extensions.getYYYYMMDD2 import com.example.base.extensions.gone import com.example.base.extensions.onClick +import com.example.base.extensions.toast import com.example.base.extensions.visible +import com.example.base.utils.L import com.example.base.utils.ScreenUtils import com.example.base.utils.SpanUtils import com.example.base.widget.EmptyView import com.example.base.widget.PageStatus -import com.cheng.blzb.R -import com.cheng.blzb.common.Constants -import com.cheng.blzb.databinding.DialogSelectCouponBinding -import com.cheng.blzb.ui.fragment.mine.vip.VipFragment +import kotlinx.coroutines.launch import java.text.DecimalFormat class SelectCouponDialog : DialogFragment() { @@ -35,8 +40,8 @@ class SelectCouponDialog : DialogFragment() { private val mAdapter by lazy { CouponAdapter(price) } private val mEmptyView by lazy { EmptyView(requireContext()) } - private var mOnBackListener: ((com.cheng.blzb.bean.CouponEntity?) -> Unit)? = null - private var selectedCoupon: com.cheng.blzb.bean.CouponEntity? = null + private var mOnBackListener: ((CouponEntity?) -> Unit)? = null + private var selectedCoupon: CouponEntity? = null lateinit var binding: DialogSelectCouponBinding @@ -62,6 +67,8 @@ class SelectCouponDialog : DialogFragment() { binding = DialogSelectCouponBinding.bind(view) + binding.tvTitle.typeface = Constants.ysbth + binding.mRecyclerView.adapter = mAdapter mEmptyView.setBtnVisible(false) mAdapter.setEmptyView(mEmptyView) @@ -89,7 +96,7 @@ class SelectCouponDialog : DialogFragment() { dismiss() } - initData() + getCouponList() val dialog = Dialog(requireContext()) dialog.setContentView(view) @@ -97,18 +104,29 @@ class SelectCouponDialog : DialogFragment() { } @SuppressLint("NotifyDataSetChanged") - private fun initData() { - val fragment = requireParentFragment() - if (fragment is VipFragment) { - fragment.mViewModel.couponListLiveData.observe(this) { list -> - mAdapter.setList(list) - mAdapter.data.forEach { - if (it.id == id) { - it.isChecked = true - selectedCoupon = it + private fun getCouponList() { + lifecycleScope.launch { + try { + val params = HashMap() + params["page"] = "1" + params["size"] = "50" + params["status"] = "1" + params["expire"] = "1" + val response = ApiFactory.apiService.couponList(params) + if (response.status) { + val list = response.data.items + mAdapter.setList(list) + mAdapter.data.forEach { + if (it.id == id) { + it.isChecked = true + selectedCoupon = it + } } - } - mAdapter.notifyDataSetChanged() + mAdapter.notifyDataSetChanged() + } else toast(response.message, true) + } catch (e: Exception) { + L.d(e) + } finally { if (mAdapter.data.isEmpty()) { mEmptyView.setStatus(PageStatus.NO_DATA) binding.layoutBottom.gone() @@ -117,11 +135,10 @@ class SelectCouponDialog : DialogFragment() { binding.layoutBottom.visible() } } - fragment.mViewModel.couponList() } } - fun setOnBackListener(listener: (com.cheng.blzb.bean.CouponEntity?) -> Unit) { + fun setOnBackListener(listener: (CouponEntity?) -> Unit) { mOnBackListener = listener } @@ -136,31 +153,31 @@ class SelectCouponDialog : DialogFragment() { } } - internal class CouponAdapter(private val price: String) : BaseQuickAdapter(R.layout.listitem_coupon_dialog) { + internal class CouponAdapter(private val price: String) : BaseQuickAdapter(R.layout.listitem_coupon_dialog) { - override fun convert(holder: BaseViewHolder, item: com.cheng.blzb.bean.CouponEntity) { + override fun convert(holder: BaseViewHolder, item: CouponEntity) { holder.setText( R.id.tv_name, if (!TextUtils.isEmpty(item.coupon_name)) item.coupon_name else "${item.coupon_value_name}${item.coupon_type_name}" ) holder.setText(R.id.tv_expire_time, "有效期至 ${(item.expire_timestamp.toLong() * 1000).getYYYYMMDD2()}") - holder.setText(R.id.tv_type, item.coupon_type_name) holder.getView(R.id.tv_discount_amount).typeface = Constants.dDIN_PRO_M when (item.coupon_type) { - com.cheng.blzb.bean.CouponEntity.TYPE_CASH -> { + CouponEntity.TYPE_CASH -> { SpanUtils.with(holder.getView(R.id.tv_discount_amount)) .append("¥") .setFontSize(12, true) .append(DecimalFormat("0.##").format(item.coupon_value.toFloat() / 100)) .create() - if (!TextUtils.isEmpty(item.threshold)) { + if (!TextUtils.isEmpty(item.threshold) && item.threshold.toFloat() > 0f) { holder.setText(R.id.tv_threshold_amount, "满${DecimalFormat("0.##").format(item.threshold.toFloat() / 100)}可用") + holder.setVisible(R.id.tv_threshold_amount, true) } else { - holder.setText(R.id.tv_threshold_amount, "无门槛") + holder.setGone(R.id.tv_threshold_amount, true) } } - com.cheng.blzb.bean.CouponEntity.TYPE_DISCOUNT -> { + CouponEntity.TYPE_DISCOUNT -> { SpanUtils.with(holder.getView(R.id.tv_discount_amount)) .append(DecimalFormat("0.##").format(item.coupon_value.toFloat() * 10)) .append("折") @@ -177,13 +194,15 @@ class SelectCouponDialog : DialogFragment() { if (TextUtils.isEmpty(item.threshold) || item.threshold.toFloat() / 100 <= price.toFloat()) { holder.setTextColor(R.id.tv_discount_amount, getColor(R.color.color_ff5846)) holder.setTextColor(R.id.tv_threshold_amount, getColor(R.color.color_ff5846)) - holder.setBackgroundResource(R.id.layout_amount, R.drawable.shape_fdf4f2_cor6) - holder.setImageResource(R.id.iv_check, if (item.isChecked) R.mipmap.ic_coupon_check_true else R.mipmap.ic_coupon_check_false) + holder.setTextColor(R.id.tv_name, getColor(R.color.color_1a1a1a)) + holder.setTextColor(R.id.tv_expire_time, getColor(R.color.color_999999)) + holder.itemView.setBackgroundResource(if (item.isChecked) R.drawable.layer_coupon_dialog_bg_checked else R.mipmap.ic_coupon_dialog_bg_default) } else { - holder.setTextColor(R.id.tv_discount_amount, getColor(R.color.color_d8d8d8)) - holder.setTextColor(R.id.tv_threshold_amount, getColor(R.color.color_d8d8d8)) - holder.setBackgroundResource(R.id.layout_amount, R.drawable.shape_f6f6f6_cor6) - holder.setImageResource(R.id.iv_check, R.mipmap.ic_coupon_check_disable) + holder.setTextColor(R.id.tv_discount_amount, getColor(R.color.color_848484)) + holder.setTextColor(R.id.tv_threshold_amount, getColor(R.color.color_848484)) + holder.setTextColor(R.id.tv_name, getColor(R.color.color_848484)) + holder.setTextColor(R.id.tv_expire_time, getColor(R.color.color_a1a1a1)) + holder.itemView.setBackgroundResource(R.mipmap.ic_coupon_dialog_bg_disable) } } } diff --git a/app/src/main/java/com/cheng/blzb/ui/fragment/home/HomeFragment.kt b/app/src/main/java/com/cheng/blzb/ui/fragment/home/HomeFragment.kt index 9b277d9..45180b1 100644 --- a/app/src/main/java/com/cheng/blzb/ui/fragment/home/HomeFragment.kt +++ b/app/src/main/java/com/cheng/blzb/ui/fragment/home/HomeFragment.kt @@ -1,19 +1,28 @@ package com.cheng.blzb.ui.fragment.home +import android.annotation.SuppressLint +import android.view.MotionEvent +import android.view.ViewGroup import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.recyclerview.widget.RecyclerView.HORIZONTAL import com.cheng.blzb.R import com.cheng.blzb.bean.BidItemEntity +import com.cheng.blzb.bean.CouponEntity import com.cheng.blzb.bean.MenuEntity +import com.cheng.blzb.bean.VipGoodsEntity import com.cheng.blzb.common.Constants import com.cheng.blzb.common.EventConstants import com.cheng.blzb.databinding.FragmentHomeBinding import com.cheng.blzb.event.HomeRefreshEvent +import com.cheng.blzb.manager.DialogEnum import com.cheng.blzb.manager.EventReportManager import com.cheng.blzb.manager.NotificationHelper import com.cheng.blzb.manager.SearchHistoryManager import com.cheng.blzb.manager.UserConfigManager import com.cheng.blzb.ui.activity.PublicActivity +import com.cheng.blzb.ui.dialog.CouponActivityDialog +import com.cheng.blzb.ui.dialog.GoodsCouponDialog +import com.cheng.blzb.ui.dialog.RedPacketActivityDialog import com.cheng.blzb.ui.fragment.bid.BidAdapter import com.cheng.blzb.ui.fragment.bid.detail.BidDetailFragment import com.cheng.blzb.ui.fragment.home.bszz.BSZZFragment @@ -28,13 +37,22 @@ import com.example.base.common.RxBus import com.example.base.decoration.GridSpaceItemDecoration import com.example.base.decoration.SpacesItemDecoration import com.example.base.extensions.getColor +import com.example.base.extensions.gone import com.example.base.extensions.onClick +import com.example.base.extensions.toast +import com.example.base.extensions.visible import com.example.base.ui.list.ListFragment +import com.example.base.utils.BarUtils import com.example.base.utils.DensityUtils +import com.example.base.utils.L import com.example.base.utils.MMKVUtils +import com.example.base.utils.ScreenUtils import com.example.base.utils.SpanUtils +import com.ylqh.cube.bean.CouponActivityEntity +import org.libpag.PAGFile import java.text.DecimalFormat import kotlin.math.abs +import kotlin.text.toInt /** @@ -51,6 +69,18 @@ class HomeFragment : ListFragment() } + private val couponActivityList by lazy { mutableListOf() } + private var isNoClick = false + private var startX = 0f + private var startY = 0f + private var downX = 0f + private var downY = 0f + /*****红包优惠券*****/ + override fun initView() { super.initView() binding.tvTitle.typeface = Constants.pmzdbt @@ -81,9 +111,19 @@ class HomeFragment : ListFragment @@ -140,7 +180,116 @@ class HomeFragment : ListFragment + when (event.action) { + MotionEvent.ACTION_DOWN -> { + isNoClick = false + startX = event.rawX + startY = event.rawY + L.e("当前的初始位置X$startX") + L.e("当前的初始位置Y$startY") + downX = event.rawX + downY = event.rawY + } + + MotionEvent.ACTION_MOVE -> { + val diffX: Float = event.rawX - startX + val diffY: Float = event.rawY - startY + L.e("当前的间距X$diffX") + L.e("当前的间距Y$diffY") + var y: Float = v.y + diffY + val parent = v.parent as ViewGroup + if (y <= DensityUtils.dp2px(44f)) { + y = DensityUtils.dp2px(44f).toFloat() + } else if (y >= parent.bottom - (BarUtils.getStatusBarHeight() + DensityUtils.dp2px(30f))) { + y = (parent.bottom - (BarUtils.getStatusBarHeight() + DensityUtils.dp2px(30f))).toFloat() + } + v.x += diffX + v.y = y + startX = (event.rawX.toInt()).toFloat() + startY = (event.rawY.toInt()).toFloat() + if (abs((startX - downX).toDouble()) > 50 || abs((startY - downY).toDouble()) > 50) { //如果移动位置超过了50则不触发点击事件 + isNoClick = true + } + } + + MotionEvent.ACTION_UP -> { + L.e((("当前的left---" + v.left) + "当前的right" + v.right) + "当前的宽度" + v.width) + if (v.x + v.width / 2 >= ScreenUtils.getScreenWidth() / 2) { //如果在右侧则直接滑动到右侧 + v.x = (ScreenUtils.getScreenWidth() - v.width).toFloat() + } else { + v.x = 0f + } + } + } + isNoClick + } + + binding.pvGoodsCoupon.setOnTouchListener { v, event -> + when (event.action) { + MotionEvent.ACTION_DOWN -> { + isNoClick = false + startX = event.rawX + startY = event.rawY + L.e("当前的初始位置X$startX") + L.e("当前的初始位置Y$startY") + downX = event.rawX + downY = event.rawY + } + + MotionEvent.ACTION_MOVE -> { + val diffX: Float = event.rawX - startX + val diffY: Float = event.rawY - startY + L.e("当前的间距X$diffX") + L.e("当前的间距Y$diffY") + var y: Float = v.y + diffY + val parent = v.parent as ViewGroup + if (y <= DensityUtils.dp2px(44f)) { + y = DensityUtils.dp2px(44f).toFloat() + } else if (y >= parent.bottom - (BarUtils.getStatusBarHeight() + DensityUtils.dp2px(30f))) { + y = (parent.bottom - (BarUtils.getStatusBarHeight() + DensityUtils.dp2px(30f))).toFloat() + } + v.x += diffX + v.y = y + startX = (event.rawX.toInt()).toFloat() + startY = (event.rawY.toInt()).toFloat() + if (abs((startX - downX).toDouble()) > 50 || abs((startY - downY).toDouble()) > 50) { //如果移动位置超过了50则不触发点击事件 + isNoClick = true + } + } + + MotionEvent.ACTION_UP -> { + L.e((("当前的left---" + v.left) + "当前的right" + v.right) + "当前的宽度" + v.width) + if (v.x + v.width / 2 >= ScreenUtils.getScreenWidth() / 2) { //如果在右侧则直接滑动到右侧 + v.x = (ScreenUtils.getScreenWidth() - v.width).toFloat() + } else { + v.x = 0f + } + } + } + isNoClick } } @@ -178,9 +327,111 @@ class HomeFragment : ListFragment + if (d == DialogEnum.CLICK_OK) { + PublicActivity.start(requireContext(), VipFragment::class.java, Pair("origin", "home_coupon")) + } else { + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_CLOSE, "home", "") + } + } + f.show(childFragmentManager, CouponActivityDialog::class.java.simpleName) +// UserConfigManager.hideGoodsCouponAnim() + } + + private fun showRedPacketActivityDialog() { + val f = RedPacketActivityDialog.newInstance(couponActivityList[0], isFirstCheckCouponActivity) + f.setOnBackListener { d -> + if (d == DialogEnum.CLICK_OK) { + mViewModel.receiveActivityCoupon(couponActivityList[0].id, 0) + EventReportManager.eventReport(EventConstants.COUPON_RECEIVE + ".red.packet", "home", "") + } else { + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_CLOSE + ".red.packet", "home", "") + } + } + f.show(childFragmentManager, RedPacketActivityDialog::class.java.simpleName) + } + + private fun showCouponActivityDialog() { + val f = CouponActivityDialog.newInstance(couponActivityList, isFirstCheckCouponActivity) + f.setOnBackListener { d -> + if (d == DialogEnum.CLICK_OK) { + val idList = mutableListOf() + couponActivityList.forEach { idList.add(it.id) } + mViewModel.receiveActivityCoupon(idList.joinToString(","), 0) + EventReportManager.eventReport(EventConstants.COUPON_RECEIVE, "home", "") + } else { + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_CLOSE, "home", "") + } + } + f.show(childFragmentManager, CouponActivityDialog::class.java.simpleName) + } + + private fun startCouponActivityAnim(isRedPacket: Boolean) { + if (isRedPacket) { + binding.pvCouponActivity.visible() + binding.pvGoodsCoupon.gone() + val pagFile = PAGFile.Load(requireActivity().assets, "effects/red_packet_float.pag") + binding.pvCouponActivity.composition = pagFile + binding.pvCouponActivity.setRepeatCount(0) + binding.pvCouponActivity.play() + } else { + binding.pvGoodsCoupon.visible() + binding.pvCouponActivity.gone() + val pagFile = PAGFile.Load(requireActivity().assets, "effects/coupon_float.pag") + binding.pvGoodsCoupon.composition = pagFile + binding.pvGoodsCoupon.setRepeatCount(0) + binding.pvGoodsCoupon.play() + } + } + + private fun stopCouponActivityAnim(isRedPacket: Boolean) { + if (isRedPacket) { + binding.pvCouponActivity.pause() + binding.pvCouponActivity.gone() + } else { + binding.pvGoodsCoupon.pause() + binding.pvCouponActivity.gone() + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/cheng/blzb/ui/fragment/home/HomeViewModel.kt b/app/src/main/java/com/cheng/blzb/ui/fragment/home/HomeViewModel.kt index 5d56fa4..ebe0018 100644 --- a/app/src/main/java/com/cheng/blzb/ui/fragment/home/HomeViewModel.kt +++ b/app/src/main/java/com/cheng/blzb/ui/fragment/home/HomeViewModel.kt @@ -5,18 +5,23 @@ import androidx.lifecycle.MutableLiveData import com.cheng.blzb.bean.BidItemEntity import com.cheng.blzb.bean.BidTypeEntity import com.cheng.blzb.bean.UserAuthEntity +import com.cheng.blzb.bean.VipGoodsEntity import com.cheng.blzb.net.ApiFactory import com.cheng.blzb.net.model.toListResult import com.example.base.extensions.toast import com.example.base.utils.L import com.example.base.viewmodel.ListViewModel +import com.ylqh.cube.bean.CouponActivityEntity class HomeViewModel : ListViewModel() { val totalLiveData = MutableLiveData() - val typeLiveData = MutableLiveData>() val authLiveData = MutableLiveData() val recommendLiveData = MutableLiveData>() + val goodsListLiveData = MutableLiveData>() + val couponActivityLiveData = MutableLiveData>() + val receiveCouponLiveData = MutableLiveData() + override suspend fun requestApi(params: ArrayMap): Result> { params["sort"] = "123" @@ -69,4 +74,40 @@ class HomeViewModel : ListViewModel() { }) } + fun getGoodsList() { + launchOnUiTryCatch({ + val response = ApiFactory.apiService.getGoodsList("member") + if (response.status) { + goodsListLiveData.postValue(response.data) + } + }, { + L.d(it) + }) + } + + fun couponActivityList() { + launchOnUiTryCatch({ + val response = ApiFactory.apiService.couponActivityList("cli_home") + if (response.status) { + couponActivityLiveData.postValue(response.data) + } + },{ + L.d(it) + }) + } + + fun receiveActivityCoupon(ids: String, type: Int) { + showDialog() + launchOnUiTryCatch({ + val response = ApiFactory.apiService.getActivityCoupons(ids) + if (response.status) { + receiveCouponLiveData.postValue(type) + } else toast(response.message, true) + dismissDialog() + }, { + dismissDialog() + setError(it) + L.d(it) + }) + } } \ No newline at end of file diff --git a/app/src/main/java/com/cheng/blzb/ui/fragment/mine/vip/VipFragment.kt b/app/src/main/java/com/cheng/blzb/ui/fragment/mine/vip/VipFragment.kt index 8c7d730..c489ca9 100644 --- a/app/src/main/java/com/cheng/blzb/ui/fragment/mine/vip/VipFragment.kt +++ b/app/src/main/java/com/cheng/blzb/ui/fragment/mine/vip/VipFragment.kt @@ -4,8 +4,11 @@ import android.animation.ValueAnimator import android.annotation.SuppressLint import android.text.TextUtils import android.view.View +import android.window.OnBackInvokedDispatcher +import androidx.activity.addCallback import androidx.constraintlayout.widget.ConstraintLayout import androidx.core.content.ContextCompat +import androidx.core.os.BuildCompat import androidx.core.view.isVisible import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.LinearLayoutManager @@ -13,6 +16,7 @@ import androidx.recyclerview.widget.RecyclerView import com.bytedance.ads.convert.event.ConvertReportHelper import com.cheng.blzb.R import com.cheng.blzb.bean.CorpEntity +import com.cheng.blzb.bean.CouponEntity import com.cheng.blzb.bean.OrderPayEntity import com.cheng.blzb.bean.UserEntity import com.cheng.blzb.bean.VipGoodsEntity @@ -26,8 +30,12 @@ import com.cheng.blzb.manager.DialogEnum import com.cheng.blzb.manager.EventReportManager import com.cheng.blzb.manager.LoginManager import com.cheng.blzb.manager.UserConfigManager +import com.cheng.blzb.ui.activity.MainActivity import com.cheng.blzb.ui.activity.PublicActivity +import com.cheng.blzb.ui.dialog.CouponActivityDialog import com.cheng.blzb.ui.dialog.PayTipDialog +import com.cheng.blzb.ui.dialog.RedPacketActivityDialog +import com.cheng.blzb.ui.dialog.SelectCouponDialog import com.cheng.blzb.ui.dialog.TipDialog import com.cheng.blzb.ui.fragment.mine.order.cert.CertificateFragment import com.cheng.blzb.utils.DateUtils @@ -48,6 +56,7 @@ import com.example.base.utils.SpanUtils import com.google.gson.Gson import com.tencent.mm.opensdk.openapi.IWXAPI import com.tencent.mm.opensdk.openapi.WXAPIFactory +import com.ylqh.cube.bean.CouponActivityEntity import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.disposables.Disposable @@ -89,6 +98,12 @@ class VipFragment : BaseFragment() { private var countdownDisposable: Disposable? = null private var scrollTask: Disposable? = null + /******优惠券红包******/ + private var selectedCoupon: CouponEntity? = null + private val couponList by lazy { mutableListOf() } + private val couponEnterActivityList by lazy { mutableListOf() } + private val couponReturnActivityList by lazy { mutableListOf() } + override fun initView() { super.initView() setBackColor(R.color.white) @@ -114,6 +129,13 @@ class VipFragment : BaseFragment() { mViewModel.getPayUserTips() mViewModel.getGoodsList(if (vipType == 0) "member" else "corp") mViewModel.getCorpInfo() + mViewModel.couponList() + if (!MainActivity.hasCheckedVipEnterCouponActivity) { + mViewModel.couponEnterActivityList() + } + if (!MainActivity.hasCheckedVipReturnCouponActivity) { + mViewModel.couponReturnActivityList() + } } @SuppressLint("NotifyDataSetChanged", "SetTextI18n") @@ -121,6 +143,21 @@ class VipFragment : BaseFragment() { super.initListener() binding.rvUserTips.onTouch { _, _ -> true } + setBackPressed() + mTitleBar?.setNavigationOnClickListener { + if (MainActivity.hasCheckedVipReturnCouponActivity || MainActivity.hasCheckedVipGuideReturnCouponActivity || couponReturnActivityList.isEmpty()) { + requireActivity().finish() + } else { + if (couponReturnActivityList.size == 1 && couponReturnActivityList[0].activity_type == CouponEntity.TYPE_CASH) { + showRedPacketActivityDialog(false) + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_PLAY + ".red.packet", "pay_return", "") + } else { + showCouponActivityDialog(false) + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_PLAY, "pay_return", "") + } + } + } + goodsAdapter.setOnItemClickListener { _, _, i -> val item = goodsAdapter.getItem(i) if (item.goods_id == goodsEntity?.goods_id) return@setOnItemClickListener @@ -144,6 +181,8 @@ class VipFragment : BaseFragment() { setPrice(goodsEntity!!.price.toFloat()) } releasePayType() + selectedCoupon = null + setCoupon() } binding.btnTab1.onClick { @@ -182,19 +221,38 @@ class VipFragment : BaseFragment() { } } + binding.tvCoupon.onClick { + if (goodsEntity == null) { + toast("请先选择要开通的会员套餐") + return@onClick + } + val f = SelectCouponDialog.newInstance(selectedCoupon?.id, goodsEntity!!.price) + f.setOnBackListener { + selectedCoupon = it + checkCouponValid() + setCoupon() + EventReportManager.eventReport(EventConstants.COUPON_DIALOG_CHECK, "选择优惠券切换", Gson().toJson(selectedCoupon)) + } + f.show(childFragmentManager, SelectCouponDialog::class.java.simpleName) + EventReportManager.eventReport(EventConstants.COUPON_VIEW, "member", "") + } + binding.tvAliPay.onClick { payType = 1 checkPayType() + EventReportManager.eventReport(EventConstants.PAY_SELECT, "alipay", origin) } binding.tvWxPay.onClick { payType = 0 checkPayType() + EventReportManager.eventReport(EventConstants.PAY_SELECT, "weixin", origin) } binding.tvBankPay.onClick { payType = 2 checkPayType() + EventReportManager.eventReport(EventConstants.PAY_SELECT, "bank", origin) } binding.cbAgree.onCheckedChange { _, isChecked -> @@ -219,31 +277,19 @@ class VipFragment : BaseFragment() { val f = PayTipDialog.newInstance(showAgreement = true, showRenew = !TextUtils.isEmpty(goodsEntity?.sign_value) && payType == 1) f.setOnSelectListener { binding.cbAgree.isChecked = true - if (payType == 0) { - if (!TextUtils.isEmpty(goodsEntity!!.weixinMpOriId)) { - mViewModel.payCreateOrder(goodsEntity!!.goods_id, "combo", origin, "${seatCount - seatCountLimit}") - } else { - mViewModel.payCreateOrder(goodsEntity!!.goods_id, "weixin", origin, "${seatCount - seatCountLimit}") - } - } else if (payType == 1) { - mViewModel.payCreateOrder(goodsEntity!!.goods_id, "alipay", origin, "${seatCount - seatCountLimit}") - } else if (payType == 2) { - mViewModel.payCreateOrder(goodsEntity!!.goods_id, "bank", origin, "${seatCount - seatCountLimit}") + if (!checkCouponValid()) { + setCoupon() + return@setOnSelectListener } + createOrder() } f.show(childFragmentManager, PayTipDialog::class.java.simpleName) } else { - if (payType == 0) { - if (!TextUtils.isEmpty(goodsEntity!!.weixinMpOriId)) { - mViewModel.payCreateOrder(goodsEntity!!.goods_id, "combo", origin, "${seatCount - seatCountLimit}") - } else { - mViewModel.payCreateOrder(goodsEntity!!.goods_id, "weixin", origin, "${seatCount - seatCountLimit}") - } - } else if (payType == 1) { - mViewModel.payCreateOrder(goodsEntity!!.goods_id, "alipay", origin, "${seatCount - seatCountLimit}") - } else if (payType == 2) { - mViewModel.payCreateOrder(goodsEntity!!.goods_id, "bank", origin, "${seatCount - seatCountLimit}") + if (!checkCouponValid()) { + setCoupon() + return@onClick } + createOrder() } EventReportManager.eventReport( EventConstants.PAY_PAY, @@ -304,9 +350,20 @@ class VipFragment : BaseFragment() { } releasePayType() } + selectedCoupon = null + setCoupon() + setCouponCount() initPrivacyTv() } + mViewModel.couponListLiveData.observe(this) { list -> + couponList.clear() + couponList.addAll(list) + if (selectedCoupon == null) { + setCouponCount() + } + } + mViewModel.corpInfoLiveData.observe(this) { corpInfo = it if (!TextUtils.isEmpty(it.sub_user_limit)) { @@ -351,6 +408,28 @@ class VipFragment : BaseFragment() { startScroll() } + mViewModel.couponEnterActivityLiveData.observe(this) { + couponEnterActivityList.clear() + couponEnterActivityList.addAll(it) + if (couponEnterActivityList.size == 1 && couponEnterActivityList[0].activity_type == CouponEntity.TYPE_CASH) { + showRedPacketActivityDialog(true) + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_PLAY + ".red.packet", "pay_enter", "") + } else { + showCouponActivityDialog(true) + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_PLAY, "pay_enter", "") + } + } + + mViewModel.couponReturnActivityLiveData.observe(this) { list -> + couponReturnActivityList.clear() + couponReturnActivityList.addAll(list) + } + + mViewModel.receiveCouponLiveData.observe(this) { + toast("领取成功") + mViewModel.couponList() + } + val payStatusDisposable = RxBus.defaultInstance.toObservable(PayStatusEvent::class.java).subscribe { when (it.payStatus) { PayStatusEnum.PAY_SUCCESS -> { @@ -373,6 +452,20 @@ class VipFragment : BaseFragment() { addDisposable(payStatusDisposable) } + private fun createOrder() { + if (payType == 0) { + if (!TextUtils.isEmpty(goodsEntity!!.weixinMpOriId)) { + mViewModel.payCreateOrder(goodsEntity!!.goods_id, "combo", origin, "${seatCount - seatCountLimit}", selectedCoupon?.id) + } else { + mViewModel.payCreateOrder(goodsEntity!!.goods_id, "weixin", origin, "${seatCount - seatCountLimit}", selectedCoupon?.id) + } + } else if (payType == 1) { + mViewModel.payCreateOrder(goodsEntity!!.goods_id, "alipay", origin, "${seatCount - seatCountLimit}", selectedCoupon?.id) + } else if (payType == 2) { + mViewModel.payCreateOrder(goodsEntity!!.goods_id, "bank", origin, "${seatCount - seatCountLimit}", selectedCoupon?.id) + } + } + private fun showQueryTipDialog() { if (showQueryTip) { val f = TipDialog.newInstance("温馨提示", "是否已完成支付", "未支付", "已支付", false) @@ -603,6 +696,101 @@ class VipFragment : BaseFragment() { } } + private fun setCouponCount() { + val enableCount = couponList.size + if (enableCount > 0) { + SpanUtils.with(binding.tvCoupon) + .append("您有") + .setForegroundColor(getColor(R.color.color_90ffffff)) + .append("${enableCount}个") + .setForegroundColor(getColor(R.color.color_f0365e)) + .append("红包") + .setForegroundColor(getColor(R.color.color_90ffffff)) + .create() + } else { + binding.tvCoupon.setTextColor(getColor(R.color.color_50ffffff)) + binding.tvCoupon.text = "暂无可用的红包" + } + } + + @SuppressLint("SetTextI18n") + private fun setCoupon() { + if (selectedCoupon != null) { + when (selectedCoupon!!.coupon_type) { + CouponEntity.TYPE_CASH -> { + binding.tvCoupon.text = "-¥${DecimalFormat("0.##").format(selectedCoupon!!.coupon_value.toFloat() / 100)}" + val endPrice = goodsEntity!!.price.toFloat() - selectedCoupon!!.coupon_value.toFloat() / 100 + setPrice(endPrice) + } + + CouponEntity.TYPE_DISCOUNT -> { + binding.tvCoupon.text = "${DecimalFormat("0.##").format(selectedCoupon!!.coupon_value.toFloat() * 10)}折" + val endPrice = goodsEntity!!.price.toFloat() * selectedCoupon!!.coupon_value.toFloat() + setPrice(endPrice) + } + } + binding.tvCoupon.setTextColor(getColor(R.color.color_f0365e)) + } else { + setPrice(goodsEntity!!.price.toFloat()) + setCouponCount() + } + } + + private fun checkCouponValid(): Boolean { + if (selectedCoupon != null) { + if (selectedCoupon!!.expire_timestamp.toLong() < System.currentTimeMillis() / 1000) { + selectedCoupon = null + toast("红包已过期") + return false + } + } + return true + } + + private fun showRedPacketActivityDialog(isEnter: Boolean) { + val coupon = if (isEnter) { + MainActivity.hasCheckedVipEnterCouponActivity = true + couponEnterActivityList[0] + } else { + MainActivity.hasCheckedVipReturnCouponActivity = true + couponReturnActivityList[0] + } + val f = RedPacketActivityDialog.newInstance(coupon) + f.setOnBackListener { d -> + if (d == DialogEnum.CLICK_OK) { + mViewModel.receiveActivityCoupon(coupon.id) + EventReportManager.eventReport(EventConstants.COUPON_RECEIVE + ".red.packet", if (isEnter) "pay_enter" else "pay_return", "") + } else { + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_CLOSE + ".red.packet", if (isEnter) "pay_enter" else "pay_return", "") + } + } + f.show(childFragmentManager, RedPacketActivityDialog::class.java.simpleName) + } + + private fun showCouponActivityDialog(isEnter: Boolean) { + val couponList = if (isEnter) { + MainActivity.hasCheckedVipEnterCouponActivity = true + couponEnterActivityList + } else { + MainActivity.hasCheckedVipReturnCouponActivity = true + couponReturnActivityList + } + if (couponList.isNotEmpty()) { + val f = CouponActivityDialog.newInstance(couponList) + f.setOnBackListener { d -> + if (d == DialogEnum.CLICK_OK) { + val idList = mutableListOf() + couponList.forEach { idList.add(it.id) } + mViewModel.receiveActivityCoupon(idList.joinToString(",")) + EventReportManager.eventReport(EventConstants.COUPON_RECEIVE, if (isEnter) "pay_enter" else "pay_return", "") + } else { + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_CLOSE, if (isEnter) "pay_enter" else "pay_return", "") + } + } + f.show(childFragmentManager, CouponActivityDialog::class.java.simpleName) + } + } + private fun sendBDReport(isSuccess: Boolean) { ConvertReportHelper.onEventPurchase( "member", @@ -666,4 +854,37 @@ class VipFragment : BaseFragment() { countdownDisposable?.dispose() super.onDestroyView() } + + @SuppressLint("UnsafeOptInUsageError") + private fun setBackPressed() { + if (BuildCompat.isAtLeastT()) { + requireActivity().onBackInvokedDispatcher.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT) { + if (MainActivity.hasCheckedVipReturnCouponActivity || MainActivity.hasCheckedVipGuideReturnCouponActivity || couponReturnActivityList.isEmpty()) { + requireActivity().finish() + } else { + if (couponReturnActivityList.size == 1 && couponReturnActivityList[0].activity_type == CouponEntity.TYPE_CASH) { + showRedPacketActivityDialog(false) + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_PLAY + ".red.packet", "pay_return_scroll_back", "") + } else { + showCouponActivityDialog(false) + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_PLAY, "pay_return_scroll_back", "") + } + } + } + } else { + requireActivity().onBackPressedDispatcher.addCallback(this) { + if (MainActivity.hasCheckedVipReturnCouponActivity || MainActivity.hasCheckedVipGuideReturnCouponActivity || couponReturnActivityList.isEmpty()) { + requireActivity().finish() + } else { + if (couponReturnActivityList.size == 1 && couponReturnActivityList[0].activity_type == CouponEntity.TYPE_CASH) { + showRedPacketActivityDialog(false) + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_PLAY + ".red.packet", "pay_return_scroll_back", "") + } else { + showCouponActivityDialog(false) + EventReportManager.eventReport(EventConstants.COUPON_ANIMATION_PLAY, "pay_return_scroll_back", "") + } + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/cheng/blzb/ui/fragment/mine/vip/VipViewModel.kt b/app/src/main/java/com/cheng/blzb/ui/fragment/mine/vip/VipViewModel.kt index e69c12d..d42ad01 100644 --- a/app/src/main/java/com/cheng/blzb/ui/fragment/mine/vip/VipViewModel.kt +++ b/app/src/main/java/com/cheng/blzb/ui/fragment/mine/vip/VipViewModel.kt @@ -20,7 +20,6 @@ import okhttp3.RequestBody.Companion.toRequestBody class VipViewModel : BaseViewModel() { var queryOrderJob: Job? = null - val userInfoLiveData = MutableLiveData() val goodsListLiveData = MutableLiveData>() val corpInfoLiveData = MutableLiveData() @@ -28,8 +27,9 @@ class VipViewModel : BaseViewModel() { val orderInfoLiveData = MutableLiveData() val payTipsLiveData = MutableLiveData>() val couponListLiveData = MutableLiveData>() - val couponActivityLiveData = MutableLiveData>() - val getCouponLiveData = MutableLiveData() + val couponEnterActivityLiveData = MutableLiveData>() + val couponReturnActivityLiveData = MutableLiveData>() + val receiveCouponLiveData = MutableLiveData() fun userInfo() { showDialog() @@ -157,23 +157,34 @@ class VipViewModel : BaseViewModel() { }) } - fun couponActivityList() { + fun couponEnterActivityList() { launchOnUiTryCatch({ - val response = ApiFactory.apiService.couponActivityList("cli_pay_return") + val response = ApiFactory.apiService.couponActivityList("cli_pay_enter") if (response.status) { - couponActivityLiveData.postValue(response.data) + couponEnterActivityLiveData.postValue(response.data) } },{ L.d(it) }) } - fun getActivityCoupon(ids: String) { + fun couponReturnActivityList() { + launchOnUiTryCatch({ + val response = ApiFactory.apiService.couponActivityList("cli_pay_return") + if (response.status) { + couponReturnActivityLiveData.postValue(response.data) + } + },{ + L.d(it) + }) + } + + fun receiveActivityCoupon(ids: String) { showDialog() launchOnUiTryCatch({ val response = ApiFactory.apiService.getActivityCoupons(ids) if (response.status) { - getCouponLiveData.postValue(Any()) + receiveCouponLiveData.postValue(Any()) } else toast(response.message, true) dismissDialog() }, { diff --git a/app/src/main/java/com/cheng/blzb/widget/MultiScrollNumber.kt b/app/src/main/java/com/cheng/blzb/widget/MultiScrollNumber.kt new file mode 100644 index 0000000..9138a8e --- /dev/null +++ b/app/src/main/java/com/cheng/blzb/widget/MultiScrollNumber.kt @@ -0,0 +1,277 @@ +package com.cheng.blzb.widget + +import android.content.Context +import android.graphics.Typeface +import android.text.TextUtils +import android.util.AttributeSet +import android.util.TypedValue +import android.view.Gravity +import android.view.ViewGroup +import android.view.animation.AccelerateDecelerateInterpolator +import android.view.animation.Interpolator +import android.widget.LinearLayout +import android.widget.TextView +import androidx.annotation.ColorRes +import androidx.annotation.IntRange +import androidx.core.content.ContextCompat +import androidx.core.content.withStyledAttributes +import com.cheng.blzb.R +import com.devcon.audi.view.ScrollNumber +import com.example.base.extensions.getColor +import java.util.regex.Pattern + +class MultiScrollNumber @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0 +) : LinearLayout(context, attrs, defStyleAttr) { + + private val mTargetNumbers: MutableList = kotlin.collections.ArrayList() + private val mPrimaryNumbers: MutableList = kotlin.collections.ArrayList() + private val mScrollNumbers: MutableList = kotlin.collections.ArrayList() + private var mTextSize = 130 + private var mTextColors = intArrayOf(R.color.color_ff2422) + private var mInterpolator: Interpolator = AccelerateDecelerateInterpolator() + private var mFontFileName: String? = null + private var mTypeFace: Typeface? = null + private var mVelocity = 15 + + init { + context.withStyledAttributes(attrs, R.styleable.MultiScrollNumber) { + val primaryNumber = getInteger(R.styleable.MultiScrollNumber_primary_number, 0) + val targetNumber = getInteger(R.styleable.MultiScrollNumber_target_number, 0) + val numberSize = getInteger(R.styleable.MultiScrollNumber_number_size, 130) + setNumber(primaryNumber, targetNumber) + setTextSize(numberSize) + } + orientation = HORIZONTAL + gravity = Gravity.CENTER + } + + fun setNumber(str: String, scroll: Boolean = true) { + require(isNumeric(str)) { "number value should >= 0" } + resetView() + val charArray = str.toCharArray() + for (i in charArray.indices.reversed()) { + if (Character.isDigit(charArray[i])) { + mTargetNumbers.add(charArray[i].code - '0'.code) + } else { + mTargetNumbers.add(-1) + } + } + for (i in mTargetNumbers.indices.reversed()) { +// for (int i = 0; i < mTargetNumbers.size(); i++) { + if (mTargetNumbers[i] != -1) { + val scrollNumber = ScrollNumber(context) + scrollNumber.setScrollable(scroll) + scrollNumber.setTextColor( + ContextCompat + .getColor(context, mTextColors[i % mTextColors.size]) + ) + scrollNumber.setVelocity(mVelocity * (i + 1)) + scrollNumber.setTextSize(mTextSize) + scrollNumber.setInterpolator(mInterpolator) + if (!TextUtils.isEmpty(mFontFileName)) scrollNumber.setTextFont(mFontFileName) + if (mTypeFace != null) { + scrollNumber.setTextFont(mTypeFace) + } + scrollNumber.setNumber((mTargetNumbers[i] + 1) % 10, mTargetNumbers[i], 0) + mScrollNumbers.add(scrollNumber) + addView(scrollNumber) + } else { + val params = ViewGroup.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT) + val point = TextView(context) + point.text = "." + point.setGravity(Gravity.BOTTOM) + point.setPadding(0, 0, 0, dp2px(10f)) + point.setTextColor( + ContextCompat + .getColor(context, mTextColors[i % mTextColors.size]) + ) + point.textSize = mTextSize.toFloat() + if (!TextUtils.isEmpty(mFontFileName)) { + val mTypeface = Typeface.createFromAsset(context.assets, mFontFileName) + point.setTypeface(mTypeface) + } else { + point.typeface = mTypeFace + } + addView(point, params) + } + } + val params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT) + val unit = TextView(context) + unit.text = "元" + unit.setGravity(Gravity.BOTTOM) + params.bottomMargin = dp2px(3f) + unit.setPadding(0, 0, 0, dp2px(10f)) + unit.textSize = 20f + unit.setTextColor(getColor(R.color.color_ff2422)) + if (!TextUtils.isEmpty(mFontFileName)) { + val mTypeface = Typeface.createFromAsset(context.assets, mFontFileName) + unit.setTypeface(mTypeface) + } else { + unit.typeface = mTypeFace + } + addView(unit, params) + } + + fun setNumber(num: Int, scroll: Boolean = true) { + resetView() + var number = num + while (number > 0) { + val i = number % 10 + mTargetNumbers.add(i) + number /= 10 + } + for (i in mTargetNumbers.indices.reversed()) { + val scrollNumber = ScrollNumber(context) + scrollNumber.setScrollable(scroll) + scrollNumber.setTextColor( + ContextCompat + .getColor(context, mTextColors[i % mTextColors.size]) + ) + scrollNumber.setVelocity(mVelocity * (i + 1)) + scrollNumber.setTextSize(mTextSize) + scrollNumber.setInterpolator(mInterpolator) + if (!TextUtils.isEmpty(mFontFileName)) scrollNumber.setTextFont(mFontFileName) + if (mTypeFace != null) { + scrollNumber.setTextFont(mTypeFace) + } + scrollNumber.setNumber((mTargetNumbers[i] + 1) % 10, mTargetNumbers[i], 0) + mScrollNumbers.add(scrollNumber) + addView(scrollNumber) + } + val params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT) + val unit = TextView(context) + unit.text = "元" + unit.setGravity(Gravity.BOTTOM) + params.bottomMargin = dp2px(3f) + unit.setPadding(0, 0, 0, dp2px(10f)) + unit.textSize = 20f + unit.setTextColor(getColor(R.color.color_ff2422)) + if (!TextUtils.isEmpty(mFontFileName)) { + val mTypeface = Typeface.createFromAsset(context.assets, mFontFileName) + unit.setTypeface(mTypeface) + } else { + unit.typeface = mTypeFace + } + addView(unit, params) + } + + private fun resetView() { + mTargetNumbers.clear() + mScrollNumbers.clear() + removeAllViews() + } + + fun setNumber(from: Int, to: Int, scroll: Boolean = true) { + if (to < from) throw kotlin.UnsupportedOperationException("'to' value must > 'from' value") + resetView() + // operate to + var number = to + var count = 0 + while (number > 0) { + val i = number % 10 + mTargetNumbers.add(i) + number /= 10 + count++ + } + // operate from + number = from + while (count > 0) { + val i = number % 10 + mPrimaryNumbers.add(i) + number /= 10 + count-- + } + for (i in mTargetNumbers.indices.reversed()) { + val scrollNumber = ScrollNumber(context) + scrollNumber.setScrollable(scroll) + scrollNumber.setTextColor( + ContextCompat + .getColor(context, mTextColors[i % mTextColors.size]) + ) + scrollNumber.setTextSize(mTextSize) + if (!TextUtils.isEmpty(mFontFileName)) scrollNumber.setTextFont(mFontFileName) + if (mTypeFace != null) { + scrollNumber.setTextFont(mTypeFace) + } + scrollNumber.setNumber(mPrimaryNumbers[i], mTargetNumbers[i], (i * 10).toLong()) + mScrollNumbers.add(scrollNumber) + addView(scrollNumber) + } + } + + fun setTextColors(@ColorRes textColors: IntArray?) { + require(!(textColors == null || textColors.isEmpty())) { "color array couldn't be empty!" } + mTextColors = textColors + for (i in mScrollNumbers.indices.reversed()) { + val scrollNumber = mScrollNumbers[i] + scrollNumber.setTextColor( + ContextCompat + .getColor(context, mTextColors[i % mTextColors.size]) + ) + } + } + + fun setTextSize(textSize: Int) { + require(textSize > 0) { "text size must > 0!" } + mTextSize = textSize + for (s in mScrollNumbers) { + s.setTextSize(textSize) + } + } + + fun setInterpolator(interpolator: Interpolator?) { + if (interpolator == null) throw kotlin.IllegalArgumentException("interpolator couldn't be null") + mInterpolator = interpolator + for (s in mScrollNumbers) { + s.setInterpolator(interpolator) + } + } + + fun setTextFont(fileName: String?) { + if (TextUtils.isEmpty(fileName)) throw kotlin.IllegalArgumentException("file name is null") + mFontFileName = fileName + for (s in mScrollNumbers) { + s.setTextFont(fileName) + } + } + + fun setTextFont(typeface: Typeface?) { + mTypeFace = typeface + for (s in mScrollNumbers) { + s.setTextFont(mTypeFace) + } + } + + fun setScrollVelocity(@IntRange(from = 0, to = 1000) velocity: Int) { + mVelocity = velocity + for (s in mScrollNumbers) { + s.setVelocity(velocity) + } + } + + private fun dp2px(dpVal: Float): Int { + return TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + dpVal, resources.displayMetrics + ).toInt() + } + + private fun sp2px(dpVal: Float): Int { + return TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_SP, + dpVal, resources.displayMetrics + ).toInt() + } + + companion object { + fun isNumeric(str: String?): Boolean { + if (TextUtils.isEmpty(str)) return false + val pattern = Pattern.compile("[0-9]*\\.?[0-9]+") + val isNum = pattern.matcher(str!!) + return isNum.matches() + } + } +} diff --git a/app/src/main/java/com/cheng/blzb/widget/ScrollNumber.kt b/app/src/main/java/com/cheng/blzb/widget/ScrollNumber.kt new file mode 100644 index 0000000..364aeb8 --- /dev/null +++ b/app/src/main/java/com/cheng/blzb/widget/ScrollNumber.kt @@ -0,0 +1,242 @@ +package com.devcon.audi.view + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Rect +import android.graphics.Typeface +import android.text.TextUtils +import android.util.AttributeSet +import android.util.TypedValue +import android.view.View +import android.view.animation.AccelerateDecelerateInterpolator +import android.view.animation.Interpolator +import androidx.annotation.IntRange +import kotlin.math.min + +/** + * @Author zhengdaquan + * @Date 2024/10/27 15:25 + * @Describe + */ +class ScrollNumber @JvmOverloads constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) { + /** + * number to - number from + */ + private var mDeltaNum = 0 + + /** + * the current showing number + */ + private var mCurNum = 0 + + /** + * the next showing number + */ + private var mNextNum = 0 + + /** + * the target number + */ + private var mTargetNum = 0 + + /** + * number offset + */ + private var mOffset = 0f + private val mPaint: Paint + private var mInterpolator: Interpolator = AccelerateDecelerateInterpolator() + private var mTextCenterX = 0f + private var mTextHeight = 0 + private val mTextBounds = Rect() + private var mTextSize = sp2px(130f) + private var mTextColor = -0x1000000 + private var mTypeface: Typeface? = null + private var mVelocity = DEFAULT_VELOCITY + private var mScrollable = true + fun setVelocity(@IntRange(from = 0, to = 1000) velocity: Int) { + mVelocity = velocity + } + + fun setNumber(from: Int, to: Int, delay: Long) { + postDelayed({ + setFromNumber(from) + setTargetNumber(to) + mDeltaNum = to - from + }, delay) + } + + fun setTextSize(textSize: Int) { + mTextSize = sp2px(textSize.toFloat()) + mPaint.textSize = mTextSize.toFloat() + measureTextHeight() + requestLayout() + invalidate() + } + + fun setTextFont(fileName: String?) { + require(!TextUtils.isEmpty(fileName)) { "please check file name end with '.ttf' or '.otf'" } + mTypeface = Typeface.createFromAsset(context.assets, fileName) + if (mTypeface == null) throw kotlin.RuntimeException("please check your font!") + mPaint.setTypeface(mTypeface) + requestLayout() + invalidate() + } + + fun setTextFont(typeface: Typeface?) { + mTypeface = typeface + if (mTypeface == null) throw kotlin.RuntimeException("please check your font!") + mPaint.setTypeface(mTypeface) + requestLayout() + invalidate() + } + + fun setTextColor(mTextColor: Int) { + this.mTextColor = mTextColor + mPaint.setColor(mTextColor) + invalidate() + } + + fun setInterpolator(interpolator: Interpolator) { + mInterpolator = interpolator + } + + private fun measureTextHeight() { + mPaint.getTextBounds(mCurNum.toString() + "", 0, 1, mTextBounds) + mTextHeight = mTextBounds.height() + } + + override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { + val width = measureWidth(widthMeasureSpec) + val height = measureHeight(heightMeasureSpec) + setMeasuredDimension(width, height) + mTextCenterX = (measuredWidth - getPaddingLeft() - getPaddingRight() ushr 1).toFloat() + } + + private fun measureHeight(measureSpec: Int): Int { + val mode = MeasureSpec.getMode(measureSpec) + val `val` = MeasureSpec.getSize(measureSpec) + var result = 0 + when (mode) { + MeasureSpec.EXACTLY -> result = `val` + MeasureSpec.AT_MOST, MeasureSpec.UNSPECIFIED -> { + mPaint.getTextBounds("0", 0, 1, mTextBounds) + result = mTextBounds.height() + } + } + result = if (mode == MeasureSpec.AT_MOST) min(result.toDouble(), `val`.toDouble()).toInt() else result + return result + paddingTop + paddingBottom + dp2px(40f) + } + + private fun measureWidth(measureSpec: Int): Int { + val mode = MeasureSpec.getMode(measureSpec) + val `val` = MeasureSpec.getSize(measureSpec) + var result = 0 + when (mode) { + MeasureSpec.EXACTLY -> result = `val` + MeasureSpec.AT_MOST, MeasureSpec.UNSPECIFIED -> { + mPaint.getTextBounds("0", 0, 1, mTextBounds) + result = mTextBounds.width() + } + } + result = if (mode == MeasureSpec.AT_MOST) min(result.toDouble(), `val`.toDouble()).toInt() else result + return result + getPaddingLeft() + getPaddingRight() + 15 + } + + override fun onDraw(canvas: Canvas) { + if (mScrollable) { + if (mCurNum != mTargetNum) { + postDelayed(mScrollRunnable, 0) + } + +// Log.d(TAG, "onDraw: curr=" + mCurNum + " target=" + mTargetNum + " offset=" + mOffset); + canvas.translate(0f, mOffset * measuredHeight) + drawSelf(canvas) + drawNext(canvas) + // canvas.restore(); + } else { + val y = measuredHeight / 2 + canvas.drawText(mTargetNum.toString() + "", mTextCenterX, (y + mTextHeight / 2).toFloat(), mPaint) + } + } + + private fun setFromNumber(number: Int) { + if (number < 0 || number > 9) throw kotlin.RuntimeException("invalidate number , should in [0,9]") + calNum(number) + mOffset = 0f + invalidate() + } + + private fun calNum(num: Int) { + var number = num + number = if (number == -1) 9 else number + number = if (number == 10) 0 else number + mCurNum = number + mNextNum = if (number + 1 == 10) 0 else number + 1 + } + + private val mScrollRunnable = Runnable { + val x = (1 - 1.0 * (mTargetNum - mCurNum) / mDeltaNum).toFloat() + // mOffset -= 0.15f * (1 - mInterpolator.getInterpolation(x) + 0.1); + mOffset -= (mVelocity * 0.01f * (1 - mInterpolator.getInterpolation(x) + 0.1)).toFloat() + invalidate() + if (mOffset <= -1) { + mOffset = 0f + calNum(mCurNum + 1) + } + } + + init { + mPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mPaint.textAlign = Paint.Align.CENTER + mPaint.textSize = mTextSize.toFloat() + mPaint.setColor(mTextColor) + if (mTypeface != null) mPaint.setTypeface(mTypeface) + measureTextHeight() + } + + private fun drawNext(canvas: Canvas) { + val y = (measuredHeight * 1.5).toFloat() + canvas.drawText(mNextNum.toString() + "", mTextCenterX, y + mTextHeight / 2, mPaint) + } + + private fun drawSelf(canvas: Canvas) { + val y = measuredHeight / 2 + canvas.drawText(mCurNum.toString() + "", mTextCenterX, (y + mTextHeight / 2).toFloat(), mPaint) + } + + fun setTargetNumber(nextNum: Int) { + mTargetNum = nextNum + invalidate() + } + + fun setScrollable(scroll: Boolean) { + mScrollable = scroll + } + + private fun dp2px(dpVal: Float): Int { + return TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + dpVal, resources.displayMetrics + ).toInt() + } + + private fun sp2px(dpVal: Float): Int { + return TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_SP, + dpVal, resources.displayMetrics + ).toInt() + } + + companion object { + const val TAG = "ScrollNumber" + + /** + * default animation velocity + */ + const val DEFAULT_VELOCITY = 15 + } +} diff --git a/app/src/main/res/drawable/layer_coupon_dialog_bg_checked.xml b/app/src/main/res/drawable/layer_coupon_dialog_bg_checked.xml new file mode 100644 index 0000000..6ec9972 --- /dev/null +++ b/app/src/main/res/drawable/layer_coupon_dialog_bg_checked.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_ff827d_dash_divider_v.xml b/app/src/main/res/drawable/shape_ff827d_dash_divider_v.xml new file mode 100644 index 0000000..ebea441 --- /dev/null +++ b/app/src/main/res/drawable/shape_ff827d_dash_divider_v.xml @@ -0,0 +1,18 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_white_top_cor30.xml b/app/src/main/res/drawable/shape_white_top_cor30.xml new file mode 100644 index 0000000..8999c90 --- /dev/null +++ b/app/src/main/res/drawable/shape_white_top_cor30.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_coupon_activity.xml b/app/src/main/res/layout/dialog_coupon_activity.xml new file mode 100644 index 0000000..d1777df --- /dev/null +++ b/app/src/main/res/layout/dialog_coupon_activity.xml @@ -0,0 +1,97 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_goods_coupon.xml b/app/src/main/res/layout/dialog_goods_coupon.xml new file mode 100644 index 0000000..c645fdf --- /dev/null +++ b/app/src/main/res/layout/dialog_goods_coupon.xml @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_red_packet_activity.xml b/app/src/main/res/layout/dialog_red_packet_activity.xml new file mode 100644 index 0000000..7dbdafc --- /dev/null +++ b/app/src/main/res/layout/dialog_red_packet_activity.xml @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_select_coupon.xml b/app/src/main/res/layout/dialog_select_coupon.xml index dba3a67..6d6b6ab 100644 --- a/app/src/main/res/layout/dialog_select_coupon.xml +++ b/app/src/main/res/layout/dialog_select_coupon.xml @@ -1,82 +1,113 @@ - + android:layout_height="wrap_content"> - + + + android:layout_height="0dp" + android:layout_marginStart="@dimen/dp_12" + android:layout_marginEnd="@dimen/dp_12" + app:layout_constraintTop_toBottomOf="@id/iv_close" + android:layout_marginTop="@dimen/dp_40" + android:background="@mipmap/ic_coupon_dialog_top" + app:layout_constraintDimensionRatio="h,1053:390" /> - + - + - + - + + android:layout_height="420dp"> - + android:layout_marginTop="@dimen/dp_6" + app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" + app:layout_constraintBottom_toTopOf="@id/layout_bottom" + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_weight="1" + tools:listitem="@layout/listitem_coupon_dialog" /> + + + + + - - \ No newline at end of file + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_home.xml b/app/src/main/res/layout/fragment_home.xml index 51bea49..0d9dcee 100644 --- a/app/src/main/res/layout/fragment_home.xml +++ b/app/src/main/res/layout/fragment_home.xml @@ -208,4 +208,24 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_vip.xml b/app/src/main/res/layout/fragment_vip.xml index f7176de..59bbb9f 100644 --- a/app/src/main/res/layout/fragment_vip.xml +++ b/app/src/main/res/layout/fragment_vip.xml @@ -323,87 +323,44 @@ - - + - - - - - - - - - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/listitem_coupon_dialog.xml b/app/src/main/res/layout/listitem_coupon_dialog.xml index 13a7a01..550945e 100644 --- a/app/src/main/res/layout/listitem_coupon_dialog.xml +++ b/app/src/main/res/layout/listitem_coupon_dialog.xml @@ -1,61 +1,73 @@ - + android:layout_height="@dimen/dp_80" + android:layout_marginStart="@dimen/dp_12" + android:layout_marginTop="@dimen/dp_14" + android:layout_marginEnd="@dimen/dp_12" + android:background="@mipmap/ic_coupon_dialog_bg_default"> + + + + + + + + + - - - - - - - + android:id="@+id/layout_info" + android:layout_width="0dp" + android:layout_height="match_parent" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toEndOf="@id/layout_amount" + app:layout_constraintTop_toTopOf="parent"> - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/listitem_goods_coupon.xml b/app/src/main/res/layout/listitem_goods_coupon.xml new file mode 100644 index 0000000..263cc32 --- /dev/null +++ b/app/src/main/res/layout/listitem_goods_coupon.xml @@ -0,0 +1,54 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_anim1.webp b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_anim1.webp new file mode 100644 index 0000000..ab1262d Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_anim1.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_anim2.webp b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_anim2.webp new file mode 100644 index 0000000..ff4c3cb Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_anim2.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_bg.webp b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_bg.webp new file mode 100644 index 0000000..1c9a3d3 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_bg.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_btn.webp b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_btn.webp new file mode 100644 index 0000000..2f71eee Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_btn.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_threshold_bg.webp b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_threshold_bg.webp new file mode 100644 index 0000000..74f196f Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_activity_coupon_threshold_bg.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_checked.webp b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_checked.webp new file mode 100644 index 0000000..a94f219 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_checked.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_default.webp b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_default.webp new file mode 100644 index 0000000..32c9a1c Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_default.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_disable.webp b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_disable.webp new file mode 100644 index 0000000..9884adb Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_bg_disable.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_checked_arrow.webp b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_checked_arrow.webp new file mode 100644 index 0000000..2742c1d Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_checked_arrow.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_checked_right.webp b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_checked_right.webp new file mode 100644 index 0000000..81bf73b Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_checked_right.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_icon.png b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_icon.png new file mode 100644 index 0000000..543f024 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_icon.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_indicator.webp b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_indicator.webp new file mode 100644 index 0000000..5ca2138 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_indicator.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_top.webp b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_top.webp new file mode 100644 index 0000000..f6465ba Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_coupon_dialog_top.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_bg.png b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_bg.png new file mode 100644 index 0000000..754febb Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_bg.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_desc.png b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_desc.png new file mode 100644 index 0000000..58332ea Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_desc.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_fg.png b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_fg.png new file mode 100644 index 0000000..94d4104 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_fg.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_light.png b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_light.png new file mode 100644 index 0000000..4b061ee Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_light.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_star.png b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_star.png new file mode 100644 index 0000000..bfbe6f3 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_goods_coupon_star.png differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet1.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet1.webp new file mode 100644 index 0000000..784d3e0 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet1.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet2.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet2.webp new file mode 100644 index 0000000..8fd7272 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet2.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet_anim.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_anim.webp new file mode 100644 index 0000000..126a9ea Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_anim.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet_btn1.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_btn1.webp new file mode 100644 index 0000000..60761aa Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_btn1.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet_btn2.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_btn2.webp new file mode 100644 index 0000000..09f2232 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_btn2.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin1.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin1.webp new file mode 100644 index 0000000..6417683 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin1.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin2.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin2.webp new file mode 100644 index 0000000..8a29950 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin2.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin3.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin3.webp new file mode 100644 index 0000000..62b35bf Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin3.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin4.webp b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin4.webp new file mode 100644 index 0000000..74bb812 Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_red_packet_coin4.webp differ diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 9b0ca5d..062e839 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -44,4 +44,12 @@ + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index c953cdb..a94f61f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -70,7 +70,7 @@ #727686 #676E87 #54230C - #F94747 + #F03D36 #896451 #54220B #D5DAE5 @@ -136,4 +136,8 @@ #05ACE8 #6DD9F2 #E6FFFF + #FF2422 + #EF2833 + #848484 + #A1A1A1 \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 1031aa0..b168e34 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -93,5 +93,6 @@ 190dp 180dp 280dp + 28sp \ No newline at end of file