perf: move root surfaces to recycler-backed pages
This commit is contained in:
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -3,8 +3,11 @@ package com.hyzq.boss;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
@@ -58,9 +61,19 @@ public class MainActivityConversationSearchTest {
|
||||
ReflectionHelpers.callInstanceMethod(activity, "renderConversationsRoot");
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
|
||||
LinearLayout content = ReflectionHelpers.getField(activity, "screenContent");
|
||||
assertTrue(content.getChildAt(0) instanceof EditText);
|
||||
EditText input = (EditText) content.getChildAt(0);
|
||||
RecyclerView list = ReflectionHelpers.getField(activity, "screenList");
|
||||
View firstItem = buildRecyclerItem(list, 0);
|
||||
assertTrue(firstItem instanceof EditText);
|
||||
EditText input = (EditText) firstItem;
|
||||
assertEquals("搜索项目或线程", String.valueOf(input.getHint()));
|
||||
}
|
||||
|
||||
private static View buildRecyclerItem(RecyclerView recyclerView, int position) {
|
||||
RecyclerView.Adapter adapter = recyclerView.getAdapter();
|
||||
int viewType = adapter.getItemViewType(position);
|
||||
RecyclerView.ViewHolder holder = adapter.createViewHolder(recyclerView, viewType);
|
||||
adapter.bindViewHolder(holder, position);
|
||||
FrameLayout container = (FrameLayout) holder.itemView;
|
||||
return container.getChildAt(0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,12 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
@@ -31,21 +34,19 @@ public class MainActivityConversationSelectionTest {
|
||||
ReflectionHelpers.callInstanceMethod(activity, "enterConversationSelectionMode");
|
||||
assertTrue(ReflectionHelpers.getField(activity, "conversationSelectionMode"));
|
||||
|
||||
LinearLayout content = ReflectionHelpers.getField(activity, "screenContent");
|
||||
assertTrue(viewTreeContainsText(content, "发起群聊"));
|
||||
assertTrue(viewTreeContainsText(content, "至少选择 2 个线程"));
|
||||
RecyclerView list = ReflectionHelpers.getField(activity, "screenList");
|
||||
assertTrue(recyclerContainsText(list, "发起群聊"));
|
||||
assertTrue(recyclerContainsText(list, "至少选择 2 个线程"));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(activity, "toggleConversationSelection",
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "thread-1"));
|
||||
content = ReflectionHelpers.getField(activity, "screenContent");
|
||||
assertTrue(viewTreeContainsText(content, "已选 1 个线程"));
|
||||
assertTrue(viewTreeContainsText(content, "至少选择 2 个线程"));
|
||||
assertTrue(recyclerContainsText(list, "已选 1 个线程"));
|
||||
assertTrue(recyclerContainsText(list, "至少选择 2 个线程"));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(activity, "toggleConversationSelection",
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "thread-2"));
|
||||
content = ReflectionHelpers.getField(activity, "screenContent");
|
||||
assertTrue(viewTreeContainsText(content, "已选 2 个线程"));
|
||||
assertFalse(viewTreeContainsText(content, "至少选择 2 个线程"));
|
||||
assertTrue(recyclerContainsText(list, "已选 2 个线程"));
|
||||
assertFalse(recyclerContainsText(list, "至少选择 2 个线程"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -56,11 +57,30 @@ public class MainActivityConversationSelectionTest {
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
ReflectionHelpers.callInstanceMethod(activity, "enterConversationSelectionMode");
|
||||
|
||||
LinearLayout content = ReflectionHelpers.getField(activity, "screenContent");
|
||||
View row = content.getChildAt(3);
|
||||
RecyclerView list = ReflectionHelpers.getField(activity, "screenList");
|
||||
View row = getRecyclerChild(list, 3);
|
||||
assertTrue("多选模式应显示单选圆点", viewTreeContainsContentDescription(row, "未选中会话"));
|
||||
}
|
||||
|
||||
private static View getRecyclerChild(RecyclerView recyclerView, int position) {
|
||||
RecyclerView.Adapter adapter = recyclerView.getAdapter();
|
||||
int viewType = adapter.getItemViewType(position);
|
||||
RecyclerView.ViewHolder holder = adapter.createViewHolder(recyclerView, viewType);
|
||||
adapter.bindViewHolder(holder, position);
|
||||
FrameLayout container = (FrameLayout) holder.itemView;
|
||||
return container.getChildAt(0);
|
||||
}
|
||||
|
||||
private static boolean recyclerContainsText(RecyclerView recyclerView, String expectedText) {
|
||||
RecyclerView.Adapter adapter = recyclerView.getAdapter();
|
||||
for (int index = 0; index < adapter.getItemCount(); index += 1) {
|
||||
if (viewTreeContainsText(getRecyclerChild(recyclerView, index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static JSONArray buildConversations() throws Exception {
|
||||
return new JSONArray()
|
||||
.put(new JSONObject()
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.hyzq.boss;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.view.View;
|
||||
import android.view.View.MeasureSpec;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
import org.robolectric.Shadows;
|
||||
import org.robolectric.util.ReflectionHelpers;
|
||||
|
||||
@RunWith(RobolectricTestRunner.class)
|
||||
@Config(sdk = 34)
|
||||
public class MainActivityDevicesRootTest {
|
||||
@Test
|
||||
public void devicesTab_hidesLegacySubtitleAndUsesRecyclerList() throws Exception {
|
||||
MainActivity activity = Robolectric.buildActivity(MainActivity.class).setup().get();
|
||||
ReflectionHelpers.setField(activity, "devicesData", new JSONArray()
|
||||
.put(new JSONObject()
|
||||
.put("id", "mac-studio")
|
||||
.put("name", "Mac Studio")
|
||||
.put("status", "online")
|
||||
.put("platform", "macOS")
|
||||
.put("account", "17600003315")));
|
||||
|
||||
ReflectionHelpers.callInstanceMethod(activity, "showContent");
|
||||
ReflectionHelpers.callInstanceMethod(activity, "setActiveTab",
|
||||
ReflectionHelpers.ClassParameter.from(String.class, "devices"),
|
||||
ReflectionHelpers.ClassParameter.from(boolean.class, false));
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
|
||||
TextView subtitle = activity.findViewById(R.id.top_subtitle);
|
||||
assertEquals(View.GONE, subtitle.getVisibility());
|
||||
|
||||
RecyclerView list = ReflectionHelpers.getField(activity, "screenList");
|
||||
layoutRecyclerView(list);
|
||||
assertTrue(list.getVisibility() == View.VISIBLE);
|
||||
assertTrue(list.getAdapter().getItemCount() > 0);
|
||||
}
|
||||
|
||||
private static void layoutRecyclerView(RecyclerView recyclerView) {
|
||||
int widthSpec = MeasureSpec.makeMeasureSpec(1080, MeasureSpec.EXACTLY);
|
||||
int heightSpec = MeasureSpec.makeMeasureSpec(2400, MeasureSpec.EXACTLY);
|
||||
recyclerView.measure(widthSpec, heightSpec);
|
||||
recyclerView.layout(0, 0, 1080, 2400);
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,12 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.junit.Test;
|
||||
@@ -45,18 +48,37 @@ public class MainActivityPinnedConversationsTest {
|
||||
ReflectionHelpers.callInstanceMethod(activity, "renderConversationsRoot");
|
||||
Shadows.shadowOf(activity.getMainLooper()).idle();
|
||||
|
||||
LinearLayout content = ReflectionHelpers.getField(activity, "screenContent");
|
||||
assertTrue(viewTreeContainsText(content, "置顶会话"));
|
||||
assertTrue(viewTreeContainsText(content, "收起"));
|
||||
assertTrue(viewTreeContainsText(content, "主 Agent"));
|
||||
assertTrue(viewTreeContainsText(content, "Boss 移动控制台"));
|
||||
RecyclerView list = ReflectionHelpers.getField(activity, "screenList");
|
||||
assertTrue(recyclerContainsText(list, "置顶会话"));
|
||||
assertTrue(recyclerContainsText(list, "收起"));
|
||||
assertTrue(recyclerContainsText(list, "主 Agent"));
|
||||
assertTrue(recyclerContainsText(list, "Boss 移动控制台"));
|
||||
|
||||
View pinnedHeader = content.getChildAt(1);
|
||||
View pinnedHeader = getRecyclerChild(list, 1);
|
||||
pinnedHeader.performClick();
|
||||
|
||||
assertEquals(true, ReflectionHelpers.getField(activity, "pinnedConversationsCollapsed"));
|
||||
assertTrue(viewTreeContainsText(content, "展开"));
|
||||
assertTrue("收起后普通会话仍应保留", viewTreeContainsText(content, "Boss 移动控制台"));
|
||||
assertTrue(recyclerContainsText(list, "展开"));
|
||||
assertTrue("收起后普通会话仍应保留", recyclerContainsText(list, "Boss 移动控制台"));
|
||||
}
|
||||
|
||||
private static View getRecyclerChild(RecyclerView recyclerView, int position) {
|
||||
RecyclerView.Adapter adapter = recyclerView.getAdapter();
|
||||
int viewType = adapter.getItemViewType(position);
|
||||
RecyclerView.ViewHolder holder = adapter.createViewHolder(recyclerView, viewType);
|
||||
adapter.bindViewHolder(holder, position);
|
||||
FrameLayout container = (FrameLayout) holder.itemView;
|
||||
return container.getChildAt(0);
|
||||
}
|
||||
|
||||
private static boolean recyclerContainsText(RecyclerView recyclerView, String expectedText) {
|
||||
RecyclerView.Adapter adapter = recyclerView.getAdapter();
|
||||
for (int index = 0; index < adapter.getItemCount(); index += 1) {
|
||||
if (viewTreeContainsText(getRecyclerChild(recyclerView, index), expectedText)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean viewTreeContainsText(View root, String expectedText) {
|
||||
|
||||
Reference in New Issue
Block a user