style: switch conversation quick actions to dropdown menu

This commit is contained in:
kris
2026-04-04 02:30:54 +08:00
parent 9d19163b0d
commit d126c46479
4 changed files with 108 additions and 108 deletions

View File

@@ -68,6 +68,7 @@ public class MainActivity extends AppCompatActivity {
private Button refreshButton;
private View conversationQuickActionsOverlay;
private View conversationQuickActionsScrim;
private View conversationQuickActionsMenu;
private View quickActionAddDevice;
private View quickActionScan;
private View quickActionGroupChat;
@@ -204,6 +205,7 @@ public class MainActivity extends AppCompatActivity {
refreshButton = findViewById(R.id.refresh_button);
conversationQuickActionsOverlay = findViewById(R.id.conversation_quick_actions_overlay);
conversationQuickActionsScrim = findViewById(R.id.conversation_quick_actions_scrim);
conversationQuickActionsMenu = findViewById(R.id.conversation_quick_actions_menu);
quickActionAddDevice = findViewById(R.id.quick_action_add_device);
quickActionScan = findViewById(R.id.quick_action_scan);
quickActionGroupChat = findViewById(R.id.quick_action_group_chat);
@@ -959,7 +961,7 @@ public class MainActivity extends AppCompatActivity {
}
private void showConversationQuickActions() {
if (conversationQuickActionsOverlay == null || conversationQuickActionsVisible
if (conversationQuickActionsOverlay == null || conversationQuickActionsMenu == null || conversationQuickActionsVisible
|| !"conversations".equals(activeTab) || conversationSelectionMode || conversationSearchMode) {
return;
}
@@ -967,21 +969,22 @@ public class MainActivity extends AppCompatActivity {
conversationQuickActionsOverlay.setVisibility(View.VISIBLE);
conversationQuickActionsOverlay.bringToFront();
conversationQuickActionsScrim.setAlpha(0f);
prepareConversationQuickAction(quickActionAddDevice);
prepareConversationQuickAction(quickActionScan);
prepareConversationQuickAction(quickActionGroupChat);
prepareConversationQuickActionMenu();
conversationQuickActionsScrim.animate()
.alpha(1f)
.setDuration(160L)
.setDuration(140L)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();
conversationQuickActionsMenu.animate()
.alpha(1f)
.translationY(0f)
.setDuration(180L)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();
animateConversationQuickAction(quickActionAddDevice, -BossUi.dp(this, 12), BossUi.dp(this, 84), 0L);
animateConversationQuickAction(quickActionScan, -BossUi.dp(this, 90), BossUi.dp(this, 50), 24L);
animateConversationQuickAction(quickActionGroupChat, -BossUi.dp(this, 172), BossUi.dp(this, 14), 48L);
}
private void hideConversationQuickActions(boolean animated) {
if (conversationQuickActionsOverlay == null) {
if (conversationQuickActionsOverlay == null || conversationQuickActionsMenu == null) {
return;
}
if (!conversationQuickActionsVisible && conversationQuickActionsOverlay.getVisibility() != View.VISIBLE) {
@@ -991,71 +994,39 @@ public class MainActivity extends AppCompatActivity {
if (!animated) {
conversationQuickActionsOverlay.setVisibility(View.GONE);
conversationQuickActionsScrim.setAlpha(0f);
resetConversationQuickAction(quickActionAddDevice);
resetConversationQuickAction(quickActionScan);
resetConversationQuickAction(quickActionGroupChat);
resetConversationQuickActionMenu();
return;
}
conversationQuickActionsScrim.animate()
.alpha(0f)
.setDuration(120L)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();
conversationQuickActionsMenu.animate()
.alpha(0f)
.translationY(-BossUi.dp(this, 8))
.setDuration(140L)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();
collapseConversationQuickAction(quickActionAddDevice, 0L);
collapseConversationQuickAction(quickActionScan, 20L);
collapseConversationQuickAction(quickActionGroupChat, 40L);
conversationQuickActionsOverlay.postDelayed(() -> {
if (!conversationQuickActionsVisible) {
conversationQuickActionsOverlay.setVisibility(View.GONE);
resetConversationQuickAction(quickActionAddDevice);
resetConversationQuickAction(quickActionScan);
resetConversationQuickAction(quickActionGroupChat);
resetConversationQuickActionMenu();
}
}, 190L);
}, 150L);
}
private void prepareConversationQuickAction(View actionView) {
actionView.setVisibility(View.VISIBLE);
actionView.setAlpha(0f);
actionView.setScaleX(0.86f);
actionView.setScaleY(0.86f);
actionView.setTranslationX(0f);
actionView.setTranslationY(0f);
private void prepareConversationQuickActionMenu() {
conversationQuickActionsMenu.setVisibility(View.VISIBLE);
conversationQuickActionsMenu.setAlpha(0f);
conversationQuickActionsMenu.setTranslationY(-BossUi.dp(this, 8));
}
private void animateConversationQuickAction(View actionView, float translationX, float translationY, long delayMs) {
actionView.animate()
.alpha(1f)
.scaleX(1f)
.scaleY(1f)
.translationX(translationX)
.translationY(translationY)
.setStartDelay(delayMs)
.setDuration(180L)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();
}
private void collapseConversationQuickAction(View actionView, long delayMs) {
actionView.animate()
.alpha(0f)
.scaleX(0.88f)
.scaleY(0.88f)
.translationX(0f)
.translationY(0f)
.setStartDelay(delayMs)
.setDuration(140L)
.setInterpolator(new AccelerateDecelerateInterpolator())
.start();
}
private void resetConversationQuickAction(View actionView) {
actionView.animate().cancel();
actionView.setAlpha(0f);
actionView.setScaleX(0.86f);
actionView.setScaleY(0.86f);
actionView.setTranslationX(0f);
actionView.setTranslationY(0f);
private void resetConversationQuickActionMenu() {
conversationQuickActionsMenu.animate().cancel();
conversationQuickActionsMenu.setAlpha(0f);
conversationQuickActionsMenu.setTranslationY(-BossUi.dp(this, 8));
conversationQuickActionsMenu.setVisibility(View.GONE);
}
static boolean matchesConversationQuery(JSONObject item, String rawQuery) {

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#F2272727" />
<corners android:radius="18dp" />
</shape>

View File

@@ -273,59 +273,62 @@
android:paddingTop="22dp"
android:paddingRight="20dp">
<Button
android:id="@+id/quick_action_add_device"
android:layout_width="wrap_content"
<LinearLayout
android:id="@+id/conversation_quick_actions_menu"
android:layout_width="196dp"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:background="@drawable/bg_secondary_button"
android:minWidth="0dp"
android:paddingLeft="14dp"
android:paddingTop="10dp"
android:paddingRight="14dp"
android:paddingBottom="10dp"
android:text="添加设备"
android:textAllCaps="false"
android:textColor="@color/boss_text_primary"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="invisible" />
android:alpha="0"
android:background="@drawable/bg_conversation_quick_actions_menu"
android:elevation="14dp"
android:orientation="vertical"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:translationY="-8dp"
android:visibility="gone">
<Button
android:id="@+id/quick_action_scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:background="@drawable/bg_secondary_button"
android:minWidth="0dp"
android:paddingLeft="14dp"
android:paddingTop="10dp"
android:paddingRight="14dp"
android:paddingBottom="10dp"
android:text="扫一扫"
android:textAllCaps="false"
android:textColor="@color/boss_text_primary"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="invisible" />
<Button
android:id="@+id/quick_action_add_device"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@android:color/transparent"
android:gravity="center_vertical|start"
android:minWidth="0dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="添加设备"
android:textAllCaps="false"
android:textColor="@android:color/white"
android:textSize="15sp" />
<Button
android:id="@+id/quick_action_group_chat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|end"
android:background="@drawable/bg_secondary_button"
android:minWidth="0dp"
android:paddingLeft="14dp"
android:paddingTop="10dp"
android:paddingRight="14dp"
android:paddingBottom="10dp"
android:text="发起群聊"
android:textAllCaps="false"
android:textColor="@color/boss_text_primary"
android:textSize="14sp"
android:textStyle="bold"
android:visibility="invisible" />
<Button
android:id="@+id/quick_action_scan"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@android:color/transparent"
android:gravity="center_vertical|start"
android:minWidth="0dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="扫一扫"
android:textAllCaps="false"
android:textColor="@android:color/white"
android:textSize="15sp" />
<Button
android:id="@+id/quick_action_group_chat"
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@android:color/transparent"
android:gravity="center_vertical|start"
android:minWidth="0dp"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:text="发起群聊"
android:textAllCaps="false"
android:textColor="@android:color/white"
android:textSize="15sp" />
</LinearLayout>
</FrameLayout>
</FrameLayout>

View File

@@ -86,6 +86,26 @@ public class MainActivityConversationSelectionTest {
assertTrue(recyclerContainsText(list, "Boss 线程修复"));
}
@Test
public void topPlusAction_opensWechatStyleDropdownMenu() throws Exception {
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().get();
ReflectionHelpers.setField(activity, "conversationsData", buildConversations());
ReflectionHelpers.callInstanceMethod(activity, "showContent");
Shadows.shadowOf(activity.getMainLooper()).idle();
Button actionButton = activity.findViewById(R.id.refresh_button);
actionButton.performClick();
Shadows.shadowOf(activity.getMainLooper()).idle();
View overlay = activity.findViewById(R.id.conversation_quick_actions_overlay);
View menu = activity.findViewById(R.id.conversation_quick_actions_menu);
assertEquals(View.VISIBLE, overlay.getVisibility());
assertEquals(View.VISIBLE, menu.getVisibility());
assertTrue(viewTreeContainsText(menu, "添加设备"));
assertTrue(viewTreeContainsText(menu, "扫一扫"));
assertTrue(viewTreeContainsText(menu, "发起群聊"));
}
private static View getRecyclerChild(RecyclerView recyclerView, int position) {
RecyclerView.Adapter adapter = recyclerView.getAdapter();
int viewType = adapter.getItemViewType(position);