perf: move root surfaces to recycler-backed pages

This commit is contained in:
kris
2026-04-04 01:31:02 +08:00
parent 35bcf92d72
commit bf4b27b062
6 changed files with 280 additions and 53 deletions

View File

@@ -12,6 +12,7 @@ import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.Button;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.ScrollView;
@@ -20,6 +21,7 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.viewpager2.widget.ViewPager2;
@@ -65,6 +67,7 @@ public class MainActivity extends AppCompatActivity {
private Button tabMe;
private ViewPager2 rootPager;
private SwipeRefreshLayout screenRefresh;
private RecyclerView screenList;
private ScrollView screenScroll;
private LinearLayout screenContent;
@@ -461,7 +464,7 @@ public class MainActivity extends AppCompatActivity {
switch (activeTab) {
case "devices":
updateHeader("设备", "这里管理已接入设备与账号状态。");
updateHeader("设备", "");
configureTopAction(WechatSurfaceMapper.rootTopAction(activeTab, false));
renderDevicesRoot();
break;
@@ -533,17 +536,18 @@ public class MainActivity extends AppCompatActivity {
}
private void renderConversationsRoot() {
if (screenContent == null) {
if (screenList == null) {
return;
}
screenContent.removeAllViews();
screenContent.addView(buildConversationSearchInput());
List<RootListItem> items = new ArrayList<>();
items.add(() -> buildConversationSearchInput());
if (conversationSelectionMode) {
appendConversationSelectionControls();
appendConversationSelectionControls(items);
}
JSONArray filteredConversations = filterConversationItems(conversationsData, conversationSearchQuery);
if (filteredConversations == null || filteredConversations.length() == 0) {
screenContent.addView(BossUi.buildEmptyCard(this, "当前没有会话数据。"));
items.add(() -> BossUi.buildEmptyCard(this, "当前没有会话数据。"));
showListPage(items);
return;
}
@@ -560,7 +564,7 @@ public class MainActivity extends AppCompatActivity {
}
if (pinnedItems.length() > 0) {
screenContent.addView(BossUi.buildConversationSectionHeader(
items.add(() -> BossUi.buildConversationSectionHeader(
this,
"置顶会话",
pinnedConversationsCollapsed ? "展开" : "收起",
@@ -570,13 +574,14 @@ public class MainActivity extends AppCompatActivity {
}
));
if (!pinnedConversationsCollapsed) {
appendConversationRows(pinnedItems);
appendConversationRows(items, pinnedItems);
}
}
appendConversationRows(regularItems);
appendConversationRows(items, regularItems);
showListPage(items);
}
private void appendConversationRows(JSONArray items) {
private void appendConversationRows(List<RootListItem> rootItems, JSONArray items) {
for (int i = 0; i < items.length(); i++) {
JSONObject item = items.optJSONObject(i);
if (item == null) continue;
@@ -584,7 +589,7 @@ public class MainActivity extends AppCompatActivity {
String conversationType = item.optString("conversationType", "");
String folderKey = item.optString("folderKey", "");
WechatSurfaceMapper.ConversationRow row = WechatSurfaceMapper.toConversationRow(item);
screenContent.addView(BossUi.buildConversationRow(
rootItems.add(() -> BossUi.buildConversationRow(
this,
row,
conversationSelectionMode,
@@ -620,8 +625,8 @@ public class MainActivity extends AppCompatActivity {
}
}
private void appendConversationSelectionControls() {
screenContent.addView(buildSelectionSummaryView());
private void appendConversationSelectionControls(List<RootListItem> items) {
items.add(() -> buildSelectionSummaryView());
Button cancelButton = BossUi.buildMiniActionButton(this, "取消", false);
cancelButton.setOnClickListener(v -> exitConversationSelectionMode());
@@ -630,7 +635,7 @@ public class MainActivity extends AppCompatActivity {
createButton.setEnabled(selectedConversationProjectIds.size() >= 2);
createButton.setOnClickListener(v -> createStandaloneGroupChatFromSelection());
screenContent.addView(BossUi.buildInlineActionRow(this, cancelButton, createButton));
items.add(() -> BossUi.buildInlineActionRow(this, cancelButton, createButton));
}
private LinearLayout buildSelectionSummaryView() {
@@ -826,12 +831,13 @@ public class MainActivity extends AppCompatActivity {
}
private void renderDevicesRoot() {
if (screenContent == null) {
if (screenList == null) {
return;
}
screenContent.removeAllViews();
List<RootListItem> items = new ArrayList<>();
if (devicesData == null || devicesData.length() == 0) {
screenContent.addView(BossUi.buildEmptyCard(this, "当前没有接入设备。"));
items.add(() -> BossUi.buildEmptyCard(this, "当前没有接入设备。"));
showListPage(items);
return;
}
@@ -840,7 +846,7 @@ public class MainActivity extends AppCompatActivity {
if (item == null) continue;
String deviceId = item.optString("id", "");
WechatSurfaceMapper.DeviceRow row = WechatSurfaceMapper.toDeviceRow(item);
screenContent.addView(BossUi.buildDeviceCard(
items.add(() -> BossUi.buildDeviceCard(
this,
row,
v -> {
@@ -853,12 +859,14 @@ public class MainActivity extends AppCompatActivity {
null
));
}
showListPage(items);
}
private void renderMeRoot() {
if (screenContent == null) {
return;
}
showScrollPage();
screenContent.removeAllViews();
String displayName = sessionData == null
? apiClient.getDisplayName()
@@ -1117,6 +1125,7 @@ public class MainActivity extends AppCompatActivity {
private void syncActivePageViews(String tab) {
if (rootPagerAdapter == null) {
screenRefresh = null;
screenList = null;
screenScroll = null;
screenContent = null;
return;
@@ -1124,12 +1133,14 @@ public class MainActivity extends AppCompatActivity {
RootPageViewHolder holder = rootPagerAdapter.findHolder(tab);
if (holder == null) {
screenRefresh = null;
screenList = null;
screenScroll = null;
screenContent = null;
rootPager.post(() -> {
RootPageViewHolder pendingHolder = rootPagerAdapter.findHolder(activeTab);
if (pendingHolder != null) {
screenRefresh = pendingHolder.refresh;
screenList = pendingHolder.list;
screenScroll = pendingHolder.scroll;
screenContent = pendingHolder.content;
renderCurrentTab();
@@ -1139,10 +1150,43 @@ public class MainActivity extends AppCompatActivity {
return;
}
screenRefresh = holder.refresh;
screenList = holder.list;
screenScroll = holder.scroll;
screenContent = holder.content;
}
private void showListPage(List<RootListItem> items) {
if (screenList == null || screenScroll == null) {
return;
}
screenList.setVisibility(View.VISIBLE);
screenScroll.setVisibility(View.GONE);
RootListAdapter adapter = adapterForCurrentTab();
if (adapter != null) {
adapter.submit(items);
}
}
private void showScrollPage() {
if (screenList == null || screenScroll == null) {
return;
}
RootListAdapter adapter = adapterForCurrentTab();
if (adapter != null) {
adapter.submit(new ArrayList<>());
}
screenList.setVisibility(View.GONE);
screenScroll.setVisibility(View.VISIBLE);
}
private @Nullable RootListAdapter adapterForCurrentTab() {
if (rootPagerAdapter == null) {
return null;
}
RootPageViewHolder holder = rootPagerAdapter.findHolder(activeTab);
return holder == null ? null : holder.listAdapter;
}
private static int indexForTab(String tab) {
switch (tab) {
case "devices":
@@ -1224,8 +1268,10 @@ public class MainActivity extends AppCompatActivity {
private static final class RootPageViewHolder extends RecyclerView.ViewHolder {
final String tab;
final SwipeRefreshLayout refresh;
final RecyclerView list;
final ScrollView scroll;
final LinearLayout content;
final RootListAdapter listAdapter;
static RootPageViewHolder create(LayoutInflater inflater, ViewGroup parent, String tab) {
View view = inflater.inflate(R.layout.view_root_tab_page, parent, false);
@@ -1236,8 +1282,61 @@ public class MainActivity extends AppCompatActivity {
super(itemView);
this.tab = tab;
this.refresh = itemView.findViewById(R.id.root_page_refresh);
this.list = itemView.findViewById(R.id.root_page_list);
this.scroll = itemView.findViewById(R.id.root_page_scroll);
this.content = itemView.findViewById(R.id.root_page_content);
this.list.setLayoutManager(new LinearLayoutManager(itemView.getContext()));
this.listAdapter = new RootListAdapter();
this.list.setAdapter(listAdapter);
}
}
private interface RootListItem {
View build();
}
private static final class RootListViewHolder extends RecyclerView.ViewHolder {
final FrameLayout container;
RootListViewHolder(FrameLayout container) {
super(container);
this.container = container;
}
}
private static final class RootListAdapter extends RecyclerView.Adapter<RootListViewHolder> {
private List<RootListItem> items = new ArrayList<>();
@Override
public int getItemCount() {
return items.size();
}
@Override
public RootListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
FrameLayout container = new FrameLayout(parent.getContext());
RecyclerView.LayoutParams params = new RecyclerView.LayoutParams(
RecyclerView.LayoutParams.MATCH_PARENT,
RecyclerView.LayoutParams.WRAP_CONTENT
);
container.setLayoutParams(params);
return new RootListViewHolder(container);
}
@Override
public void onBindViewHolder(RootListViewHolder holder, int position) {
holder.container.removeAllViews();
View child = items.get(position).build();
ViewParent parent = child.getParent();
if (parent instanceof ViewGroup) {
((ViewGroup) parent).removeView(child);
}
holder.container.addView(child);
}
void submit(List<RootListItem> nextItems) {
items = new ArrayList<>(nextItems);
notifyDataSetChanged();
}
}
}

View File

@@ -4,21 +4,37 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="@+id/root_page_scroll"
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/root_page_content"
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/root_page_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:background="@color/boss_bg_app"
android:orientation="vertical"
android:clipToPadding="false"
android:paddingTop="12dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingBottom="88dp" />
</ScrollView>
android:paddingBottom="88dp"
android:scrollbars="vertical" />
<ScrollView
android:id="@+id/root_page_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:visibility="gone">
<LinearLayout
android:id="@+id/root_page_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/boss_bg_app"
android:orientation="vertical"
android:paddingTop="12dp"
android:paddingLeft="0dp"
android:paddingRight="0dp"
android:paddingBottom="88dp" />
</ScrollView>
</FrameLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>