首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 程式設計疑難解答

醫院掛號預約項目:科室列表雙向排列界面實現方案

     編輯:程式設計疑難解答

需求分析

  • 左側/頂部:一級科室垂直列表(內科、外科、婦產科...)。
  • 右側/底部:點擊一級科室,顯示其二級科室橫向/網格排列(心血管內科、呼吸內科...)。
  • 點擊二級科室跳轉醫生列表/掛號頁。

方案對比

方案技術棧優點缺點適用場景
原生 ListView + AdapterAndroid SDK輕量、無依賴需手寫雙層適配器、項目類型判斷、性能一般簡單項目、舊版本維護
RecyclerView + 多類型 ItemAndroidX性能優、靈活、支持動畫、標準化需實現多 ViewHolder、SpanSizeLookup現代 App 首選
ExpandableListViewAndroid SDK自帶分組展開/收起動畫定制困難、不支持橫向二級布局、已棄用趨勢簡單樹形結構
雙 RecyclerView (主從聯動)AndroidX邏輯清晰、獨立滾動、易於複雜交互佔用更多內存、需同步選中狀態大量科室、複雜篩選
FlowLayout / FlexboxLayout第三方庫完美支持標簽流式/網格排列引入依賴、需配合 RecyclerView二級科室標簽雲展示

推薦方案:RecyclerView 多 ItemType + GridLayoutManager

數據模型

// 一級科室
public class Department {
    public int id;
    public String name;
    public List<SubDepartment> subList;
    public boolean expanded;  // 展開狀態
}

// 二級科室
public class SubDepartment {
    public int id;
    public String name;
    public int parentId;
}

佈局文件

<!-- item_department_parent.xml (一級) -->
<LinearLayout xmlns:android="..."
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp">
    <TextView android:id="@+id/tvName" ... />
    <ImageView android:id="@+id/ivArrow" ... />
</LinearLayout>

<!-- item_sub_department.xml (二級) -->
<TextView xmlns:android="..."
    android:id="@+id/tvSubName"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="8dp 16dp"
    android:background="@drawable/bg_sub_dept"
    android:layout_margin="4dp"
    android:textSize="14sp" />

Adapter 實現核心

public class DeptAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private static final int TYPE_PARENT = 0;
    private static final int TYPE_CHILD = 1;
    
    private List<Department> data;
    private OnSubDeptClickListener listener;
    
    @Override
    public int getItemViewType(int position) {
        // 扁平化數據:父節點 + 其展開的子節點
        return getItem(position) instanceof Department ? TYPE_PARENT : TYPE_CHILD;
    }
    
    // 需將樹形結構扁平化為 List<Object> flatList
    // 父節點插入後,若 expanded 則緊接插入其子節點列表
    
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (viewType == TYPE_PARENT) {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_department_parent, parent, false);
            return new ParentVH(v);
        } else {
            View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_sub_department, parent, false);
            return new ChildVH(v);
        }
    }
    
    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        Object item = flatList.get(position);
        if (holder instanceof ParentVH) {
            ((ParentVH)holder).bind((Department)item);
        } else {
            ((ChildVH)holder).bind((SubDepartment)item);
        }
    }
    
    // 父 ViewHolder
    class ParentVH extends RecyclerView.ViewHolder {
        TextView tvName; ImageView ivArrow;
        ParentVH(View v) { super(v); tvName=v.findViewById(R.id.tvName); ivArrow=v.findViewById(R.id.ivArrow); }
        void bind(Department dept) {
            tvName.setText(dept.name);
            ivArrow.setRotation(dept.expanded ? 180 : 0);
            itemView.setOnClickListener(v -> toggleExpand(dept));
        }
    }
    
    // 子 ViewHolder
    class ChildVH extends RecyclerView.ViewHolder {
        TextView tvName;
        ChildVH(View v) { super(v); tvName=v.findViewById(R.id.tvSubName); }
        void bind(SubDepartment sub) {
            tvName.setText(sub.name);
            itemView.setOnClickListener(v -> listener.onSubDeptClick(sub));
        }
    }
    
    void toggleExpand(Department dept) {
        dept.expanded = !dept.expanded;
        rebuildFlatList();  // 重建扁平列表
        notifyDataSetChanged(); // 或用 DiffUtil 精細更新
    }
}

GridLayoutManager 實現橫向網格

RecyclerView rv = findViewById(R.id.rvDepartments);
GridLayoutManager glm = new GridLayoutManager(this, 4); // 4 列
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
    @Override
    public int getSpanSize(int position) {
        return adapter.getItemViewType(position) == TYPE_PARENT ? 4 : 1; // 父佔滿行,子佔 1 格
    }
});
rv.setLayoutManager(glm);
rv.setAdapter(adapter);

替代方案:雙 RecyclerView (左側一級 + 右側二級)

<LinearLayout android:orientation="horizontal" ...>
    <RecyclerView android:id="@+id/rvParent" android:layout_width="120dp" android:layout_height="match_parent" />
    <RecyclerView android:id="@+id/rvChild" android:layout_width="0dp" android:layout_weight="1" android:layout_height="match_parent" />
</LinearLayout>

// 邏輯
rvParent.setAdapter(parentAdapter);
parentAdapter.setOnItemClickListener(dept -> {
    childAdapter.setData(dept.subList);  // 更新右側數據
    childAdapter.notifyDataSetChanged();
});
  • 優勢:左右獨立滾動、一級科室始終可見、交互直觀(類似美團/大眾點評分類頁)。
  • 實現:左側 LinearLayoutManager,右側 GridLayoutManager/StaggeredGridLayoutManager。

參考項目

總結

  • 單列表樹形展開:用 RecyclerView 多 ItemType + GridLayoutManager SpanSizeLookup,一個列表搞定,代碼緊湊。
  • 左右分欄聯動:用 雙 RecyclerView,體驗更佳,適合科室較多場景。
  • 二級科室標簽流:引入 FlexboxLayout 或 FlowLayout 庫,自動換行排版。

核心在於樹形數據扁平化與多類型 ViewHolder 綁定,掌握此模式可應對所有分組列表場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved