From 718388db51f2efd2609fa42949ba1a1f0f9c974f Mon Sep 17 00:00:00 2001 From: Kr328 <39107975+Kr328@users.noreply.github.com> Date: Sat, 15 Feb 2020 14:42:09 +0800 Subject: [PATCH] [WIP] UI refactor --- .../com/github/kr328/clash/ProxiesActivity.kt | 67 +++----------- .../kr328/clash/adapter/ProxyChipAdapter.kt | 54 +++++++++++- .../kr328/clash/view/ProxiesTabMediator.kt | 88 +++++++++++++++++++ .../main/res/layout/adapter_proxies_chip.xml | 3 +- .../src/main/res/drawable/ic_notification.xml | 10 +-- 5 files changed, 158 insertions(+), 64 deletions(-) create mode 100644 app/src/main/java/com/github/kr328/clash/view/ProxiesTabMediator.kt diff --git a/app/src/main/java/com/github/kr328/clash/ProxiesActivity.kt b/app/src/main/java/com/github/kr328/clash/ProxiesActivity.kt index 253b6d8..a06c861 100644 --- a/app/src/main/java/com/github/kr328/clash/ProxiesActivity.kt +++ b/app/src/main/java/com/github/kr328/clash/ProxiesActivity.kt @@ -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 = mutableSetOf() - private val updateChipChannel = Channel(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() + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/adapter/ProxyChipAdapter.kt b/app/src/main/java/com/github/kr328/clash/adapter/ProxyChipAdapter.kt index b3061f7..964c155 100644 --- a/app/src/main/java/com/github/kr328/clash/adapter/ProxyChipAdapter.kt +++ b/app/src/main/java/com/github/kr328/clash/adapter/ProxyChipAdapter.kt @@ -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)) } } } \ No newline at end of file diff --git a/app/src/main/java/com/github/kr328/clash/view/ProxiesTabMediator.kt b/app/src/main/java/com/github/kr328/clash/view/ProxiesTabMediator.kt new file mode 100644 index 0000000..48929e7 --- /dev/null +++ b/app/src/main/java/com/github/kr328/clash/view/ProxiesTabMediator.kt @@ -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(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) + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_proxies_chip.xml b/app/src/main/res/layout/adapter_proxies_chip.xml index d43393f..125faa0 100644 --- a/app/src/main/res/layout/adapter_proxies_chip.xml +++ b/app/src/main/res/layout/adapter_proxies_chip.xml @@ -9,7 +9,8 @@ android:layout_marginTop="10dp" app:cardCornerRadius="12dp" android:focusable="true" - android:clickable="true"> + android:clickable="true" + app:cardBackgroundColor="@color/chipBackgroundColor"> + android:width="402.47244dp" + android:height="381.04724dp" + android:viewportWidth="402.47244" + android:viewportHeight="381.04724"> + 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"/>