完善引导页功能,添加我的页面企业未认证状态, 2.4.0/240

This commit is contained in:
wangyu 2026-01-29 17:25:43 +08:00
parent dba4cdd23f
commit b358bdb889
20 changed files with 436 additions and 177 deletions

View File

@ -20,8 +20,8 @@ android {
applicationId "com.cheng.BoLe"
minSdk 26
targetSdk 34
versionCode 200
versionName "2.0.0"
versionCode 240
versionName "2.4.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {

View File

@ -116,8 +116,7 @@ class LauncherActivity : BaseActivity() {
LoginActivity.start(this@LauncherActivity)
}
} else {
// startActivity<MainActivity>()
startActivity<GuideActivity>()
startActivity<MainActivity>()
}
finish()
}

View File

@ -60,6 +60,7 @@ class GuideFragment : BaseFragment<FragmentGuideContentBinding, GuideViewModel>(
super.initData()
mViewModel.getHotWordList()
mViewModel.getCityList()
mViewModel.getBidTypeList()
EventReportManager.eventReport(EventConstants.GUIDE_LAUNCH, "", "")
}
@ -93,6 +94,10 @@ class GuideFragment : BaseFragment<FragmentGuideContentBinding, GuideViewModel>(
UserConfigManager.saveAreaList(it)
}
mViewModel.bidTypeLiveData.observe(this) {
UserConfigManager.saveBidTypes(it)
}
val guideEvent = RxBus.defaultInstance.toObservable(GuideEvent::class.java).subscribe {
if (it.toIndex < fragmentList.size) {
binding.viewPager.currentItem = it.toIndex

View File

@ -2,6 +2,7 @@ package com.cheng.blzb.ui.fragment.guide
import androidx.lifecycle.MutableLiveData
import com.cheng.blzb.bean.AreaEntity
import com.cheng.blzb.bean.BidTypeEntity
import com.cheng.blzb.bean.GuideTotalBidEntity
import com.cheng.blzb.bean.HotWordEntity
import com.cheng.blzb.net.ApiFactory
@ -12,6 +13,7 @@ import com.example.base.viewmodel.BaseViewModel
class GuideViewModel :BaseViewModel(){
val hotWordLiveData = MutableLiveData<List<HotWordEntity>>()
val cityLiveData = MutableLiveData<List<AreaEntity>>()
val bidTypeLiveData = MutableLiveData<List<BidTypeEntity>>()
val userCityLiveData = MutableLiveData<AreaEntity>()
val totalLiveData = MutableLiveData<GuideTotalBidEntity>()
@ -39,6 +41,18 @@ class GuideViewModel :BaseViewModel(){
})
}
fun getBidTypeList() {
launchOnUiTryCatch({
val response = ApiFactory.apiService.getBidTypeList()
if (response.status) {
bidTypeLiveData.postValue(response.data)
} else toast(response.message, true)
}, {
setError(it)
L.d(it)
})
}
fun getUserCity() {
launchOnUiTryCatch({
val response = ApiFactory.apiService.getUserCity()

View File

@ -11,7 +11,9 @@ import com.example.base.decoration.GridSpaceItemDecoration
import com.example.base.extensions.onClick
import com.example.base.utils.DensityUtils
class GuideItem2Area2Adapter(val area1Adapter: GuideItem2Area1Adapter): BaseQuickAdapter<AreaEntity, BaseViewHolder>(R.layout.listitem_guide_area2) {
class GuideItem2Area2Adapter(): BaseQuickAdapter<AreaEntity, BaseViewHolder>(R.layout.listitem_guide_area2) {
private var mListener: (() -> Unit)? = null
@SuppressLint("NotifyDataSetChanged")
override fun convert(holder: BaseViewHolder, item: AreaEntity) {
val tvName = holder.getView<TextView>(R.id.tv_name)
@ -38,6 +40,7 @@ class GuideItem2Area2Adapter(val area1Adapter: GuideItem2Area1Adapter): BaseQuic
val cityItem = cityAdapter.getItem(position)
cityItem.isChecked = !cityItem.isChecked
notifyDataSetChanged()
mListener?.invoke()
}
tvCheckAll.onClick {
@ -59,16 +62,8 @@ class GuideItem2Area2Adapter(val area1Adapter: GuideItem2Area1Adapter): BaseQuic
}
}
notifyDataSetChanged()
mListener?.invoke()
}
tvName.onClick {
data.find { it.isChecked }?.isChecked = false
item.isChecked = true
notifyDataSetChanged()
area1Adapter.notifyDataSetChanged()
}
holder.setGone(R.id.rv_city, !item.isChecked)
}
private fun updateCheckState(textView: TextView, isCheckAll: Boolean) {
@ -78,4 +73,8 @@ class GuideItem2Area2Adapter(val area1Adapter: GuideItem2Area1Adapter): BaseQuic
textView.setCompoundDrawablesWithIntrinsicBounds(R.mipmap.ic_check_false, 0, 0, 0)
}
}
fun setOnCityClickListener(listener: () -> Unit) {
this.mListener = listener
}
}

View File

@ -4,6 +4,8 @@ import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import androidx.core.animation.addListener
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.cheng.blzb.bean.AreaEntity
import com.cheng.blzb.common.Constants
import com.cheng.blzb.databinding.FragmentGuideItem2Binding
@ -13,6 +15,7 @@ import com.cheng.blzb.ui.fragment.guide.GuideFragment
import com.cheng.blzb.ui.fragment.guide.GuideViewModel
import com.cheng.blzb.ui.fragment.guide.adapter.GuideItem2Area1Adapter
import com.cheng.blzb.ui.fragment.guide.adapter.GuideItem2Area2Adapter
import com.cheng.blzb.widget.LinearTopSmoothScroller
import com.example.base.common.RxBus
import com.example.base.extensions.onClick
import com.example.base.extensions.toast
@ -24,7 +27,7 @@ import com.example.base.utils.ScreenUtils
class GuideItem2Fragment: BaseFragment<FragmentGuideItem2Binding, GuideViewModel>() {
private val area1Adapter by lazy { GuideItem2Area1Adapter() }
private val area2Adapter by lazy { GuideItem2Area2Adapter(area1Adapter) }
private val area2Adapter by lazy { GuideItem2Area2Adapter() }
private var canClick = false
@ -36,20 +39,13 @@ class GuideItem2Fragment: BaseFragment<FragmentGuideItem2Binding, GuideViewModel
binding.rvArea2.adapter = area2Adapter
}
override fun initData() {
super.initData()
binding.tvCityCount.text = "${UserConfigManager.getCityList().size}"
val areaList = UserConfigManager.getAreaList()
areaList.add(0, AreaEntity(0, name = "全国地区"))
areaList.forEach { area ->
area.children.forEach { it.isChecked = true }
}
area1Adapter.setList(areaList)
area2Adapter.setList(areaList)
}
override fun onLazyLoad() {
super.onLazyLoad()
val areaList = UserConfigManager.getAreaList()
areaList.add(0, AreaEntity(0, name = "全国地区", isChecked = true))
area1Adapter.setList(areaList)
area2Adapter.setList(areaList)
startAnim()
}
@ -60,10 +56,33 @@ class GuideItem2Fragment: BaseFragment<FragmentGuideItem2Binding, GuideViewModel
val item = area1Adapter.getItem(position)
area1Adapter.data.find { it.isChecked }?.isChecked = false
item.isChecked = true
area1Adapter.notifyDataSetChanged()
area2Adapter.notifyDataSetChanged()
scrollToPosition(position)
}
area2Adapter.setOnCityClickListener {
val selectList = mutableListOf<AreaEntity>()
area2Adapter.data.forEach { area ->
selectList.addAll(area.children.filter { it.isChecked })
}
binding.tvCityCount.text = "${selectList.size}"
}
binding.rvArea2.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
super.onScrolled(recyclerView, dx, dy)
val layoutManager = binding.rvArea2.layoutManager as LinearLayoutManager
val firstPosition = layoutManager.findFirstCompletelyVisibleItemPosition()
if (firstPosition >= 0) {
val firstItem = area1Adapter.getItem(firstPosition)
area1Adapter.data.find { it.isChecked }?.isChecked = false
firstItem.isChecked = true
area1Adapter.notifyDataSetChanged()
}
}
})
binding.btnNext.onClick {
if (canClick) {
val selectList = mutableListOf<AreaEntity>()
@ -80,6 +99,13 @@ class GuideItem2Fragment: BaseFragment<FragmentGuideItem2Binding, GuideViewModel
}
}
private fun scrollToPosition(position: Int) {
binding.rvArea2.layoutManager?.startSmoothScroll(
LinearTopSmoothScroller(requireContext(), true)
.apply { targetPosition = position }
)
}
private fun startAnim() {
val titleTransAnim = ObjectAnimator.ofFloat(binding.ivTitle, "translationX", -DensityUtils.dp2px(200f).toFloat(), 0f)
titleTransAnim.duration = 1000

View File

@ -38,8 +38,8 @@ class GuideItem3Fragment : BaseFragment<FragmentGuideItem3Binding, GuideViewMode
binding.tvName8.typeface = Constants.douyinsansB
}
override fun initData() {
super.initData()
override fun onLazyLoad() {
super.onLazyLoad()
val hotWords = UserConfigManager.getGuideKeywords()
hotWords.forEachIndexed { index, item ->
when(index) {
@ -80,10 +80,7 @@ class GuideItem3Fragment : BaseFragment<FragmentGuideItem3Binding, GuideViewMode
}
}
}
}
override fun onLazyLoad() {
super.onLazyLoad()
startAnim()
}

