feat: 新游开测相关功能优化(第五期)—客户端 https://jira.shanqu.cc/browse/GHZSCY-8124

This commit is contained in:
叶子维
2025-06-23 10:37:28 +08:00
parent 09a823d4da
commit dbd6e7bb43
10 changed files with 329 additions and 243 deletions

View File

@ -0,0 +1,26 @@
package com.gh.gamecenter.entity
import com.gh.gamecenter.servers.gametest2.GameServerTestV2ViewModel
import com.google.gson.annotations.SerializedName
data class GameServerTestDisplaySetting(
@SerializedName("time_text_past")
val timeTextPast: String = RECENT,
@SerializedName("time_text_present")
val timeTextPresent: String = TODAY,
@SerializedName("time_text_future")
val timeTextFuture: String = FUTURE,
@SerializedName("game_category")
val gameCategory: List<String> = listOf(
GameServerTestV2ViewModel.GameCategory.Local.value,
GameServerTestV2ViewModel.GameCategory.Online.value,
GameServerTestV2ViewModel.GameCategory.Welfare.value,
GameServerTestV2ViewModel.GameCategory.Gjonline.value
),
) {
companion object {
const val RECENT = "近期"
const val TODAY = "今天"
const val FUTURE = "预约"
}
}

View File

@ -50,6 +50,7 @@ import com.gh.gamecenter.entity.GameColumnCollection;
import com.gh.gamecenter.entity.GameData;
import com.gh.gamecenter.entity.GameDigestEntity;
import com.gh.gamecenter.entity.GameGuidePopupEntity;
import com.gh.gamecenter.entity.GameServerTestDisplaySetting;
import com.gh.gamecenter.entity.GameServerTestTopGame;
import com.gh.gamecenter.entity.GameServerTestV2Entity;
import com.gh.gamecenter.entity.GameVideoInfo;
@ -3105,6 +3106,12 @@ public interface ApiService {
@GET("columns/tests/v2")
Observable<GameServerTestV2Entity> getServerTestV2(@Query("filter") String filter);
/**
* 新游开测-显示配置
*/
@GET("app/column_test_v2/{link_id}/display_setting")
Single<GameServerTestDisplaySetting> getGameServerTestDisplaySetting(@Path("link_id") String linkId);
/**
* 新游开测-详情列表
*/

View File

@ -32,6 +32,7 @@ class GameServerTestV2Fragment : LazyFragment() {
private var mSettingBinding: LayoutGameServerTestV2SettingBinding? = null
private var mCurrentFragment: GameServerTestV2ListFragment? = null
private var mLinkText = ""
private var mLinkId = ""
private var mPageLocation: PageLocation? = null
private var mIsSettingAnimating = false
@ -39,6 +40,7 @@ class GameServerTestV2Fragment : LazyFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mLinkId = arguments?.getString(EntranceConsts.KEY_ID, "none") ?: "none"
mLinkText = requireArguments().getString(EntranceConsts.KEY_NAME, "")
val topTabIndex = requireArguments().getInt(EntranceConsts.KEY_TAB_INDEX, -1)
val bottomTabIndex = requireArguments().getInt(EntranceConsts.KEY_BOTTOM_TAB_INDEX, -1)
@ -64,22 +66,40 @@ class GameServerTestV2Fragment : LazyFragment() {
}
override fun onFragmentFirstVisible() {
mViewModel = viewModelProviderFromParent()
mViewModel = viewModelProviderFromParent(GameServerTestV2ViewModel.Factory(mLinkId))
super.onFragmentFirstVisible()
mViewModel?.run {
mBinding?.filterView?.setItemList(timeFilterList, DEFAULT_TIME_FILTER_INDEX)
selectedTimeFilterLiveData.observe(this@GameServerTestV2Fragment) {
displaySettingLiveData.observe(viewLifecycleOwner) {
mBinding?.filterView?.setItemList(timeFilterList, DEFAULT_TIME_FILTER_INDEX)
mViewModel?.selectedTimeFilterLiveData?.value = mViewModel?.timeFilterList?.getOrNull(DEFAULT_TIME_FILTER_INDEX) ?: ""
mBinding?.filterView?.isVisible = true
mBinding?.optionIv?.setOnClickListener {
showSettingView()
SensorsBridge.trackColumnTestClick(
location = "详情页",
recommendType = mLinkText,
text = "设置",
bottomTab = mPageLocation?.bottomTab,
multiTabName = mPageLocation?.severalTabPageName,
multiTabId = mPageLocation?.severalTabPageId,
position = mPageLocation?.tabPosition,
tabContent = mPageLocation?.tabContent
)
}
mCurrentFragment?.loadData()
if (SPUtils.getBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, true)) {
mBaseHandler.post {
showGuide()
}
SPUtils.setBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, false)
}
}
selectedTimeFilterLiveData.observe(viewLifecycleOwner) {
mBinding?.filterView?.setCurrentItem(timeFilterList.indexOf(it))
}
}
changeFragment()
if (SPUtils.getBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, true)) {
mBaseHandler.post {
showGuide()
}
SPUtils.setBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, false)
}
}
override fun initRealView() {
@ -89,7 +109,7 @@ class GameServerTestV2Fragment : LazyFragment() {
filterContainer.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_background.toColor(requireContext()))
filterView.onCheckedAction = { index ->
mViewModel?.let {
it.setSelectedTimeFilter(it.timeFilterList[index])
it.setSelectedTimeFilter(it.timeFilterList.getOrNull(index))
mCurrentFragment?.changeTimeFilter()
SensorsBridge.trackColumnTestClick(
location = "详情页",
@ -100,7 +120,7 @@ class GameServerTestV2Fragment : LazyFragment() {
multiTabId = mPageLocation?.severalTabPageId,
position = mPageLocation?.tabPosition,
tabContent = mPageLocation?.tabContent,
buttonType = it.timeFilterList[index]
buttonType = it.timeFilterList.getOrNull(index) ?: ""
)
}
}
@ -124,19 +144,6 @@ class GameServerTestV2Fragment : LazyFragment() {
)
}
}
optionIv.setOnClickListener {
showSettingView()
SensorsBridge.trackColumnTestClick(
location = "详情页",
recommendType = mLinkText,
text = "设置",
bottomTab = mPageLocation?.bottomTab,
multiTabName = mPageLocation?.severalTabPageName,
multiTabId = mPageLocation?.severalTabPageId,
position = mPageLocation?.tabPosition,
tabContent = mPageLocation?.tabContent
)
}
}
}
@ -190,7 +197,7 @@ class GameServerTestV2Fragment : LazyFragment() {
val isCategoryFilterChanged = selectedCategoryFilterSet != tempCategoryFilterSet
selectedCategoryFilterSet = HashSet(tempCategoryFilterSet)
SPUtils.setStringSet(Constants.SP_GAME_SERVER_TEST_V2_CATEGORY_SET, selectedCategoryFilterSet)
setSelectedTimeFilter(GameServerTestV2ViewModel.TODAY_GAME)
setSelectedTimeFilter(timeFilterList.getOrNull(1))
if (isCategoryFilterChanged) {
mCurrentFragment?.changeCategoryFilter()
}
@ -212,8 +219,8 @@ class GameServerTestV2Fragment : LazyFragment() {
}
val itemList = arrayListOf<TextView>()
val allItem = getItemTextView("全部")
allItem.tag = "全部"
val allItem = getItemTextView(GameServerTestV2ViewModel.GameCategory.All.displayName)
allItem.tag = GameServerTestV2ViewModel.GameCategory.All.displayName
binding.flexbox.addView(allItem)
toggleHighlightedTextView(allItem, mViewModel?.tempCategoryFilterSet.isNullOrEmpty())
allItem.setOnClickListener {
@ -338,7 +345,6 @@ class GameServerTestV2Fragment : LazyFragment() {
}
private fun changeFragment() {
mViewModel?.selectedTimeFilterLiveData?.value = mViewModel?.timeFilterList?.get(DEFAULT_TIME_FILTER_INDEX) ?: ""
mCurrentFragment =
childFragmentManager.findFragmentByTag(GameServerTestV2ViewModel::class.java.name) as? GameServerTestV2ListFragment
?: GameServerTestV2ListFragment()

View File

@ -18,6 +18,7 @@ import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.baselist.LazyListFragment
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.exposure.ExposureSource
@ -76,6 +77,12 @@ class GameServerTestV2ListFragment :
)
}
override fun isAutomaticLoad(): Boolean = false
fun loadData() {
mListViewModel.load(LoadType.NORMAL)
}
override fun onCreate(savedInstanceState: Bundle?) {
mGameServerTestV2ViewModel = viewModelProviderFromParent()
super.onCreate(savedInstanceState)
@ -356,7 +363,7 @@ class GameServerTestV2ListFragment :
override fun onChanged(ts: MutableList<GameServerTestV2ListViewModel.ItemData>?) {
super.onChanged(ts)
if (!ts.isNullOrEmpty() && mListViewModel.isLoadFirstPage) {
scrollToTime(GameServerTestV2ViewModel.TimeFilter.TODAY.value)
scrollToTime(GameServerTestV2ViewModel.TimeFilter.Today.value)
mListRv?.postDelayed({
scroll()
mScrollCalculatorHelper.onScrollStateChanged(RecyclerView.SCROLL_STATE_IDLE)

View File

@ -55,14 +55,9 @@ class GameServerTestV2ListViewModel(
mListLiveData.value = null
mLoadStatusLiveData.postValue(LoadStatus.INIT_LOADING)
var categoryFilter = "" //如果选择全部类型,则传空值
mGameServerTestV2ViewModel?.selectedCategoryFilterSet?.forEachIndexed { index, category ->
if (index == 0) {
categoryFilter = mGameServerTestV2ViewModel.getCategoryFilterValue(category).value
} else {
categoryFilter += "-${mGameServerTestV2ViewModel.getCategoryFilterValue(category).value}"
}
}
val categoryFilter = mGameServerTestV2ViewModel?.selectedCategoryFilterSet?.joinToString("-") {
GameServerTestV2ViewModel.GameCategory.fromDisplayName(it).value
} ?: "" //如果选择全部类型,则传空值
Observable.zip(provideDetailObservable(categoryFilter), provideTopGamesObservable()) { t1, t2 ->
mTopGameList = ArrayList(t2).apply {
@ -151,11 +146,11 @@ class GameServerTestV2ListViewModel(
// 置顶游戏固定显示在今天上方,如果今天没有数据则展示在未来上方;如果今天和未来都没有数据,则显示在近期上方
val topGamesReadableDaysOffset = if (mTodayCount != 0) {
GameServerTestV2ViewModel.TimeFilter.TODAY.value
GameServerTestV2ViewModel.TimeFilter.Today.value
} else if (mFutureCount != 0) {
GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value
GameServerTestV2ViewModel.TimeFilter.Future.value
} else {
GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value
GameServerTestV2ViewModel.TimeFilter.Recent.value
}
var isTopGamesAdded = false
@ -179,11 +174,7 @@ class GameServerTestV2ListViewModel(
}
}
val readableDaysOffset = when (slice.timeType) {
"recent" -> GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value
"future" -> GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value
else -> GameServerTestV2ViewModel.TimeFilter.TODAY.value
}
val readableDaysOffset = slice.timeType
if (!mTopGameList.isNullOrEmpty() && readableDaysOffset == topGamesReadableDaysOffset && !isTopGamesAdded) {
subList.add(
@ -335,31 +326,31 @@ class GameServerTestV2ListViewModel(
if (position >= 0) return position
val recentCount =
itemDataList.count { it.readableDaysOffset == GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value }
itemDataList.count { it.readableDaysOffset == GameServerTestV2ViewModel.TimeFilter.Recent.value }
val todayCount =
itemDataList.count { it.readableDaysOffset == GameServerTestV2ViewModel.TimeFilter.TODAY.value }
itemDataList.count { it.readableDaysOffset == GameServerTestV2ViewModel.TimeFilter.Today.value }
when (timeFilter) {
GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value -> {
position = mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.TODAY.value, -1)
GameServerTestV2ViewModel.TimeFilter.Recent.value -> {
position = mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.Today.value, -1)
if (position >= 0) return position
position =
mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value, -1)
mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.Future.value, -1)
if (position >= 0) return position
}
GameServerTestV2ViewModel.TimeFilter.TODAY.value -> {
GameServerTestV2ViewModel.TimeFilter.Today.value -> {
position =
mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value, -1)
mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.Future.value, -1)
if (position >= 0) return position
position =
mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value, -1)
mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.Recent.value, -1)
if (position >= 0) return recentCount - 1
}
GameServerTestV2ViewModel.TimeFilter.UPCOMING_DAY.value -> {
position = mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.TODAY.value, -1)
GameServerTestV2ViewModel.TimeFilter.Future.value -> {
position = mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.Today.value, -1)
if (position >= 0) return recentCount + todayCount - 1
position =
mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.PAST_DAY.value, -1)
mTimePositionArrayMap.getOrDefault(GameServerTestV2ViewModel.TimeFilter.Recent.value, -1)
if (position >= 0) recentCount - 1
}
}

View File

@ -1,5 +1,6 @@
package com.gh.gamecenter.servers.gametest2
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
@ -7,89 +8,129 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.common.constant.Config
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.GameServerTestDisplaySetting
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.servers.gametest2.GameServerTestV2ViewModel.GameCategory.*
import com.halo.assistant.HaloApp
class GameServerTestV2ViewModel(application: Application) : AndroidViewModel(application) {
class GameServerTestV2ViewModel(application: Application, private val linkId: String = "none") : AndroidViewModel(application) {
val timeFilterList = arrayListOf(RECENT_GAME, TODAY_GAME, FUTURE_GAME)
val categoryFilterList = arrayListOf("单机游戏", "网络游戏", "福利游戏", "国际服游戏")
val timeFilterList = arrayListOf(
GameServerTestDisplaySetting.RECENT,
GameServerTestDisplaySetting.TODAY,
GameServerTestDisplaySetting.FUTURE
)
val categoryFilterList = arrayListOf(
Local.displayName,
Online.displayName,
Welfare.displayName,
Gjonline.displayName
)
var selectedCategoryFilterSet = SPUtils.getStringSet(Constants.SP_GAME_SERVER_TEST_V2_CATEGORY_SET).toHashSet()
var tempCategoryFilterSet = SPUtils.getStringSet(Constants.SP_GAME_SERVER_TEST_V2_CATEGORY_SET).toHashSet()
var selectedTimeFilterLiveData = MutableLiveData(timeFilterList[GameServerTestV2Fragment.DEFAULT_TIME_FILTER_INDEX])
var selectedTimeFilterLiveData = MutableLiveData<String?>()
var bigImageMode = SPUtils.getBoolean(
Constants.SP_GAME_SERVER_TEST_V2_BIG_IMAGE_MODE,
Config.getSettings() == null || Config.getSettings()?.columnTestV2Setting?.defaultView == "on"
)
fun getCategoryFilterString(): String {
var selectedCategoryFilter = "全部类型"
selectedCategoryFilterSet.forEachIndexed { index, category ->
if (index == 0) {
selectedCategoryFilter = category
} else {
selectedCategoryFilter += "+$category"
}
}
return selectedCategoryFilter
val displaySettingLiveData = MutableLiveData<GameServerTestDisplaySetting>()
init {
loadDisplaySetting()
}
fun getCategoryFilterValue(category: String): GameCategory {
return when (category) {
"单机游戏" -> GameCategory.LOCAL
"网络游戏" -> GameCategory.ONLINE
"福利游戏" -> GameCategory.WELFARE
"国际服游戏" -> GameCategory.GJONLINE
else -> GameCategory.ALL
}
@SuppressLint("CheckResult")
fun loadDisplaySetting() {
RetrofitManager.getInstance().newApi.getGameServerTestDisplaySetting(linkId)
.compose(singleToMain())
.subscribe({ setting ->
timeFilterList.clear()
timeFilterList.addAll(listOf(setting.timeTextPast, setting.timeTextPresent, setting.timeTextFuture))
TimeFilter.Recent.displayName = setting.timeTextPast
TimeFilter.Today.displayName = setting.timeTextPresent
TimeFilter.Future.displayName = setting.timeTextFuture
categoryFilterList.clear()
categoryFilterList.addAll(setting.gameCategory.map { GameCategory.fromValue(it).displayName })
selectedCategoryFilterSet.removeAll { !categoryFilterList.contains(it) }
tempCategoryFilterSet.removeAll { !categoryFilterList.contains(it) }
displaySettingLiveData.postValue(setting)
}, {
it.printStackTrace()
displaySettingLiveData.postValue(GameServerTestDisplaySetting())
})
}
fun getSelectedTimeFilterString(dayFilter: String): String {
return when (dayFilter) {
TimeFilter.PAST_DAY.value -> RECENT_GAME
TimeFilter.TODAY.value -> TODAY_GAME
TimeFilter.UPCOMING_DAY.value -> FUTURE_GAME
else -> TODAY_GAME
}
}
fun getCategoryFilterString() = selectedCategoryFilterSet.joinToString("+").ifEmpty { "全部类型" }
fun getCurrentSelectedTimeFilter(): String {
return when (selectedTimeFilterLiveData.value) {
RECENT_GAME -> TimeFilter.PAST_DAY.value
TODAY_GAME -> TimeFilter.TODAY.value
FUTURE_GAME -> TimeFilter.UPCOMING_DAY.value
else -> TimeFilter.TODAY.value
}
}
fun getSelectedTimeFilterString(dayFilter: String) = TimeFilter.fromValue(dayFilter).displayName
fun getCurrentSelectedTimeFilter() = TimeFilter.fromDisplayName(selectedTimeFilterLiveData.value ?: "").value
fun setSelectedTimeFilter(filter: String?) {
if (filter.isNullOrEmpty()) return
selectedTimeFilterLiveData.value = filter
}
enum class TimeFilter(val value: String) {
PAST_DAY("recent"),
TODAY("today"),
UPCOMING_DAY("future")
sealed class TimeFilter(val value: String, var displayName: String) {
data object Recent : TimeFilter("recent", GameServerTestDisplaySetting.RECENT)
data object Today : TimeFilter("today", GameServerTestDisplaySetting.TODAY)
data object Future : TimeFilter("future", GameServerTestDisplaySetting.FUTURE)
companion object {
fun fromDisplayName(displayName: String): TimeFilter {
return when (displayName) {
Recent.displayName -> Recent
Future.displayName -> Future
else -> Today
}
}
fun fromValue(value: String): TimeFilter {
return when (value) {
Recent.value -> Recent
Future.value -> Future
else -> Today
}
}
}
}
enum class GameCategory(val value: String) {
ALL("all"),
LOCAL("local"),
ONLINE("online"),
WELFARE("welfare"),
GJONLINE("gjonline"),
sealed class GameCategory(val value: String, val displayName: String) {
data object All : GameCategory("all", "全部")
data object Local : GameCategory("local", "单机游戏")
data object Online : GameCategory("online", "网络游戏")
data object Welfare : GameCategory("welfare", "福利游戏")
data object Gjonline : GameCategory("gjonline", "国际服游戏")
companion object {
fun fromDisplayName(displayName: String): GameCategory {
return when (displayName) {
Local.displayName -> Local
Online.displayName -> Online
Welfare.displayName -> Welfare
Gjonline.displayName -> Gjonline
else -> All
}
}
fun fromValue(value: String): GameCategory {
return when (value) {
Local.value -> Local
Online.value -> Online
Welfare.value -> Welfare
Gjonline.value -> Gjonline
else -> All
}
}
}
}
companion object {
const val RECENT_GAME = "近期"
const val TODAY_GAME = "今天"
const val FUTURE_GAME = "预约"
}
class Factory(private val mApplication: Application) :
class Factory(private val linkId: String = "none") :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return GameServerTestV2ViewModel(mApplication) as T
return GameServerTestV2ViewModel(HaloApp.getInstance(), linkId) as T
}
}
}

View File

@ -56,9 +56,10 @@
<com.gh.gamecenter.common.view.SegmentedIconFilterView
android:id="@+id/filterView"
android:layout_width="130dp"
android:layout_width="wrap_content"
android:layout_height="28dp"
android:layout_marginStart="16dp"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View File

@ -5,10 +5,12 @@ import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.core.view.setPadding
import com.gh.gamecenter.common.R
import com.gh.gamecenter.common.databinding.ViewSegmentedIconFilterBinding
import com.gh.gamecenter.common.utils.DarkModeUtils
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toDrawable
class SegmentedIconFilterView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null): FrameLayout(context, attrs) {
private var textSize = 12F
@ -23,15 +25,12 @@ class SegmentedIconFilterView @JvmOverloads constructor(context: Context, attrs:
var onCheckedAction: ((Int) -> Unit)? = null
init {
val view = LayoutInflater.from(context).inflate(R.layout.view_segmented_icon_filter, this, true)
val view = LayoutInflater.from(context).inflate(R.layout.view_segmented_icon_filter, this, false)
binding = ViewSegmentedIconFilterBinding.bind(view)
addView(view)
initView(attrs)
}
fun setContainerBackground(drawable: Drawable?) {
binding.motionLayout.background = drawable
}
private fun initView(attrs: AttributeSet?) {
if (attrs != null) {
val ta = context.obtainStyledAttributes(attrs, R.styleable.SegmentedFilterView)
@ -47,12 +46,8 @@ class SegmentedIconFilterView @JvmOverloads constructor(context: Context, attrs:
ta.recycle()
}
binding.motionLayout.run {
setPadding(containerPadding, containerPadding, containerPadding, containerPadding)
containerBackground?.let {
background = it
}
}
background = containerBackground ?: R.drawable.bg_shape_ui_container_2_radius_999.toDrawable(context)
setPadding(containerPadding)
indicatorBackground?.let {
binding.indicator.background = it
}
@ -129,6 +124,7 @@ class SegmentedIconFilterView @JvmOverloads constructor(context: Context, attrs:
}
fun updateView(isDarkModeOn: Boolean) {
background = containerBackground ?: R.drawable.bg_shape_ui_container_2_radius_999.toDrawable(context)
binding.motionLayout.run {
when (currentIndex) {
0 -> jumpToState(if (isDarkModeOn) R.id.filterDark1 else R.id.filter1)

View File

@ -1,66 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.motion.widget.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/motionLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
android:layout_height="24dp"
app:layoutDescription="@xml/view_segmented_icon_filter_scene">
<androidx.constraintlayout.motion.widget.MotionLayout
android:id="@+id/motionLayout"
android:layout_width="130dp"
android:layout_height="28dp"
android:background="@drawable/bg_shape_ui_container_2_radius_999"
android:padding="2dp"
app:layoutDescription="@xml/view_segmented_icon_filter_scene">
<View
android:id="@+id/indicator"
android:layout_width="0dp"
android:layout_height="0dp"
android:background="@drawable/background_shape_white_radius_999" />
<View
android:id="@+id/indicator"
android:layout_width="50dp"
android:layout_height="0dp"
android:background="@drawable/background_shape_white_radius_999" />
<ImageView
android:id="@+id/icon1Iv"
android:layout_width="12dp"
android:layout_height="12dp"
app:srcCompat="@drawable/ic_segmented_icon_filter_dot" />
<ImageView
android:id="@+id/icon1Iv"
android:layout_width="12dp"
android:layout_height="12dp"
app:srcCompat="@drawable/ic_segmented_icon_filter_dot" />
<TextView
android:id="@+id/filter1Tv"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:gravity="center"
android:includeFontPadding="false"
android:text="近期"
android:textSize="@dimen/secondary_size" />
<TextView
android:id="@+id/filter1Tv"
android:layout_width="wrap_content"
android:layout_height="28dp"
android:gravity="center"
android:includeFontPadding="false"
android:text="近期"
android:textSize="@dimen/secondary_size" />
<ImageView
android:id="@+id/icon2Iv"
android:layout_width="12dp"
android:layout_height="12dp"
app:srcCompat="@drawable/ic_segmented_icon_filter_dot" />
<ImageView
android:id="@+id/icon2Iv"
android:layout_width="12dp"
android:layout_height="12dp"
app:srcCompat="@drawable/ic_segmented_icon_filter_dot" />
<TextView
android:id="@+id/filter2Tv"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:gravity="center"
android:includeFontPadding="false"
android:text="今天"
android:textSize="@dimen/secondary_size" />
<TextView
android:id="@+id/filter2Tv"
android:layout_width="wrap_content"
android:layout_height="28dp"
android:gravity="center"
android:includeFontPadding="false"
android:text="今天"
android:textSize="@dimen/secondary_size" />
<ImageView
android:id="@+id/icon3Iv"
android:layout_width="12dp"
android:layout_height="12dp"
app:srcCompat="@drawable/ic_segmented_icon_filter_dot" />
<ImageView
android:id="@+id/icon3Iv"
android:layout_width="12dp"
android:layout_height="12dp"
app:srcCompat="@drawable/ic_segmented_icon_filter_dot" />
<TextView
android:id="@+id/filter3Tv"
android:layout_width="wrap_content"
android:layout_height="28dp"
android:gravity="center"
android:includeFontPadding="false"
android:text="未来"
android:textSize="@dimen/secondary_size" />
</androidx.constraintlayout.motion.widget.MotionLayout>
</FrameLayout>
<TextView
android:id="@+id/filter3Tv"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:gravity="center"
android:includeFontPadding="false"
android:text="未来"
android:textSize="@dimen/secondary_size" />
</androidx.constraintlayout.motion.widget.MotionLayout>

View File

@ -31,9 +31,11 @@
<ConstraintSet android:id="@+id/filter1">
<Constraint
android:id="@+id/indicator"
android:layout_width="50dp"
android:layout_height="24dp"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toStartOf="@+id/filter2Tv"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
@ -64,18 +66,18 @@
android:id="@+id/icon2Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/indicator"
motion:layout_constraintStart_toStartOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter2Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="14dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/indicator"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="textColor"
@ -86,18 +88,19 @@
android:id="@+id/icon3Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
motion:layout_constraintStart_toStartOf="@+id/filter3Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter3Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="-2dp"
android:layout_marginStart="14dp"
android:layout_marginEnd="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
@ -109,9 +112,12 @@
<ConstraintSet android:id="@+id/filter2">
<Constraint
android:id="@+id/indicator"
android:layout_width="50dp"
android:layout_height="24dp"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toStartOf="@+id/filter3Tv"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent" />
@ -119,17 +125,16 @@
android:id="@+id/icon1Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintStart_toStartOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter1Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="-2dp"
android:layout_marginStart="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
@ -142,7 +147,7 @@
android:id="@+id/icon2Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:layout_marginStart="14dp"
android:alpha="1"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
@ -173,11 +178,13 @@
<Constraint
android:id="@+id/filter3Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="-2dp"
android:layout_marginStart="14dp"
android:layout_marginEnd="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="textColor"
@ -188,27 +195,28 @@
<ConstraintSet android:id="@+id/filter3">
<Constraint
android:id="@+id/indicator"
android:layout_width="50dp"
android:layout_height="24dp"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/icon1Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintStart_toStartOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter1Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="-2dp"
android:layout_marginStart="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
@ -221,17 +229,16 @@
android:id="@+id/icon2Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
motion:layout_constraintStart_toStartOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter2Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="-2dp"
android:layout_marginStart="14dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent">
@ -244,7 +251,7 @@
android:id="@+id/icon3Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:layout_marginStart="14dp"
android:alpha="1"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
@ -255,7 +262,9 @@
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="2dp"
android:layout_marginEnd="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toEndOf="@+id/icon3Iv"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
@ -267,9 +276,11 @@
<ConstraintSet android:id="@+id/filterDark1">
<Constraint
android:id="@+id/indicator"
android:layout_width="50dp"
android:layout_height="24dp"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toStartOf="@+id/filter2Tv"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
@ -300,18 +311,18 @@
android:id="@+id/icon2Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/indicator"
motion:layout_constraintStart_toStartOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter2Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="14dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/indicator"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="textColor"
@ -322,18 +333,19 @@
android:id="@+id/icon3Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
motion:layout_constraintStart_toStartOf="@+id/filter3Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter3Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="-2dp"
android:layout_marginStart="14dp"
android:layout_marginEnd="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
@ -345,9 +357,12 @@
<ConstraintSet android:id="@+id/filterDark2">
<Constraint
android:id="@+id/indicator"
android:layout_width="50dp"
android:layout_height="24dp"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toStartOf="@+id/filter3Tv"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent" />
@ -355,17 +370,16 @@
android:id="@+id/icon1Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintStart_toStartOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter1Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="-2dp"
android:layout_marginStart="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
@ -378,7 +392,7 @@
android:id="@+id/icon2Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:layout_marginStart="14dp"
android:alpha="1"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
@ -401,7 +415,6 @@
android:id="@+id/icon3Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="@+id/filter3Tv"
@ -409,11 +422,13 @@
<Constraint
android:id="@+id/filter3Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginEnd="-2dp"
android:layout_marginStart="14dp"
android:layout_marginEnd="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="textColor"
@ -424,27 +439,28 @@
<ConstraintSet android:id="@+id/filterDark3">
<Constraint
android:id="@+id/indicator"
android:layout_width="50dp"
android:layout_height="24dp"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/icon1Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintStart_toStartOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter1Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="-2dp"
android:layout_marginStart="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
@ -457,17 +473,16 @@
android:id="@+id/icon2Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
motion:layout_constraintStart_toStartOf="@+id/filter2Tv"
motion:layout_constraintTop_toTopOf="parent" />
<Constraint
android:id="@+id/filter2Tv"
android:layout_width="40dp"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="-2dp"
android:layout_marginStart="14dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter1Tv"
motion:layout_constraintTop_toTopOf="parent">
@ -480,7 +495,7 @@
android:id="@+id/icon3Iv"
android:layout_width="12dp"
android:layout_height="12dp"
android:layout_marginStart="6dp"
android:layout_marginStart="14dp"
android:alpha="1"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toEndOf="@+id/filter2Tv"
@ -491,7 +506,9 @@
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_marginStart="2dp"
android:layout_marginEnd="6dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintStart_toEndOf="@+id/icon3Iv"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute