[WIP] UI refactor
This commit is contained in:
5 files changed
+158
-64
No files matched your search
@@ -1,33 +1,29 @@
|
||||
package com.github.kr328.clash
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.kr328.clash.adapter.AbstractProxyAdapter
|
||||
import com.github.kr328.clash.adapter.GridProxyAdapter
|
||||
import com.github.kr328.clash.adapter.ProxyChipAdapter
|
||||
import com.github.kr328.clash.remote.withClash
|
||||
import com.github.kr328.clash.utils.ProxySorter
|
||||
import com.github.kr328.clash.view.ProxiesTabMediator
|
||||
import kotlinx.android.synthetic.main.activity_proxies.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class ProxiesActivity : BaseActivity() {
|
||||
private val activity: ProxiesActivity
|
||||
get() = this
|
||||
private var maskedGroup: MutableSet<String> = mutableSetOf()
|
||||
private val updateChipChannel = Channel<Unit>(Channel.CONFLATED)
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_proxies)
|
||||
setSupportActionBar(toolbar)
|
||||
|
||||
val mediator = ProxiesTabMediator(this, mainList, chipList)
|
||||
|
||||
GridProxyAdapter(this).apply {
|
||||
mainList.layoutManager = layoutManager
|
||||
mainList.adapter = this
|
||||
@@ -39,56 +35,16 @@ class ProxiesActivity : BaseActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
mainList.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
updateChipChannel.offer(Unit)
|
||||
}
|
||||
})
|
||||
|
||||
chipList.adapter = ProxyChipAdapter(this) {
|
||||
launch {
|
||||
(mainList.adapter!! as AbstractProxyAdapter).getGroupPosition(it)?.also {
|
||||
val scroller = (object : LinearSmoothScroller(activity) {
|
||||
override fun getVerticalSnapPreference(): Int {
|
||||
return SNAP_TO_START
|
||||
}
|
||||
|
||||
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics?): Float {
|
||||
return 40f / displayMetrics!!.densityDpi
|
||||
}
|
||||
|
||||
init {
|
||||
targetPosition = it
|
||||
}
|
||||
})
|
||||
|
||||
mainList.layoutManager?.startSmoothScroll(scroller)
|
||||
}
|
||||
mediator.scrollTo(it)
|
||||
}
|
||||
}
|
||||
chipList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)
|
||||
chipList.itemAnimator?.changeDuration = 80
|
||||
chipList.itemAnimator?.changeDuration = 0
|
||||
|
||||
launch {
|
||||
var currentChecked = ""
|
||||
|
||||
while (isActive) {
|
||||
updateChipChannel.receive()
|
||||
|
||||
val currentGroup = (mainList.adapter!! as AbstractProxyAdapter).getCurrentGroup()
|
||||
|
||||
if (currentChecked == currentGroup)
|
||||
continue
|
||||
|
||||
currentChecked = currentGroup
|
||||
|
||||
(chipList.adapter!! as ProxyChipAdapter).apply {
|
||||
selected = currentChecked
|
||||
|
||||
(chipList.layoutManager!! as LinearLayoutManager)
|
||||
.scrollToPositionWithOffset(chips.indexOf(currentChecked), 0)
|
||||
}
|
||||
}
|
||||
mediator.exec()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +58,7 @@ class ProxiesActivity : BaseActivity() {
|
||||
finish()
|
||||
}
|
||||
|
||||
private fun refreshList(scrollTo: String? = null) {
|
||||
private fun refreshList() {
|
||||
launch {
|
||||
val proxies = withClash {
|
||||
queryAllProxyGroups()
|
||||
@@ -110,17 +66,20 @@ class ProxiesActivity : BaseActivity() {
|
||||
|
||||
val sorter = ProxySorter(ProxySorter.Order.DEFAULT, ProxySorter.Order.DEFAULT)
|
||||
|
||||
val filtered = withContext(Dispatchers.Default) {
|
||||
val sorted = withContext(Dispatchers.Default) {
|
||||
sorter.sort(proxies.toList())
|
||||
}
|
||||
|
||||
(mainList.adapter!! as AbstractProxyAdapter).apply {
|
||||
root = filtered.filter { !maskedGroup.contains(it.name) }
|
||||
root = sorted
|
||||
|
||||
applyChange()
|
||||
}
|
||||
|
||||
(chipList.adapter!! as ProxyChipAdapter).chips = filtered.map { it.name }
|
||||
(chipList.adapter!! as ProxyChipAdapter).apply {
|
||||
chips = sorted.map { it.name }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.github.kr328.clash.adapter
|
||||
|
||||
import android.animation.ValueAnimator
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.util.TypedValue
|
||||
@@ -44,6 +45,51 @@ class ProxyChipAdapter(
|
||||
class Holder(root: View) : RecyclerView.ViewHolder(root) {
|
||||
val card: MaterialCardView = root.findViewById(R.id.root)
|
||||
val title: TextView = root.findViewById(android.R.id.title)
|
||||
|
||||
private var cardAnimator: ValueAnimator? = null
|
||||
private var cardColor: Int = card.cardBackgroundColor.defaultColor
|
||||
private var titleAnimator: ValueAnimator? = null
|
||||
private var titleColor: Int = title.textColors.defaultColor
|
||||
|
||||
fun setCardColorAnimation(color: Int) {
|
||||
if (cardColor == color)
|
||||
return
|
||||
|
||||
cardAnimator?.cancel()
|
||||
|
||||
cardAnimator = ValueAnimator.ofArgb(cardColor, color).apply {
|
||||
addUpdateListener {
|
||||
val v = animatedValue as Int
|
||||
|
||||
card.setCardBackgroundColor(v)
|
||||
|
||||
cardColor = v
|
||||
}
|
||||
|
||||
duration = 200
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
fun setTitleColorAnimation(color: Int) {
|
||||
if (color == titleColor)
|
||||
return
|
||||
|
||||
titleAnimator?.cancel()
|
||||
|
||||
titleAnimator = ValueAnimator.ofArgb(titleColor, color).apply {
|
||||
addUpdateListener {
|
||||
val v = animatedValue as Int
|
||||
|
||||
title.setTextColor(v)
|
||||
|
||||
titleColor = v
|
||||
}
|
||||
|
||||
duration = 200
|
||||
start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
|
||||
@@ -65,11 +111,11 @@ class ProxyChipAdapter(
|
||||
}
|
||||
|
||||
if (selected == current) {
|
||||
holder.title.setTextColor(Color.WHITE)
|
||||
holder.card.setCardBackgroundColor(context.getColor(R.color.primaryCardColorStarted))
|
||||
holder.setTitleColorAnimation(Color.WHITE)
|
||||
holder.setCardColorAnimation(context.getColor(R.color.primaryCardColorStarted))
|
||||
} else {
|
||||
holder.title.setTextColor(colorOnSurface)
|
||||
holder.card.setCardBackgroundColor(context.getColor(R.color.chipBackgroundColor))
|
||||
holder.setTitleColorAnimation(colorOnSurface)
|
||||
holder.setCardColorAnimation(context.getColor(R.color.chipBackgroundColor))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.github.kr328.clash.view
|
||||
|
||||
import android.util.DisplayMetrics
|
||||
import androidx.recyclerview.widget.LinearSmoothScroller
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.kr328.clash.ProxiesActivity
|
||||
import com.github.kr328.clash.adapter.AbstractProxyAdapter
|
||||
import com.github.kr328.clash.adapter.ProxyChipAdapter
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
|
||||
class ProxiesTabMediator(
|
||||
private val context: ProxiesActivity,
|
||||
private val proxiesView: RecyclerView,
|
||||
private val chipView: RecyclerView
|
||||
) {
|
||||
private var preventChipScroll = false
|
||||
private val updateChipChannel = Channel<Unit>(Channel.CONFLATED)
|
||||
|
||||
init {
|
||||
proxiesView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
updateChipChannel.offer(Unit)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
suspend fun exec() {
|
||||
while (true) {
|
||||
var currentChecked = ""
|
||||
|
||||
while (true) {
|
||||
updateChipChannel.receive()
|
||||
|
||||
val currentGroup = (proxiesView.adapter!! as AbstractProxyAdapter).getCurrentGroup()
|
||||
|
||||
if (currentChecked == currentGroup)
|
||||
continue
|
||||
|
||||
currentChecked = currentGroup
|
||||
|
||||
(chipView.adapter!! as ProxyChipAdapter).apply {
|
||||
selected = currentChecked
|
||||
|
||||
if (!preventChipScroll)
|
||||
chipView.smoothScrollToPosition(chips.indexOf(currentChecked))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun scrollTo(group: String) {
|
||||
(proxiesView.adapter!! as AbstractProxyAdapter).apply {
|
||||
val index = getGroupPosition(group) ?: return
|
||||
|
||||
scrollTo(index)
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollTo(position: Int) {
|
||||
val scroller = (object : LinearSmoothScroller(context) {
|
||||
override fun getVerticalSnapPreference(): Int {
|
||||
return SNAP_TO_START
|
||||
}
|
||||
|
||||
override fun calculateSpeedPerPixel(displayMetrics: DisplayMetrics?): Float {
|
||||
return 35f / displayMetrics!!.densityDpi
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
|
||||
preventChipScroll = false
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
preventChipScroll = true
|
||||
}
|
||||
|
||||
init {
|
||||
targetPosition = position
|
||||
}
|
||||
})
|
||||
|
||||
proxiesView.layoutManager!!.startSmoothScroll(scroller)
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,8 @@
|
||||
android:layout_marginTop="10dp"
|
||||
app:cardCornerRadius="12dp"
|
||||
android:focusable="true"
|
||||
android:clickable="true">
|
||||
android:clickable="true"
|
||||
app:cardBackgroundColor="@color/chipBackgroundColor">
|
||||
|
||||
<TextView
|
||||
android:id="@android:id/title"
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="200dp"
|
||||
android:height="200dp"
|
||||
android:viewportWidth="349"
|
||||
android:viewportHeight="336">
|
||||
android:width="402.47244dp"
|
||||
android:height="381.04724dp"
|
||||
android:viewportWidth="402.47244"
|
||||
android:viewportHeight="381.04724">
|
||||
<path
|
||||
android:fillColor="#FF000000"
|
||||
android:pathData="M68.857,304.788l4,-41l12,-97l13,-94l12,-61l2,-7c2,-5 4,-6 9,-3l10,9l26,41c3,4 6,5 11,4c28,-6 57,-5 85,1c5,1 7,-1 9,-5a1453,1453 0,0 1,30 -49c4,-4 7,-4 9,2l8,36l28,177c5,34 8,67 11,100c2,12 1,13 -11,14c-54,5 -108,5 -162,4c-30,0 -59,-2 -88,-3c-6,0 -12,-3 -18,-4l-17,-1c-18,-2 -34,-9 -44,-26c-17,-28 -6,-65 29,-75c4,-1 8,0 12,1c3,2 3,5 0,7l-6,5c-4,3 -9,6 -12,10a31,31 0,0 0,7 49c10,5 20,6 31,6zM149.857,116.788c-8,0 -15,7 -15,15s6,15 15,15c8,0 15,-6 15,-15c0,-8 -7,-15 -15,-15zM272.857,146.788c8,0 15,-6 15,-15c0,-8 -6,-15 -15,-15a15,15 0,1 0,0 30zM189.857,166.788c4,7 11,7 20,1c7,6 16,6 18,-1c-8,3 -14,2 -19,-7c-4,9 -10,10 -19,7z"/>
|
||||
android:pathData="M95.758,327.156l4,-41l12,-97l13,-94l12,-61l2,-7c2,-5 4,-6 9,-3l10,9l26,41c3,4 6,5 11,4c28,-6 57,-5 85,1c5,1 7,-1 9,-5a1453,1453 0,0 1,30 -49c4,-4 7,-4 9,2l8,36l28,177c5,34 8,67 11,100c2,12 1,13 -11,14c-54,5 -108,5 -162,4c-30,0 -59,-2 -88,-3c-6,0 -12,-3 -18,-4l-17,-1c-18,-2 -34,-9 -44,-26c-17,-28 -6,-65 29,-75c4,-1 8,0 12,1c3,2 3,5 0,7l-6,5c-4,3 -9,6 -12,10a31,31 0,0 0,7 49c10,5 20,6 31,6zM176.758,139.156c-8,0 -15,7 -15,15s6,15 15,15c8,0 15,-6 15,-15c0,-8 -7,-15 -15,-15zM299.758,169.156c8,0 15,-6 15,-15c0,-8 -6,-15 -15,-15a15,15 0,1 0,0 30zM216.758,189.156c4,7 11,7 20,1c7,6 16,6 18,-1c-8,3 -14,2 -19,-7c-4,9 -10,10 -19,7z"/>
|
||||
</vector>
|
||||
Reference in new issue
Block a user