View File

@ -119,7 +119,7 @@ class GuideItem5Fragment : BaseFragment<FragmentGuideItem5Binding, GuideViewMode
binding.ivProgressMask.visible()
}
}
progressMaskAnim.addListener(onEnd = {
progressMaskAnim.addListener(onStart = {
startProgressAnim()
val pagFile = PAGFile.Load(requireContext().assets, "effects/guide_bubble.pag")

View File

@ -2,6 +2,7 @@ package com.cheng.blzb.ui.fragment.guide.vip
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.os.Build
import android.text.TextUtils
@ -45,6 +46,7 @@ import com.example.base.extensions.getColor
import com.example.base.extensions.getDD
import com.example.base.extensions.gone
import com.example.base.extensions.onClick
import com.example.base.extensions.setStatusBarLight
import com.example.base.extensions.toast
import com.example.base.extensions.visible
import com.example.base.ui.BaseFragment
@ -64,7 +66,6 @@ import org.jetbrains.anko.startActivity
import java.text.DecimalFormat
import java.util.Calendar
import java.util.concurrent.TimeUnit
import kotlin.collections.forEachIndexed
class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>() {
private val userTipsAdapter by lazy { GuideVipUserAdapter(requireContext(), userTipsList) }
@ -81,6 +82,7 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
private lateinit var api: IWXAPI
private var isAgree = false
private var totalPrice = 0f
private var lastGoodsPrice = 0f
private val goodsAdapter by lazy { GuideVipAdapter() }
@ -93,11 +95,14 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
override fun initView() {
super.initView()
setStatusBarLight(false)
binding.tvTotalInfoCount1.typeface = Constants.dDIN_PRO_M
binding.tvTotalInfoCount2.typeface = Constants.dDIN_PRO_M
binding.tvTotalInfoCount3.typeface = Constants.dDIN_PRO_M
binding.tvTotalInfoCount4.typeface = Constants.dDIN_PRO_M
binding.tvPrice.typeface = Constants.dDIN_PRO_M
binding.tvPay.typeface = Constants.almmsht
binding.rvUser.adapter = userTipsAdapter
@ -109,6 +114,8 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
binding.cbAgree.visibility = if (UserConfigManager.isPayAgreementEnable()) View.VISIBLE else View.GONE
binding.tvAgree.visibility = if (UserConfigManager.isPayAgreementEnable()) View.VISIBLE else View.GONE
binding.tvChartTitle.text = "月份:${Calendar.getInstance().get(Calendar.MONTH) + 1}月 单位:条"
binding.layoutContent.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
override fun onGlobalLayout() {
mViewModel.getGoodsList()
@ -153,27 +160,28 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
binding.layoutTotalInfo.visible()
binding.layoutChart.gone()
updateTabStyle(binding.tvTab1)
setTotalData()
}
binding.tvTab2.onClick {
binding.layoutTotalInfo.gone()
binding.layoutChart.visible()
updateTabStyle(binding.tvTab2)
setChartData()
setDailyChartData()
}
binding.tvTab3.onClick {
binding.layoutTotalInfo.gone()
binding.layoutChart.visible()
updateTabStyle(binding.tvTab3)
setChartData()
setBidTypeChartData()
}
binding.tvTab4.onClick {
binding.layoutTotalInfo.gone()
binding.layoutChart.visible()
updateTabStyle(binding.tvTab4)
setChartData()
setCityChartData()
}
binding.tvAliPay.onClick {
@ -276,7 +284,6 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
mViewModel.updateLiveData.observe(this) {
updateInfoList.clear()
updateInfoList.addAll(it)
binding.tvChartTitle.text = "月份:${Calendar.getInstance().get(Calendar.MONTH) + 1}月 单位:条"
}
val payStatusDisposable = RxBus.defaultInstance.toObservable(PayStatusEvent::class.java).subscribe {
@ -312,27 +319,6 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
arguments?.getSerializable("total") as? GuideTotalBidEntity
}
SpanUtils.with(binding.tvTotalInfoCount1)
.append("${totalInfo?.bidCount}")
.append("")
.setFontSize(10, true)
.create()
SpanUtils.with(binding.tvTotalInfoCount2)
.append("${totalInfo?.hasContact}")
.append("")
.setFontSize(10, true)
.create()
SpanUtils.with(binding.tvTotalInfoCount3)
.append("${totalInfo?.maxMoney}")
.append("亿元")
.setFontSize(10, true)
.create()
SpanUtils.with(binding.tvTotalInfoCount4)
.append("${totalInfo?.yesterdayCount}")
.append("")
.setFontSize(10, true)
.create()
val hotWordListStr = arguments?.getString("hotWords")
if (!TextUtils.isEmpty(hotWordListStr)) {
hotWordChildList.addAll(Gson().fromJson<List<HotWordEntity.Child>>(hotWordListStr, object : TypeToken<List<HotWordEntity.Child>>() {}.type))
@ -346,13 +332,78 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
}
}
private fun setChartData() {
val dateList = getDateList(updateInfoList, System.currentTimeMillis())
val valueList = mutableListOf<BarChartView.XAxisValue>()
updateInfoList.forEachIndexed { index, item ->
valueList.add(BarChartView.XAxisValue(item.count.toFloat(), dateList[index]))
private fun setTotalData() {
totalInfo?.let {
val valueAnim = ValueAnimator.ofFloat(0f, 1f)
valueAnim.duration = 1000
valueAnim.addUpdateListener { animator ->
val percent = animator.animatedValue as Float
SpanUtils.with(binding.tvTotalInfoCount1)
.append("${(it.bidCount * percent).toInt()}")
.append("")
.setFontSize(10, true)
.create()
SpanUtils.with(binding.tvTotalInfoCount2)
.append("${(it.hasContact * percent).toInt()}")
.append("")
.setFontSize(10, true)
.create()
SpanUtils.with(binding.tvTotalInfoCount3)
.append("${(it.maxMoney * percent).toInt()}")
.append("亿元")
.setFontSize(10, true)
.create()
SpanUtils.with(binding.tvTotalInfoCount4)
.append("${(it.yesterdayCount * percent).toInt()}")
.append("")
.setFontSize(10, true)
.create()
}
valueAnim.start()
}
binding.barChart.setData(valueList)
}
private fun setDailyChartData() {
val dateList = getDateList(updateInfoList, System.currentTimeMillis())
val dataList = mutableListOf<BarChartView.BarDataSet>()
updateInfoList.forEachIndexed { index, item ->
dataList.add(BarChartView.BarDataSet(dateList[index], listOf(item.count.toFloat())))
}
binding.barChart.setData(dataList)
binding.tvLegend1.gone()
binding.tvLegend2.gone()
}
private fun setBidTypeChartData() {
totalInfo?.let {
val percentArray = intArrayOf(4, 6, 34, 5, 28, 7, 45, 1)
val bidTypeArray = arrayOf("竞争性磋商", "更正公告", "招标公告", "招标计划", "采购公告", "合同公告", "中标公告", "终止公告")
val dataList = mutableListOf<BarChartView.BarDataSet>()
bidTypeArray.forEachIndexed { index, item ->
dataList.add(BarChartView.BarDataSet(item, listOf((it.bidCount * percentArray[index] / 100).toFloat())))
}
binding.barChart.setData(dataList)
binding.tvLegend1.gone()
binding.tvLegend2.gone()
}
}
private fun setCityChartData() {
val array1 = intArrayOf(
134089, 93969, 69947, 186730, 103113, 60920, 32955, 156973, 43129, 232609, 84234, 72890, 44618, 48437, 43981,
102764, 139170, 64218, 45001, 71689, 39366, 113385, 51259, 33253, 31930, 16348, 19388, 17568, 39384, 21928, 5853
)
val array2 = intArrayOf(
14467, 9491, 7524, 6651, 6036, 5505, 4962, 4584, 4397, 3982, 3609, 3472, 3295, 3157, 3103,
3078, 1907, 1803, 1690, 1555, 1421, 1128, 892, 758, 731, 708, 525, 525, 436, 328, 254
)
val dataList = mutableListOf<BarChartView.BarDataSet>()
UserConfigManager.getCityList().forEachIndexed { index, item ->
dataList.add(BarChartView.BarDataSet(item.name, listOf(array1[index].toFloat(), array2[index].toFloat())))
}
binding.barChart.setData(dataList)
binding.tvLegend1.visible()
binding.tvLegend2.visible()
}
private fun startAnim() {
@ -395,6 +446,7 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
infoRotateAnim.addListener(onStart = {
binding.layoutInfo.visible()
}, onEnd = {
setTotalData()
infoLine1TransAnim.start()
infoLine2TransAnim.start()
})
@ -507,18 +559,24 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
val globalPayType = UserConfigManager.getPayType()
if (list?.find { it == "weixin" } != null && globalPayType.find { it == "weixin" } != null) {
binding.tvWxPay.visible()
binding.viewLine1.visible()
} else {
binding.tvWxPay.gone()
binding.viewLine1.gone()
}
if (list?.find { it == "alipay" } != null && globalPayType.find { it == "alipay" } != null) {
binding.tvAliPay.visible()
binding.viewLine2.visible()
} else {
binding.tvAliPay.gone()
binding.viewLine2.gone()
}
if (list?.find { it == "bank" } != null && globalPayType.find { it == "bank" } != null) {
binding.tvBankPay.visible()
binding.viewLine3.visible()
} else {
binding.tvBankPay.gone()
binding.viewLine3.gone()
}
if (goodsEntity?.pay_type!!.startsWith("weixin") && binding.tvWxPay.isVisible) {
@ -553,14 +611,24 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
@SuppressLint("SetTextI18n")
private fun setPrice(price: Float) {
lastGoodsPrice = totalPrice
totalPrice = if (price < 0) 0f else price
SpanUtils.with(binding.tvPrice)
.append("")
.setFontSize(12, true)
.append(DecimalFormat("0.##").format(totalPrice))
.append(formatPricePeriod(goodsEntity!!.value))
.setFontSize(12, true)
.create()
val valueAnim = ValueAnimator.ofFloat(0f, 1f).setDuration(500)
valueAnim.addUpdateListener {
val percent = it.animatedValue as Float
val diffPrice = totalPrice - lastGoodsPrice
val animPrice = if (percent == 1f) totalPrice * percent else (lastGoodsPrice + diffPrice * percent).toInt()
SpanUtils.with(binding.tvPrice)
.append("")
.setFontSize(12, true)
.append(DecimalFormat("0.##").format(animPrice))
.append(formatPricePeriod(goodsEntity!!.value))
.setFontSize(12, true)
.create()
}
valueAnim.start()
binding.tvSavedPrice.text = "已优惠${DecimalFormat("0.##").format(goodsEntity!!.origin_price.toFloat() - totalPrice)}"
}
@ -652,9 +720,13 @@ class GuideVipFragment: BaseFragment<FragmentGuideVipBinding, GuideVipViewModel>
@SuppressLint("UnsafeOptInUsageError")
private fun setBackPressed() {
if (BuildCompat.isAtLeastT()) {
requireActivity().onBackInvokedDispatcher.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT) {}
requireActivity().onBackInvokedDispatcher.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT) {
requireActivity().startActivity<MainActivity>()
}
} else {
requireActivity().onBackPressedDispatcher.addCallback(this) {}
requireActivity().onBackPressedDispatcher.addCallback(this) {
requireActivity().startActivity<MainActivity>()
}
}
}
}

View File

@ -181,7 +181,10 @@ class MineFragment : BaseFragment<FragmentMineBinding, MineViewModel>() {
else -> binding.tvAuthStatus.gone()
}
} else {
binding.tvAuthStatus.gone()
binding.tvAuthStatus.text = "未认证"
binding.tvAuthStatus.setTextColor(getColor(R.color.color_ff592b))
binding.tvAuthStatus.setCompoundDrawablesWithIntrinsicBounds(R.drawable.shape_auth_fail_dot, 0, 0, 0)
binding.tvAuthStatus.visible()
}
}

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 android.widget.TextView
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
@ -23,54 +24,56 @@ 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 xAxisColumnCount = 6
private val yAxisRowCount = 6
private var maxYAxisValue = 0f
/**
* 设置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)
init {
initXAxisData()
initYAxisData()
}
/**
* 设置Y轴数据
* 初始化X轴
*/
private fun setYAxisData(data: List<YAxisValue>) {
private fun initXAxisData() {
val rvXAxis = RecyclerView(context)
rvXAxis.adapter = xAxisAdapter
rvXAxis.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
val xAxisLp = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
xAxisLp.marginStart = DensityUtils.dp2px(40f)
rvXAxis.layoutParams = xAxisLp
addView(rvXAxis)
}
/**
* 初始化Y轴
*/
private fun initYAxisData() {
val rvYAxis = RecyclerView(context)
val adapter = YAxisAdapter()
rvYAxis.adapter = adapter
rvYAxis.layoutManager = LinearLayoutManager(context)
rvYAxis.adapter = yAxisAdapter
rvYAxis.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
val yAxisLp = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
yAxisLp.bottomMargin = DensityUtils.dp2px(15f)
rvYAxis.layoutParams = yAxisLp
addView(rvYAxis)
adapter.setList(data)
rvYAxis.overScrollMode = OVER_SCROLL_NEVER
addView(rvYAxis, 0)
}
/**
* 设置数据
*/
fun setData(vararg xValueLists: List<XAxisValue>) {
removeAllViews()
val allValueList = mutableListOf<XAxisValue>()
xValueLists.forEach { allValueList.addAll(it) }
fun setData(data: List<BarDataSet>) {
val allValues = mutableListOf<Float>()
data.forEach { allValues.addAll(it.values) }
val yValueList = mutableListOf<YAxisValue>()
val rowValue = allValueList.maxBy { it.value }.value / yAxisRowCount
val rowValue = allValues.maxBy { it } / yAxisRowCount
val value = if (rowValue > 10) {
var bigNum = rowValue.toInt().toFloat()
val numLength = rowValue.toInt().toString().length
@ -89,50 +92,68 @@ class BarChartView @JvmOverloads constructor(
}
maxYAxisValue = value * yAxisRowCount
setYAxisData(yValueList)
xValueLists.forEachIndexed { index, xAxisValues ->
setXAxisData(xAxisValues, index + 1)
}
xAxisAdapter.setList(data.map { XAxisValue(it.key, it.values) })
yAxisAdapter.setList(yValueList)
}
data class XAxisValue(val value: Float, val date: String, var anim: Boolean = true)
data class BarDataSet(val key: String, val values: List<Float>)
data class XAxisValue(val key: String, val values: List<Float>, var anim: Boolean = true)
data class YAxisValue(val value: Float)
inner class XAxisAdapter(val type: Int) : BaseQuickAdapter<XAxisValue, BaseViewHolder>(R.layout.listitem_bar_chart_x) {
inner class XAxisAdapter() : 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_date, item.date)
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)
}
val lp = holder.itemView.layoutParams as RecyclerView.LayoutParams
lp.width = recyclerView.measuredWidth / xAxisColumnCount
holder.itemView.layoutParams = lp
if (item.anim) {
startAnimation(holder.getView(R.id.iv_bar), item)
item.anim = false
} else {
holder.setVisible(R.id.iv_bar, true)
holder.setText(R.id.tv_key, item.key)
val container = holder.getView<FrameLayout>(R.id.layout_container)
container.removeAllViews()
item.values.forEachIndexed { index, value ->
val view = inflate(context, R.layout.layout_x_axis_bar, null)
val tvValue = view.findViewById<TextView>(R.id.tv_value)
val ivBar = view.findViewById<ImageView>(R.id.iv_bar)
val ivBarTop = view.findViewById<ImageView>(R.id.iv_bar_top)
tvValue.text = DecimalFormat("0.#").format(value)
if (index == 0) {
ivBar.setImageResource(R.mipmap.ic_bar_chart1)
ivBarTop.setImageResource(R.mipmap.ic_bar_chart_top1)
} else {
ivBar.setImageResource(R.mipmap.ic_bar_chart2)
ivBarTop.setImageResource(R.mipmap.ic_bar_chart_top2)
}
container.addView(view)
val barHeight = (value / maxYAxisValue * recyclerView.measuredHeight * yAxisRowCount / (yAxisRowCount + 1)).toInt() - DensityUtils.dp2px(10f)
if (item.anim) {
startAnimation(ivBar, barHeight)
item.anim = false
} else {
val lp = ivBar.layoutParams as ConstraintLayout.LayoutParams
lp.height = barHeight.coerceAtLeast(DensityUtils.dp2px(7f))
ivBar.layoutParams = lp
ivBar.visible()
}
}
}
private fun startAnimation(barView: View, item: XAxisValue) {
val itemHeight = (item.value / maxYAxisValue * recyclerView.measuredHeight * yAxisRowCount / (yAxisRowCount + 1)).toInt() - DensityUtils.dp2px(10f)
private fun startAnimation(barView: View, height: Int) {
val valueAnim = ValueAnimator.ofFloat(0f, 1f)
valueAnim.addUpdateListener { animator ->
val lp = barView.layoutParams as ConstraintLayout.LayoutParams
val value = animator.animatedValue as Float
lp.height = (value * itemHeight).coerceAtLeast(DensityUtils.dp2px(6f).toFloat()).toInt()
lp.height = (value * height).coerceAtLeast(DensityUtils.dp2px(6f).toFloat()).toInt()
barView.layoutParams = lp
}
valueAnim.addListener(onStart = {
barView.visible()
})
valueAnim.duration = 2000
valueAnim.startDelay = 1000
valueAnim.start()
}
}

View File

@ -0,0 +1,56 @@
package com.cheng.blzb.widget
import android.content.Context
import android.util.DisplayMetrics
import androidx.recyclerview.widget.LinearSmoothScroller
class LinearTopSmoothScroller(val context: Context, needFast: Boolean) : LinearSmoothScroller(context) {
/**
* speed 值越大滚动越慢
*/
private var speed = 0.03f
/**
* @param context context
* @param needFast 是否需要快速滑动
*/
init {
if (needFast) {
setScrollFast()
} else {
setScrollSlowly()
}
}
override fun getVerticalSnapPreference(): Int {
return SNAP_TO_START
}
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics): Float {
return speed / displayMetrics.density
}
/**
* 缓慢滚动
*/
fun setScrollSlowly() {
try {
// 建议不同分辨率设备上的滑动速度相同
speed = context.resources.displayMetrics.density * 0.3f
} catch (e: Exception) {
// do nothing
}
}
/**
* 快速滚动
*/
fun setScrollFast() {
try {
speed = context.resources.displayMetrics.density * 0.03f
} catch (e: Exception) {
// do nothing
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:width="@dimen/dp_6"
android:height="@dimen/dp_6" />
<solid android:color="#00D9EA" />
</shape>

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<size
android:width="@dimen/dp_6"
android:height="@dimen/dp_6" />
<solid android:color="#FF9E3A" />
</shape>

View File

@ -186,7 +186,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:text="43925条"
android:text="0条"
android:textSize="@dimen/sp_20"
app:gradient_endColor="@color/color_6dd9f2"
app:gradient_startColor="@color/white"
@ -231,7 +231,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:text="43925条"
android:text="0条"
android:textSize="@dimen/sp_20"
app:gradient_endColor="@color/color_6dd9f2"
app:gradient_startColor="@color/white"
@ -276,7 +276,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:text="43925条"
android:text="0亿元"
android:textSize="@dimen/sp_20"
app:gradient_endColor="@color/color_6dd9f2"
app:gradient_startColor="@color/white"
@ -321,7 +321,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:text="43925条"
android:text="0条"
android:textSize="@dimen/sp_20"
app:gradient_endColor="@color/color_6dd9f2"
app:gradient_startColor="@color/white"
@ -363,6 +363,37 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_legend1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableStart="@drawable/shape_ff9e3a_dp6"
android:drawablePadding="@dimen/dp_4"
android:gravity="center"
android:text="交易规模"
android:textColor="@color/white"
android:textSize="8sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/tv_chart_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/tv_chart_title" />
<TextView
android:id="@+id/tv_legend2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_10"
android:drawableStart="@drawable/shape_00d9ea_dp6"
android:drawablePadding="@dimen/dp_4"
android:gravity="center"
android:text="标段数"
android:textColor="@color/white"
android:textSize="8sp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/tv_chart_title"
app:layout_constraintEnd_toStartOf="@id/tv_legend1"
app:layout_constraintTop_toTopOf="@id/tv_chart_title" />
<com.cheng.blzb.widget.BarChartView
android:id="@+id/barChart"
android:layout_width="match_parent"
@ -521,8 +552,8 @@
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:background="@mipmap/ic_guide_vip_info_bg"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:background="@mipmap/ic_guide_vip_info_bg">
<TextView
android:id="@+id/tv_wx_pay"
@ -538,6 +569,7 @@
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/view_line1"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"
android:background="#1AFFFFFF"
@ -557,6 +589,7 @@
app:layout_constraintTop_toBottomOf="@id/tv_wx_pay" />
<View
android:id="@+id/view_line2"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"
android:background="#1AFFFFFF"
@ -576,6 +609,7 @@
app:layout_constraintTop_toBottomOf="@id/tv_ali_pay" />
<View
android:id="@+id/view_line3"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_1"
android:background="#1AFFFFFF"

View File

@ -303,9 +303,8 @@
android:layout_marginEnd="@dimen/dp_35"
android:drawableStart="@drawable/shape_auth_fail_dot"
android:drawablePadding="@dimen/dp_6"
android:text="认证失败"
android:text="认证"
android:textColor="@color/color_ff592b"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="@id/tv_auth"
app:layout_constraintEnd_toEndOf="@id/tv_auth"
app:layout_constraintTop_toTopOf="@id/tv_auth" />

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_10"
android:text="14235"
android:textColor="@color/white"
android:textSize="@dimen/sp_10"
app:layout_constraintBottom_toTopOf="@id/iv_bar"
app:layout_constraintEnd_toEndOf="parent"
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_top1"
app:layout_constraintBottom_toTopOf="@id/iv_bar"
app:layout_constraintEnd_toEndOf="@id/iv_bar"
app:layout_constraintStart_toStartOf="@id/iv_bar" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_bar"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_6"
android:scaleType="fitXY"
android:src="@mipmap/ic_bar_chart1"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -4,43 +4,25 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_10"
android:text="14235"
android:textColor="@color/white"
android:textSize="@dimen/sp_10"
app:layout_constraintBottom_toTopOf="@id/iv_bar"
app:layout_constraintEnd_toEndOf="parent"
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_top1"
app:layout_constraintBottom_toTopOf="@id/iv_bar"
app:layout_constraintEnd_toEndOf="@id/iv_bar"
app:layout_constraintStart_toStartOf="@id/iv_bar" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_bar"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_6"
<FrameLayout
android:id="@+id/layout_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginBottom="@dimen/dp_5"
android:scaleType="fitXY"
android:src="@mipmap/ic_bar_chart1"
app:layout_constraintBottom_toTopOf="@id/tv_date"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
app:layout_constraintBottom_toTopOf="@id/tv_key"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/tv_date"
android:id="@+id/tv_key"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:maxLines="1"
android:scrollHorizontally="true"
android:singleLine="true"
android:text="08日"
android:textColor="#8194AF"
android:textSize="@dimen/sp_10"

View File

@ -9,7 +9,7 @@
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="@dimen/dp_30"
android:layout_marginStart="@dimen/dp_10"
android:layout_marginStart="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_2"
android:ellipsize="marquee"
android:gravity="center_vertical"
@ -21,7 +21,7 @@
android:focusable="true"
android:focusableInTouchMode="true"
android:textColor="@color/color_125ffe"
android:textSize="@dimen/sp_13"
android:textSize="@dimen/sp_12"
app:csb_drawablePosition="right"
app:csb_fillColor="#F5F8FF"
app:layout_constraintEnd_toStartOf="@id/iv_delete"
@ -31,7 +31,7 @@
android:id="@+id/iv_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_6"
android:src="@mipmap/ic_guide_item4_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -9,14 +9,14 @@
android:id="@+id/tv_name"
android:layout_width="0dp"
android:layout_height="@dimen/dp_30"
android:layout_marginStart="@dimen/dp_10"
android:layout_marginStart="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_2"
android:gravity="center_vertical"
android:maxLines="1"
android:singleLine="true"
android:text="数字电路"
android:textColor="@color/color_1a1a1a"
android:textSize="@dimen/sp_13"
android:textSize="@dimen/sp_12"
app:csb_drawablePosition="right"
app:csb_fillColor="#F5F8FF"
app:layout_constraintEnd_toStartOf="@id/iv_add"
@ -26,7 +26,7 @@
android:id="@+id/iv_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_8"
android:layout_marginEnd="@dimen/dp_6"
android:src="@mipmap/ic_guide_item4_checked"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"