Skip to content

Commit 8597829

Browse files
authored
Merge pull request #214 from xyoye/dev_4.1.0
Dev 4.1.0
2 parents f752fdf + 8a9ab2b commit 8597829

File tree

327 files changed

+8822
-7633
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

327 files changed

+8822
-7633
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,62 @@
11
package com.xyoye.anime_component.ui.activities.anime_detail
22

3-
import android.graphics.Color
4-
import androidx.core.graphics.BlendModeColorFilterCompat
5-
import androidx.core.graphics.BlendModeCompat
6-
import androidx.fragment.app.Fragment
7-
import androidx.fragment.app.FragmentManager
8-
import androidx.fragment.app.FragmentPagerAdapter
3+
import android.graphics.drawable.ShapeDrawable
4+
import android.graphics.drawable.shapes.RoundRectShape
95
import com.alibaba.android.arouter.facade.annotation.Autowired
106
import com.alibaba.android.arouter.facade.annotation.Route
117
import com.alibaba.android.arouter.launcher.ARouter
8+
import com.google.android.material.tabs.TabLayoutMediator
129
import com.gyf.immersionbar.ImmersionBar
1310
import com.xyoye.anime_component.BR
1411
import com.xyoye.anime_component.R
1512
import com.xyoye.anime_component.databinding.ActivityAnimeDetailBinding
16-
import com.xyoye.anime_component.ui.fragment.anime_episode.AnimeEpisodeFragment
17-
import com.xyoye.anime_component.ui.fragment.anime_intro.AnimeIntroFragment
18-
import com.xyoye.anime_component.ui.fragment.anime_recommend.AnimeRecommendFragment
13+
import com.xyoye.anime_component.ui.adapter.AnimeDetailPageAdapter
14+
import com.xyoye.anime_component.utils.loadAnimeCover
1915
import com.xyoye.common_component.base.BaseActivity
2016
import com.xyoye.common_component.config.RouteTable
21-
import com.xyoye.common_component.extension.*
22-
import com.xyoye.common_component.weight.ToastCenter
23-
import com.xyoye.data_component.data.BangumiData
24-
import kotlin.math.abs
25-
import kotlin.math.max
17+
import com.xyoye.common_component.extension.clamp
18+
import com.xyoye.common_component.extension.dp
19+
import com.xyoye.common_component.extension.isNightMode
20+
import com.xyoye.common_component.extension.loadImage
21+
import com.xyoye.common_component.extension.opacity
22+
import com.xyoye.common_component.extension.setTextColorRes
23+
import com.xyoye.common_component.extension.toResColor
24+
import com.xyoye.data_component.bean.AnimeArgument
25+
import com.xyoye.data_component.bean.ColorRange
26+
import com.xyoye.data_component.enums.AnimeDetailTab
27+
import kotlin.math.absoluteValue
2628

