完善柱状图

This commit is contained in:
wangyu4life 2026-01-28 23:47:05 +08:00
parent 36467af122
commit dba4cdd23f
10 changed files with 125 additions and 88 deletions

View File

@ -3,7 +3,6 @@ package com.cheng.blzb.ui.fragment.guide
import androidx.lifecycle.MutableLiveData
import com.cheng.blzb.bean.AreaEntity
import com.cheng.blzb.bean.GuideTotalBidEntity
import com.cheng.blzb.bean.GuideUpdateEntity
import com.cheng.blzb.bean.HotWordEntity
import com.cheng.blzb.net.ApiFactory
import com.example.base.extensions.toast
@ -15,7 +14,6 @@ class GuideViewModel :BaseViewModel(){
val cityLiveData = MutableLiveData<List<AreaEntity>>()
val userCityLiveData = MutableLiveData<AreaEntity>()
val totalLiveData = MutableLiveData<GuideTotalBidEntity>()
val updateLiveData = MutableLiveData<List<GuideUpdateEntity>>()
fun getHotWordList() {
launchOnUiTryCatch({
@ -64,16 +62,4 @@ class GuideViewModel :BaseViewModel(){
L.d(it)
})
}
fun getUpdateNum() {
launchOnUiTryCatch({
val response = ApiFactory.apiService.getUpdateNum("8")
if (response.status) {
updateLiveData.postValue(response.data)
} else toast(response.message, true)
}, {
setError(it)
L.d(it)
})
}
}

View File

@ -7,6 +7,7 @@ import android.os.Build
import android.text.TextUtils
import android.view.View
import android.view.ViewTreeObserver.OnGlobalLayoutListener
import android.widget.TextView
import android.window.OnBackInvokedDispatcher
import androidx.activity.addCallback
import androidx.core.animation.addListener
@ -148,6 +149,33 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
requireActivity().startActivity<MainActivity>()
}
binding.tvTab1.onClick {
binding.layoutTotalInfo.visible()
binding.layoutChart.gone()
updateTabStyle(binding.tvTab1)
}
binding.tvTab2.onClick {
binding.layoutTotalInfo.gone()
binding.layoutChart.visible()
updateTabStyle(binding.tvTab2)
setChartData()
}
binding.tvTab3.onClick {
binding.layoutTotalInfo.gone()
binding.layoutChart.visible()
updateTabStyle(binding.tvTab3)
setChartData()
}
binding.tvTab4.onClick {
binding.layoutTotalInfo.gone()
binding.layoutChart.visible()
updateTabStyle(binding.tvTab4)
setChartData()
}
binding.tvAliPay.onClick {
payType = 1
checkPayType()
@ -211,7 +239,7 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
}
}
@SuppressLint("NotifyDataSetChanged")
@SuppressLint("NotifyDataSetChanged", "SetTextI18n")
override fun initObserve() {
super.initObserve()
mViewModel.goodsListLiveData.observe(this) { list ->
@ -248,7 +276,6 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
mViewModel.updateLiveData.observe(this) {
updateInfoList.clear()
updateInfoList.addAll(it)
setChartData()
binding.tvChartTitle.text = "月份:${Calendar.getInstance().get(Calendar.MONTH) + 1}月 单位:条"
}
@ -415,6 +442,25 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
animSet.start()
}
private fun updateTabStyle(textView: TextView) {
binding.tvTab1.setTextColor(getColor(R.color.color_9bbbdf))
binding.tvTab2.setTextColor(getColor(R.color.color_9bbbdf))
binding.tvTab3.setTextColor(getColor(R.color.color_9bbbdf))
binding.tvTab4.setTextColor(getColor(R.color.color_9bbbdf))
binding.tvTab1.setBackgroundResource(R.mipmap.ic_guide_vip_tab_default)
binding.tvTab2.setBackgroundResource(R.mipmap.ic_guide_vip_tab_default)
binding.tvTab3.setBackgroundResource(R.mipmap.ic_guide_vip_tab_default)
binding.tvTab4.setBackgroundResource(R.mipmap.ic_guide_vip_tab_default)
textView.setTextColor(getColor(R.color.white))
textView.setBackgroundResource(R.mipmap.ic_guide_vip_tab_checked)
val infoRotateAnim = ObjectAnimator.ofFloat(binding.layoutInfo, "rotationX", 90f, -10f, 10f, 0f)
infoRotateAnim.duration = 1000
infoRotateAnim.start()
}
private fun checkPayType() {
if (payType == 0) {
val start1 = ContextCompat.getDrawable(requireContext(), R.mipmap.ic_wx_pay)
@ -549,7 +595,7 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
countdownDisposable = RxCountDown.countdown(totalSeconds)
.subscribe {
setCountdownTime(it)
if (it == 0L) {
if (it == 0L && !isStateSaved) {
val f = GuideSaleDialog.newInstance()
f.setOnSelectListener { seconds ->
totalSeconds = seconds
@ -561,6 +607,7 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
}
//格式化倒计时
@SuppressLint("DefaultLocale")
private fun setCountdownTime(seconds: Long) {
val minutes = seconds / 60
val hours = minutes / 60

View File

@ -5,9 +5,10 @@ import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.animation.addListener
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.chad.library.adapter.base.BaseQuickAdapter
import com.chad.library.adapter.base.viewholder.BaseViewHolder
@ -22,32 +23,54 @@ class BarChartView @JvmOverloads constructor(
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {
private val xAxisAdapter by lazy { XAxisAdapter() }
private val yAxisAdapter by lazy { YAxisAdapter() }
private val yAxisRowCount = 6
private var maxValue = 0f
private var maxYAxisValue = 0f
init {
val view = inflate(context, R.layout.layout_bar_chart, null)
addView(view, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
/**
* 设置X轴数据
*/
private fun setXAxisData(data: List<XAxisValue>, type: Int = 1) {
val rvXAxis = RecyclerView(context)
val adapter = XAxisAdapter(type)
rvXAxis.adapter = adapter
rvXAxis.layoutManager = GridLayoutManager(context, 7)
val xAxisLp = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
xAxisLp.marginStart = DensityUtils.dp2px(50f)
xAxisLp.marginEnd = DensityUtils.dp2px(20f)
rvXAxis.layoutParams = xAxisLp
addView(rvXAxis)
adapter.setList(data)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
/**
* 设置Y轴数据
*/
private fun setYAxisData(data: List<YAxisValue>) {
val rvYAxis = RecyclerView(context)
val adapter = YAxisAdapter()
rvYAxis.adapter = adapter
rvYAxis.layoutManager = LinearLayoutManager(context)
val yAxisLp = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
yAxisLp.bottomMargin = DensityUtils.dp2px(15f)
rvYAxis.layoutParams = yAxisLp
addView(rvYAxis)
val rvX = findViewById<RecyclerView>(R.id.rv_x)
rvX.adapter = xAxisAdapter
val rvY = findViewById<RecyclerView>(R.id.rv_y)
rvY.adapter = yAxisAdapter
adapter.setList(data)
}
fun setData(xValueList: List<XAxisValue>) {
/**
* 设置数据
*/
fun setData(vararg xValueLists: List<XAxisValue>) {
removeAllViews()
val allValueList = mutableListOf<XAxisValue>()
xValueLists.forEach { allValueList.addAll(it) }
val yValueList = mutableListOf<YAxisValue>()
val rowValue = xValueList.maxBy { it.value }.value / yAxisRowCount
val rowValue = allValueList.maxBy { it.value }.value / yAxisRowCount
val value = if (rowValue > 10) {
var bigNum = rowValue.toInt().toFloat()
val numLength = rowValue.toInt().toString().length
@ -64,40 +87,52 @@ class BarChartView @JvmOverloads constructor(
for (i in 0 .. yAxisRowCount) {
yValueList.add(0, YAxisValue(i * value))
}
maxValue = value * yAxisRowCount
maxYAxisValue = value * yAxisRowCount
xAxisAdapter.setList(xValueList)
yAxisAdapter.setList(yValueList)
setYAxisData(yValueList)
xValueLists.forEachIndexed { index, xAxisValues ->
setXAxisData(xAxisValues, index + 1)
}
}
data class XAxisValue(val value: Float, val date: String)
data class XAxisValue(val value: Float, val date: String, var anim: Boolean = true)
data class YAxisValue(val value: Float)
inner class XAxisAdapter : BaseQuickAdapter<XAxisValue, BaseViewHolder>(R.layout.listitem_bar_chart_x) {
inner class XAxisAdapter(val type: Int) : BaseQuickAdapter<XAxisValue, BaseViewHolder>(R.layout.listitem_bar_chart_x) {
override fun convert(holder: BaseViewHolder, item: XAxisValue) {
holder.setText(R.id.tv_value, "${DecimalFormat("0.#").format(item.value)}")
holder.setText(R.id.tv_value, DecimalFormat("0.#").format(item.value))
holder.setText(R.id.tv_date, item.date)
val lp = holder.getView<ImageView>(R.id.iv_bar).layoutParams as ConstraintLayout.LayoutParams
lp.height = (item.value / maxValue * recyclerView.measuredHeight * yAxisRowCount / (yAxisRowCount + 1)).toInt() - DensityUtils.dp2px(6f)
(holder.getView<ImageView>(R.id.iv_bar)).layoutParams = lp
// startAnim(holder.getView(R.id.iv_bar), item)
if (type == 1) {
holder.setImageResource(R.id.iv_bar, R.mipmap.ic_bar_chart1)
holder.setImageResource(R.id.iv_bar_top, R.mipmap.ic_bar_chart_top1)
} else {
holder.setImageResource(R.id.iv_bar, R.mipmap.ic_bar_chart2)
holder.setImageResource(R.id.iv_bar_top, R.mipmap.ic_bar_chart_top2)
}
private fun startAnim(barView: View, item: XAxisValue) {
if (item.anim) {
startAnimation(holder.getView(R.id.iv_bar), item)
item.anim = false
} else {
holder.setVisible(R.id.iv_bar, true)
}
}
private fun startAnimation(barView: View, item: XAxisValue) {
val itemHeight = (item.value / maxYAxisValue * recyclerView.measuredHeight * yAxisRowCount / (yAxisRowCount + 1)).toInt() - DensityUtils.dp2px(10f)
val valueAnim = ValueAnimator.ofFloat(0f, 1f)
valueAnim.addUpdateListener { animator ->
val value = animator.animatedValue as Float
val itemHeight = (item.value / maxValue * recyclerView.measuredHeight * yAxisRowCount / (yAxisRowCount + 1)).toInt() - DensityUtils.dp2px(6f)
val lp = barView.layoutParams as ConstraintLayout.LayoutParams
lp.height = (value * itemHeight).toInt()
val value = animator.animatedValue as Float
lp.height = (value * itemHeight).coerceAtLeast(DensityUtils.dp2px(6f).toFloat()).toInt()
barView.layoutParams = lp
}
valueAnim.addListener(onStart = {
barView.visible()
})
valueAnim.duration = 1000
valueAnim.duration = 2000
valueAnim.startDelay = 1000
valueAnim.start()
}
}
@ -109,7 +144,7 @@ class BarChartView @JvmOverloads constructor(
lp.height = recyclerView.measuredHeight / (yAxisRowCount + 1)
holder.itemView.layoutParams = lp
holder.setText(R.id.tv_value, "${DecimalFormat("0.#").format(item.value)}")
holder.setText(R.id.tv_value, DecimalFormat("0.#").format(item.value))
}
}
}

View File

@ -163,7 +163,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@mipmap/ic_guide_vip_info_bg"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
@ -351,7 +350,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@mipmap/ic_guide_vip_info_bg"
android:visibility="visible"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent">
<TextView

View File

@ -1,28 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_y"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/dp_15"
android:orientation="vertical"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/listitem_bar_chart_y" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_x"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="@dimen/dp_50"
android:layout_marginEnd="@dimen/dp_20"
android:overScrollMode="never"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
app:spanCount="7"
tools:listitem="@layout/listitem_bar_chart_x" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -2,8 +2,7 @@
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools">
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_value"
@ -18,10 +17,11 @@
app:layout_constraintStart_toStartOf="parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_bar_top"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_6"
android:layout_marginBottom="-3dp"
android:src="@mipmap/ic_bar_chart_top"
android:src="@mipmap/ic_bar_chart_top1"
app:layout_constraintBottom_toTopOf="@id/iv_bar"
app:layout_constraintEnd_toEndOf="@id/iv_bar"
app:layout_constraintStart_toStartOf="@id/iv_bar" />
@ -29,12 +29,10 @@
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_bar"
android:layout_width="@dimen/dp_20"
android:layout_height="wrap_content"
android:layout_height="@dimen/dp_6"
android:layout_marginBottom="@dimen/dp_5"
android:scaleType="fitXY"
android:src="@mipmap/ic_bar_chart"
android:visibility="visible"
tools:visibility="visible"
android:src="@mipmap/ic_bar_chart1"
app:layout_constraintBottom_toTopOf="@id/tv_date"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 586 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 534 B