From 81816b12a200abebbcc0ad2ef862d0178cfe4f59 Mon Sep 17 00:00:00 2001 From: Tungstend Date: Thu, 3 Nov 2022 21:42:23 +0800 Subject: [PATCH] add ui manager & add a custom dynamic island --- .../tungsten/fcl/activity/MainActivity.java | 28 +++-- .../com/tungsten/fcl/ui/ChildUIManager.java | 4 + .../java/com/tungsten/fcl/ui/UIManager.java | 79 ++++++++++++ .../java/com/tungsten/fcl/ui/main/MainUI.java | 18 +++ FCL/src/main/res/layout/activity_main.xml | 19 +-- FCL/src/main/res/layout/ui_main.xml | 6 + .../fcllibrary/anim/DynamicIslandAnim.java | 92 ++++++++++++++ .../fcllibrary/component/ui/FCLCommonUI.java | 1 + .../component/view/FCLDynamicIsland.java | 116 ++++++++++++++++++ 9 files changed, 347 insertions(+), 16 deletions(-) create mode 100644 FCL/src/main/java/com/tungsten/fcl/ui/ChildUIManager.java create mode 100644 FCL/src/main/java/com/tungsten/fcl/ui/UIManager.java create mode 100644 FCL/src/main/java/com/tungsten/fcl/ui/main/MainUI.java create mode 100644 FCL/src/main/res/layout/ui_main.xml create mode 100644 FCLLibrary/src/main/java/com/tungsten/fcllibrary/anim/DynamicIslandAnim.java create mode 100644 FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLDynamicIsland.java diff --git a/FCL/src/main/java/com/tungsten/fcl/activity/MainActivity.java b/FCL/src/main/java/com/tungsten/fcl/activity/MainActivity.java index d9f820d3..fc3eed6c 100644 --- a/FCL/src/main/java/com/tungsten/fcl/activity/MainActivity.java +++ b/FCL/src/main/java/com/tungsten/fcl/activity/MainActivity.java @@ -6,14 +6,20 @@ import androidx.constraintlayout.widget.ConstraintLayout; import com.google.android.material.tabs.TabLayout; import com.tungsten.fcl.R; +import com.tungsten.fcl.ui.UIManager; import com.tungsten.fcllibrary.component.FCLActivity; import com.tungsten.fcllibrary.component.theme.ThemeEngine; +import com.tungsten.fcllibrary.component.view.FCLDynamicIsland; import com.tungsten.fcllibrary.component.view.FCLTitleView; +import com.tungsten.fcllibrary.component.view.FCLUILayout; public class MainActivity extends FCLActivity implements TabLayout.OnTabSelectedListener { public ConstraintLayout background; - private FCLTitleView titleView; + private FCLDynamicIsland titleView; + + private UIManager uiManager; + private FCLUILayout uiLayout; private TabLayout tabLayout; @@ -26,6 +32,11 @@ public class MainActivity extends FCLActivity implements TabLayout.OnTabSelected background.setBackground(ThemeEngine.getInstance().getTheme().getBackground(this)); titleView = findViewById(R.id.title); + + uiLayout = findViewById(R.id.ui_layout); + uiManager = new UIManager(this, uiLayout); + uiManager.init(); + tabLayout = findViewById(R.id.tab_layout); tabLayout.addOnTabSelectedListener(this); tabLayout.selectTab(tabLayout.getTabAt(3)); @@ -38,25 +49,26 @@ public class MainActivity extends FCLActivity implements TabLayout.OnTabSelected public void onTabSelected(TabLayout.Tab tab) { switch (tab.getPosition()) { case 0: - titleView.setTitle(getString(R.string.account)); + titleView.setTextWithAnim(getString(R.string.account)); break; case 1: - titleView.setTitle(getString(R.string.version)); + titleView.setTextWithAnim(getString(R.string.version)); break; case 2: - titleView.setTitle(getString(R.string.install)); + titleView.setTextWithAnim(getString(R.string.install)); break; case 4: - titleView.setTitle(getString(R.string.download)); + titleView.setTextWithAnim(getString(R.string.download)); break; case 5: - titleView.setTitle(getString(R.string.multiplayer)); + titleView.setTextWithAnim(getString(R.string.multiplayer)); break; case 6: - titleView.setTitle(getString(R.string.setting)); + titleView.setTextWithAnim(getString(R.string.setting)); break; default: - titleView.setTitle(getString(R.string.app_name)); + titleView.setTextWithAnim(getString(R.string.app_name)); + uiManager.switchUI(uiManager.getMainUI()); break; } } diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/ChildUIManager.java b/FCL/src/main/java/com/tungsten/fcl/ui/ChildUIManager.java new file mode 100644 index 00000000..90cb3077 --- /dev/null +++ b/FCL/src/main/java/com/tungsten/fcl/ui/ChildUIManager.java @@ -0,0 +1,4 @@ +package com.tungsten.fcl.ui; + +public class ChildUIManager { +} diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/UIManager.java b/FCL/src/main/java/com/tungsten/fcl/ui/UIManager.java new file mode 100644 index 00000000..da881211 --- /dev/null +++ b/FCL/src/main/java/com/tungsten/fcl/ui/UIManager.java @@ -0,0 +1,79 @@ +package com.tungsten.fcl.ui; + +import android.content.Context; + +import com.tungsten.fcl.R; +import com.tungsten.fcl.ui.main.MainUI; +import com.tungsten.fclcore.util.Logging; +import com.tungsten.fcllibrary.component.ui.FCLBaseUI; +import com.tungsten.fcllibrary.component.view.FCLUILayout; + +import java.util.logging.Level; + +public class UIManager { + + private static UIManager instance; + + public static UIManager getInstance() { + if (instance == null) { + throw new IllegalStateException("UIManager not initialized!"); + } + return instance; + } + + private final Context context; + private final FCLUILayout parent; + + private MainUI mainUI; + + private FCLBaseUI[] allUIs; + private FCLBaseUI currentUI; + + public UIManager(Context context, FCLUILayout parent) { + this.context = context; + this.parent = parent; + } + + public void init() { + if (instance != null) { + Logging.LOG.log(Level.WARNING, "UIManager already initialized!"); + return; + } + instance = this; + + mainUI = new MainUI(context, parent, R.layout.ui_main); + + allUIs = new FCLBaseUI[] { + mainUI + }; + } + + public Context getContext() { + return context; + } + + public FCLUILayout getParent() { + return parent; + } + + public MainUI getMainUI() { + return mainUI; + } + + public FCLBaseUI getCurrentUI() { + return currentUI; + } + + public void switchUI(FCLBaseUI ui) { + for (FCLBaseUI baseUI : allUIs) { + if (ui == baseUI) { + if (currentUI != null) { + currentUI.onStop(); + } + ui.onStart(); + currentUI = ui; + break; + } + } + } +} diff --git a/FCL/src/main/java/com/tungsten/fcl/ui/main/MainUI.java b/FCL/src/main/java/com/tungsten/fcl/ui/main/MainUI.java new file mode 100644 index 00000000..02db96d7 --- /dev/null +++ b/FCL/src/main/java/com/tungsten/fcl/ui/main/MainUI.java @@ -0,0 +1,18 @@ +package com.tungsten.fcl.ui.main; + +import android.content.Context; + +import com.tungsten.fcllibrary.component.ui.FCLCommonUI; +import com.tungsten.fcllibrary.component.view.FCLUILayout; + +public class MainUI extends FCLCommonUI { + + public MainUI(Context context, FCLUILayout parent, int id) { + super(context, parent, id); + } + + @Override + public void refresh() { + + } +} diff --git a/FCL/src/main/res/layout/activity_main.xml b/FCL/src/main/res/layout/activity_main.xml index 25150406..5d0abee6 100644 --- a/FCL/src/main/res/layout/activity_main.xml +++ b/FCL/src/main/res/layout/activity_main.xml @@ -7,16 +7,19 @@ android:id="@+id/background" tools:context=".activity.MainActivity"> - + + diff --git a/FCL/src/main/res/layout/ui_main.xml b/FCL/src/main/res/layout/ui_main.xml new file mode 100644 index 00000000..77d9ef65 --- /dev/null +++ b/FCL/src/main/res/layout/ui_main.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/anim/DynamicIslandAnim.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/anim/DynamicIslandAnim.java new file mode 100644 index 00000000..a9c3eddf --- /dev/null +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/anim/DynamicIslandAnim.java @@ -0,0 +1,92 @@ +package com.tungsten.fcllibrary.anim; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.ObjectAnimator; + +import com.tungsten.fcllibrary.component.view.FCLDynamicIsland; + +public class DynamicIslandAnim { + + private final FCLDynamicIsland view; + + private ObjectAnimator expandScaleAnimatorX; + private ObjectAnimator shrinkScaleAnimatorX; + private ObjectAnimator expandScaleAnimatorY; + private ObjectAnimator shrinkScaleAnimatorY; + private ObjectAnimator expandAdjustAnimatorX; + private ObjectAnimator shrinkAdjustAnimatorX; + private ObjectAnimator expandAdjustAnimatorY; + private ObjectAnimator shrinkAdjustAnimatorY; + + public DynamicIslandAnim(FCLDynamicIsland view) { + this.view = view; + } + + public void refresh(float scale) { + if (expandScaleAnimatorX != null && expandScaleAnimatorX.isRunning()) { + expandScaleAnimatorX.cancel(); + } + if (shrinkScaleAnimatorX != null && shrinkScaleAnimatorX.isRunning()) { + shrinkScaleAnimatorX.cancel(); + } + if (expandScaleAnimatorY != null && expandScaleAnimatorY.isRunning()) { + expandScaleAnimatorY.cancel(); + } + if (shrinkScaleAnimatorY != null && shrinkScaleAnimatorY.isRunning()) { + shrinkScaleAnimatorY.cancel(); + } + if (expandAdjustAnimatorX != null && expandAdjustAnimatorX.isRunning()) { + expandAdjustAnimatorX.cancel(); + } + if (shrinkAdjustAnimatorX != null && shrinkAdjustAnimatorX.isRunning()) { + shrinkAdjustAnimatorX.cancel(); + } + if (expandAdjustAnimatorY != null && expandAdjustAnimatorY.isRunning()) { + expandAdjustAnimatorY.cancel(); + } + if (shrinkAdjustAnimatorY != null && shrinkAdjustAnimatorY.isRunning()) { + shrinkAdjustAnimatorY.cancel(); + } + expandScaleAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", scale, 0.95f).setDuration(200); + shrinkScaleAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", 0.95f, scale).setDuration(200); + expandScaleAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", 0.95f, 0.95f).setDuration(200); + shrinkScaleAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", 0.95f, 0.95f).setDuration(200); + expandAdjustAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", 0.95f, 1f).setDuration(200); + shrinkAdjustAnimatorX = ObjectAnimator.ofFloat(view, "scaleX", 1f, 0.95f).setDuration(200); + expandAdjustAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", 0.95f, 1f).setDuration(200); + shrinkAdjustAnimatorY = ObjectAnimator.ofFloat(view, "scaleY", 1f, 0.95f).setDuration(200); + } + + public void run(String text) { + shrinkScaleAnimatorX.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + view.refresh(text); + view.post(() -> { + expandScaleAnimatorX.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + expandAdjustAnimatorX.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + shrinkAdjustAnimatorX.start(); + shrinkAdjustAnimatorY.start(); + } + }); + expandAdjustAnimatorX.start(); + expandAdjustAnimatorY.start(); + } + }); + expandScaleAnimatorX.start(); + expandScaleAnimatorY.start(); + }); + } + }); + shrinkScaleAnimatorX.start(); + shrinkScaleAnimatorY.start(); + } +} diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/ui/FCLCommonUI.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/ui/FCLCommonUI.java index 76ea0f2f..17c14d1d 100644 --- a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/ui/FCLCommonUI.java +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/ui/FCLCommonUI.java @@ -18,6 +18,7 @@ public abstract class FCLCommonUI extends FCLBaseUI { super(context); setContentView(id); this.parent = parent; + onCreate(); } @Override diff --git a/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLDynamicIsland.java b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLDynamicIsland.java new file mode 100644 index 00000000..a1fe4091 --- /dev/null +++ b/FCLLibrary/src/main/java/com/tungsten/fcllibrary/component/view/FCLDynamicIsland.java @@ -0,0 +1,116 @@ +package com.tungsten.fcllibrary.component.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.util.AttributeSet; +import android.view.Gravity; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.appcompat.widget.AppCompatTextView; + +import com.tungsten.fcllibrary.anim.DynamicIslandAnim; +import com.tungsten.fcllibrary.component.theme.ThemeEngine; +import com.tungsten.fcllibrary.util.ConvertUtils; + +public class FCLDynamicIsland extends AppCompatTextView { + + DynamicIslandAnim anim; + + private Path outlinePath; + private Paint outlinePaint; + private Paint insidePaint; + private Paint textPaint; + + private final Runnable runnable = () -> { + outlinePaint.setAntiAlias(true); + outlinePaint.setColor(ThemeEngine.getInstance().getTheme().getDkColor()); + outlinePaint.setStyle(Paint.Style.STROKE); + outlinePaint.setStrokeWidth(ConvertUtils.dip2px(getContext(), 3)); + insidePaint.setAntiAlias(true); + insidePaint.setColor(ThemeEngine.getInstance().getTheme().getColor()); + insidePaint.setStyle(Paint.Style.FILL); + textPaint.setAntiAlias(true); + textPaint.setStyle(Paint.Style.FILL); + textPaint.setTextSize(56); + textPaint.setColor(ThemeEngine.getInstance().getTheme().getAutoTint()); + textPaint.setTextAlign(Paint.Align.CENTER); + invalidate(); + setTextColor(ThemeEngine.getInstance().getTheme().getAutoTint()); + }; + + private void init() { + anim = new DynamicIslandAnim(this); + setGravity(Gravity.CENTER); + int tb = ConvertUtils.dip2px(getContext(), 8f); + int lr = ConvertUtils.dip2px(getContext(), 18f); + setPadding(lr, tb, lr, tb); + outlinePath = new Path(); + outlinePaint = new Paint(); + insidePaint = new Paint(); + textPaint = new Paint(); + outlinePaint.setAntiAlias(true); + outlinePaint.setColor(ThemeEngine.getInstance().getTheme().getDkColor()); + outlinePaint.setStyle(Paint.Style.STROKE); + outlinePaint.setStrokeWidth(ConvertUtils.dip2px(getContext(), 3)); + insidePaint.setAntiAlias(true); + insidePaint.setColor(ThemeEngine.getInstance().getTheme().getColor()); + insidePaint.setStyle(Paint.Style.FILL); + textPaint.setAntiAlias(true); + textPaint.setStyle(Paint.Style.FILL); + textPaint.setTextSize(56); + textPaint.setColor(ThemeEngine.getInstance().getTheme().getAutoTint()); + textPaint.setTextAlign(Paint.Align.CENTER); + } + + public FCLDynamicIsland(@NonNull Context context) { + super(context); + init(); + ThemeEngine.getInstance().registerEvent(this, runnable); + } + + public FCLDynamicIsland(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + init(); + ThemeEngine.getInstance().registerEvent(this, runnable); + } + + public FCLDynamicIsland(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(); + ThemeEngine.getInstance().registerEvent(this, runnable); + } + + @SuppressLint("DrawAllocation") + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + float offset = ConvertUtils.dip2px(getContext(), 1.5f); + float width = getMeasuredWidth() - 2 * offset; + float height = getMeasuredHeight() - 2 * offset; + outlinePath = new Path(); + outlinePath.moveTo(offset + (height / 2), offset); + outlinePath.arcTo(offset, offset, offset + height, offset + height, 270, -180, false); + outlinePath.lineTo(offset + width - (height / 2), height + offset); + outlinePath.arcTo(offset + width - height, offset, width + offset, height + offset, 90, -180, false); + outlinePath.lineTo(offset + (height / 2), offset); + canvas.drawPath(outlinePath, insidePaint); + canvas.drawPath(outlinePath, outlinePaint); + canvas.drawText(getText().toString(), (int) (getWidth() / 2), (int) (getHeight() / 2) + 21, textPaint); + } + + public void refresh(String text) { + setText(text); + invalidate(); + } + + public void setTextWithAnim(String text) { + post(() -> { + anim.refresh((float) getMeasuredHeight() / (float) getMeasuredWidth()); + anim.run(text); + }); + } +}