2729
@Route(path = RouteTable.Anime.AnimeDetail)
2830
class AnimeDetailActivity : BaseActivity<AnimeDetailViewModel, ActivityAnimeDetailBinding>() {
2931

3032
@Autowired
3133
@JvmField
32-
var animeId: Int = -1
34+
var animeArgument: AnimeArgument = AnimeArgument()
35+
36+
// 标题栏字体颜色变化范围
37+
private val textColorRange by lazy {
38+
ColorRange(
39+
R.color.text_white_immutable.toResColor(this),
40+
R.color.text_theme.toResColor(this)
41+
)
42+
}
43+
44+
// 页面颜色变化范围
45+
private val pageColorRange by lazy {
46+
ColorRange(
47+
R.color.item_bg_color.toResColor(this),
48+
R.color.theme.toResColor(this)
49+
)
50+
}
51+
52+
// 上一次滚动的百分比
53+
private var lastScrollLimit = 0
54+
55+
// 默认的tab列表
56+
private val defaultTabs = AnimeDetailTab.values()
57+
58+
// 返回事件拦截器
59+
private var backPressInterceptor: (() -> Boolean)? = null
3360

3461
override fun initViewModel() = ViewModelInit(
3562
BR.viewModel,
@@ -48,83 +75,56 @@ class AnimeDetailActivity : BaseActivity<AnimeDetailViewModel, ActivityAnimeDeta
4875
override fun initView() {
4976
ARouter.getInstance().inject(this)
5077

51-
postponeEnterTransition()
52-
5378
title = ""
5479

55-
dataBinding.toolbar.setNavigationOnClickListener { finishAfterTransition() }
80+
initViewPager(defaultTabs)
81+
initListener()
5682

57-
if (animeId == -1) {
58-
ToastCenter.showError("获取番剧信息失败")
59-
return
60-
}
83+
updateUIByScroll(0f)
84+
updateTabLayoutRound(0f)
85+
updateUIbyAnime(animeArgument.title, animeArgument.imageUrl)
6186

62-
dataBinding.appBarLayout.addOnOffsetChangedListener { appBarLayout, verticalOffset ->
63-
//用于计算的偏移及高度,从1/2位置开始计算
64-
val calcRange = appBarLayout.totalScrollRange / 2f
65-
val calcOffset = max(0f, abs(verticalOffset) - calcRange)
66-
val offsetPercent = calcOffset / calcRange
67-
68-
//返回图标颜色
69-
val color = getBackIconColor(offsetPercent)
70-
val colorFilter =
71-
BlendModeColorFilterCompat.createBlendModeColorFilterCompat(
72-
color,
73-
BlendModeCompat.SRC_IN
74-
)
75-
dataBinding.toolbar.navigationIcon?.colorFilter = colorFilter
76-
77-
//标题颜色
78-
val alpha = (255 * offsetPercent).toInt()
79-
val titleTextColor = R.color.text_theme.toResColor()
80-
val titleColor = Color.argb(
81-
alpha,
82-
Color.red(titleTextColor),
83-
Color.green(titleTextColor),
84-
Color.blue(titleTextColor)
85-
)
86-
dataBinding.toolbar.setTitleTextColor(titleColor)
87-
dataBinding.followTv.background?.alpha = 255 - alpha
88-
89-
//状态栏文字颜色
90-
//tips: MIUI深色模式下状态栏字体颜色不受此控制
91-
val isDarkFont = calcOffset > 0 && !isNightMode()
92-
ImmersionBar.with(this)
93-
.transparentBar()
94-
.statusBarDarkFont(isDarkFont)
95-
.init()
96-
}
97-
98-
dataBinding.tabLayout.setupWithViewPager(dataBinding.viewpager)
99-
100-
initObserver()
101-
102-
viewModel.animeIdField.set(animeId.toString())
103-
viewModel.getAnimeDetail(animeId.toString())
87+
viewModel.animeIdField.set(animeArgument.id.toString())
88+
viewModel.animeTitleField.set(animeArgument.title)
89+
viewModel.getAnimeDetail(animeArgument.id.toString())
10490
}
10591

106-
private fun getBackIconColor(percent: Float): Int {
107-
//颜色由白->蓝变化
108-
val startColor = R.color.text_white_immutable.toResColor()
109-
val endColor = R.color.text_theme.toResColor()
110-
111-
val startRed = Color.red(startColor)
112-
val startGreen = Color.green(startColor)
113-
val startBlue = Color.blue(startColor)
114-
115-
val endRed = Color.red(endColor)
116-
val endGreen = Color.green(endColor)
117-
val endBlue = Color.blue(endColor)
92+
private fun initViewPager(tabs: Array<AnimeDetailTab>) {
93+
dataBinding.viewpager.adapter = AnimeDetailPageAdapter(this, tabs)
94+
val mediator = TabLayoutMediator(
95+
dataBinding.tabLayout,
96+
dataBinding.viewpager
97+
) { tab, position ->
98+
tab.text = tabs[position].title
99+
}
100+
mediator.attach()
101+
}
118102

119-
//红绿蓝统一变化即得到相应比例颜色
120-
val diffRed = startRed + ((endRed - startRed) * percent).toInt()
121-
val diffGreen = startRed + ((endGreen - startGreen) * percent).toInt()
122-
val diffBlue = startRed + ((endBlue - startBlue) * percent).toInt()
103+
@Deprecated("Deprecated in Java", ReplaceWith("finish()"))
104+
override fun onBackPressed() {
105+
// 如果有返回事件拦截器,优先执行拦截器
106+
if (backPressInterceptor?.invoke() == true) {
107+
return
108+
}
123109

124-
return Color.rgb(diffRed, diffGreen, diffBlue)
110+
// 重写以防止返回动画
111+
finish()
125112
}
126113

127-
private fun initObserver() {
114+
private fun initListener() {
115+
dataBinding.appBarLayout.addOnOffsetChangedListener { layout, offset ->
116+
val percent = offset.absoluteValue.toFloat() / layout.totalScrollRange * 2f
117+
// 降低滚动更新UI的频率
118+
val scrollLimit = (percent * 100).toInt()
119+
if (scrollLimit != lastScrollLimit) {
120+
lastScrollLimit = scrollLimit
121+
// 前半段滚动,更新颜色
122+
updateUIByScroll(percent.clamp(0f, 1f))
123+
// 后半段滚动,更新圆角
124+
val roundedPercent = (scrollLimit - 100) / 100f
125+
updateTabLayoutRound(roundedPercent.clamp(0f, 1f))
126+
}
127+
}
128128

129129
viewModel.followLiveData.observe(this) { followed ->
130130
if (followed) {
@@ -138,58 +138,85 @@ class AnimeDetailActivity : BaseActivity<AnimeDetailViewModel, ActivityAnimeDeta
138138
}
139139
}
140140

141-
viewModel.animeDetailLiveData.observe(this) { bangumiData ->
142-
bangumiData.apply {
143-
dataBinding.collapsingToolbarLayout.title = animeTitle
144-
145-
dataBinding.backgroundCoverIv.loadImage(imageUrl)
146-
147-
dataBinding.coverIv.loadImageWithCallback(
148-
source = imageUrl,
149-
dpRadius = 3f,
150-
errorRes = R.drawable.ic_load_image_failed
151-
) { supportStartPostponedEnterTransition() }
152-
153-
dataBinding.viewpager.apply {
154-
adapter = AnimeDetailAdapter(supportFragmentManager, bangumiData)
155-
offscreenPageLimit = 2
156-
currentItem = 0
157-
}
141+
viewModel.animeDetailLiveData.observe(this) {
142+
if (it.animeTitle != animeArgument.title || it.imageUrl != animeArgument.imageUrl) {
143+
updateUIbyAnime(it.animeTitle.orEmpty(), it.imageUrl.orEmpty())
158144
}
159145

146+
// 如果没有相关推荐和相似番剧,移除推荐tab
147+
val dynamicTabs = defaultTabs.toMutableList()
148+
if (it.relateds.isEmpty() && it.similars.isEmpty()) {
149+
dynamicTabs.remove(AnimeDetailTab.RECOMMEND)
150+
initViewPager(dynamicTabs.toTypedArray())
151+
}
160152
}
153+
}
161154

162-
viewModel.transitionFailedLiveData.observe(this) {
163-
finishAfterTransition()
164-
}
155+
/**
156+
* 根据滚动百分比更新UI
157+
*/
158+
private fun updateUIByScroll(percent: Float) {
159+
// 标题栏颜色
160+
dataBinding.toolbar.navigationIcon?.setTint(textColorRange.take(percent))
161+
dataBinding.toolbar.setTitleTextColor(pageColorRange.end.opacity(percent))
162+
dataBinding.toolbar.setBackgroundColor(pageColorRange.start.opacity(percent))
163+
// 追番按钮背景
164+
dataBinding.followTv.background?.alpha = 255 - (255 * percent).toInt()
165+
166+
//状态栏文字颜色
167+
//tips: MIUI深色模式下状态栏字体颜色不 受此控制
168+
val isDarkFont = percent > 0.5f && !isNightMode()
169+
ImmersionBar.with(this)
170+
.statusBarColorInt(pageColorRange.start.opacity(percent))
171+
.statusBarDarkFont(isDarkFont)
172+
.init()
165173
}
166174

167-
inner class AnimeDetailAdapter(
168-
fragmentManager: FragmentManager,
169-
private val bangumiData: BangumiData
170-
) : FragmentPagerAdapter(
171-
fragmentManager,
172-
BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
173-
) {
174-
private var titles = if (bangumiData.similars.size == 0 && bangumiData.relateds.size == 0) {
175-
arrayOf("信息", "剧集")
176-
} else {
177-
arrayOf("信息", "剧集", "推荐")
175+
/**
176+
* 根据滚动百分比更新TabLayout圆角
177+
*/
178+
private fun updateTabLayoutRound(percent: Float) {
179+
val topRadius = 12.dp() * (1 - percent)
180+
val bottomRadius = 0f
181+
val roundCorners = floatArrayOf(
182+
topRadius, topRadius,
183+
topRadius, topRadius,
184+
bottomRadius, bottomRadius,
185+
bottomRadius, bottomRadius
186+
)
187+
dataBinding.tabLayout.background = ShapeDrawable().apply {
188+
shape = RoundRectShape(roundCorners, null, null)
189+
paint.color = pageColorRange.start
178190
}
191+
}
179192

180-
override fun getItem(position: Int): Fragment {
181-
return when (position) {
182-
0 -> AnimeIntroFragment.newInstance(bangumiData)
183-
1 -> AnimeEpisodeFragment.newInstance(bangumiData)
184-
2 -> AnimeRecommendFragment.newInstance(bangumiData)
185-
else -> AnimeIntroFragment.newInstance(bangumiData)
186-
}
187-
}
193+
/**
194+
* 根据番剧信息更新UI
195+
*/
196+
private fun updateUIbyAnime(title: String, image: String) {
197+
dataBinding.collapsingToolbarLayout.title = title
198+
dataBinding.backgroundCoverIv.loadImage(image)
199+
dataBinding.coverIv.loadAnimeCover(image)
200+
}
188201

189-
override fun getCount() = titles.size
202+
/**
203+
* 刷新番剧详情
204+
*/
205+
fun refreshAnimeDetail() {
206+
viewModel.getAnimeDetail(animeArgument.id.toString())
207+
}
190208

191-
override fun getPageTitle(position: Int): CharSequence {
192-
return titles[position]
193-
}
209+
/**
210+
* 注册返回事件拦截器
211+
*/
212+
fun registerBackPressInterceptor(interceptor: () -> Boolean) {
213+
backPressInterceptor = interceptor
214+
}
215+
216+
/**
217+
* 取消返回事件拦截器
218+
*/
219+
fun unregisterBackPressInterceptor() {
220+
backPressInterceptor = null
194221
}
195222
}

0 commit comments

Comments
 (0)