简介:这份资源面向具备一定 Android 基础、希望从零搭建仿微信界面的开发者与学习者,围绕 Android Studio 开发环境,系统演示一个微信风格 App 的界面实现路径。压缩包共 1041 个文件,约 24.25MB,以 flat、json、xml 布局与资源文件、png 图片素材、dex 与 class 编译产物、java 源码及 gradle 构建脚本为主,完整保留了工程结构与依赖配置,便于直接导入运行和逐层拆解。内容覆盖底部导航栏、聊天列表、个人信息页等核心模块,涉及 ConstraintLayout 布局、BottomNavigationView 与 RecyclerView 等 Material Design 组件、自定义聊天气泡视图、适配器数据绑定、Glide 图片加载、VectorDrawable 图标、主题配色以及页面切换动画等知识点,并附带版本控制与更新记录痕迹。目前已有 3104 人学习,适合作为界面仿写练习与组件用法查阅的实战样本。
1. 需求先立住:为什么用 Android Studio 复刻微信界面值得认真做一遍
很多人拿到「用 Android Studio 制作微信的界面」这个题目,第一反应是照着截图堆布局,觉得把四个 Tab 摆出来就完事了。真动手才发现,微信的界面难点根本不在单个页面好不好看,而在于底部导航切换时状态不能丢、消息列表滚动要顺、聊天页输入法弹起时布局不能乱跳。这三件事任何一件翻车,答辩现场演示就会当场露馅。
这套方案要解决的就是「一个可运行、可讲解、可验收的微信主界面复刻工程」:底部四 Tab 导航、消息列表、联系人列表、发现页、我的页,加上一个能发消息的聊天详情页。适合正在做 Android 课程设计、移动开发毕设的学生,也适合想拿一个结构干净的 Android 项目练手的从业者。下面按需求、架构、模块实现、避坑、验收的顺序讲透,代码和配置都能直接抄。
2. 总体架构设计:单 Activity 加多 Fragment 的骨架怎么搭
2.1 为什么选单 Activity 多 Fragment 而不是多 Activity
微信主界面本质是四个平级入口来回切,用多 Activity 实现的话,每次切换都要走一遍生命周期重建,消息列表的滚动位置、未读红点状态全得自己存,纯属给自己找麻烦。单 Activity 承载一个 FragmentContainerView,四个 Fragment 用 show/hide 切换,状态天然保留,这是这个项目最省心的骨架。
工程目录我一般这样分:
app/src/main/java/com/example/wechatui/ ├── MainActivity.java # 唯一宿主 Activity,管底部导航 ├── fragment/ │ ├── ChatListFragment.java # 消息列表 │ ├── ContactFragment.java # 通讯录 │ ├── DiscoverFragment.java # 发现 │ └── MineFragment.java # 我的 ├── chat/ │ └── ChatActivity.java # 聊天详情页 ├── adapter/ │ ├── ChatListAdapter.java │ └── MessageAdapter.java └── model/ ├── ChatItem.java └── Message.java这个分层的好处是每个 Fragment 只关心自己的列表和点击事件,MainActivity 只负责导航切换,职责边界清楚,答辩时被问「为什么这么分」也有话讲。
2.2 底部导航的布局与切换逻辑
底部导航用 BottomNavigationView 配 menu 资源,四个 item 的图标和文字对齐微信。关键在切换时用 FragmentTransaction 的 show/hide 而不是 replace,避免 Fragment 反复重建。
<!-- activity_main.xml 核心结构 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <androidx.fragment.app.FragmentContainerView android:id="@+id/fragment_container" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_nav" android:layout_width="match_parent" android:layout_height="wrap_content" app:menu="@menu/bottom_nav_menu" /> </LinearLayout>// MainActivity.java 切换逻辑 private Fragment currentFragment; private void switchFragment(Fragment target) { if (target == currentFragment) return; FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if (currentFragment != null) ft.hide(currentFragment); // 隐藏而非销毁 if (!target.isAdded()) { ft.add(R.id.fragment_container, target); } else { ft.show(target); // 已添加则直接显示,状态保留 } currentFragment = target; ft.commit(); }这里hide和show是核心,replace会把旧 Fragment 的视图销毁,列表滚动位置和输入框内容全丢。isAdded()判断保证 Fragment 只 add 一次,后续都走 show。BottomNavigationView 的setOnItemSelectedListener里根据 itemId 映射到对应 Fragment 即可。
2.3 主题与沉浸式状态栏的配置
微信顶部是深色状态栏配浅色内容区,这个效果靠主题配置加状态栏颜色设置。在themes.xml里用 NoActionBar 主题,代码里设置状态栏颜色:
<style name="Theme.WeChatUI" parent="Theme.MaterialComponents.Light.NoActionBar"> <item name="colorPrimary">#07C160</item> <item name="android:statusBarColor">#EDEDED</item> <item name="android:windowLightStatusBar">true</item> </style>windowLightStatusBar设为 true 让状态栏图标变深色,配浅灰背景才看得清。这个细节不做的话,状态栏图标和背景撞色,一眼就看出是半成品。
3. 核心页面实现:消息列表、聊天页与通讯录的落地细节
3.1 消息列表:RecyclerView 加 ViewHolder 的标准写法
消息列表是微信的门面,每行有头像、昵称、最后一条消息、时间、未读红点。用 RecyclerView 配自定义 Adapter,item 布局用 ConstraintLayout 摆位。
// ChatListAdapter.java 核心部分 public class ChatListAdapter extends RecyclerView.Adapter<ChatListAdapter.VH> { private final List<ChatItem> data; private final OnItemClickListener listener; public interface OnItemClickListener { void onClick(ChatItem item); } public ChatListAdapter(List<ChatItem> data, OnItemClickListener listener) { this.data = data; this.listener = listener; } @NonNull @Override public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_chat_list, parent, false); return new VH(v); } @Override public void onBindViewHolder(@NonNull VH holder, int position) { ChatItem item = data.get(position); holder.name.setText(item.getName()); holder.lastMsg.setText(item.getLastMessage()); holder.time.setText(item.getTime()); // 未读红点:大于 0 才显示,超过 99 显示 99+ if (item.getUnread() > 0) { holder.badge.setVisibility(View.VISIBLE); holder.badge.setText(item.getUnread() > 99 ? "99+" : String.valueOf(item.getUnread())); } else { holder.badge.setVisibility(View.GONE); } holder.itemView.setOnClickListener(v -> listener.onClick(item)); } @Override public int getItemCount() { return data.size(); } static class VH extends RecyclerView.ViewHolder { TextView name, lastMsg, time, badge; VH(View v) { super(v); name = v.findViewById(R.id.tv_name); lastMsg = v.findViewById(R.id.tv_last_msg); time = v.findViewById(R.id.tv_time); badge = v.findViewById(R.id.tv_badge); } } }onBindViewHolder里红点的显隐判断是重点,未读为 0 时必须GONE,否则会出现空红点。99+的截断逻辑微信就是这么处理的,加上去答辩时是个加分细节。点击事件通过接口回调给 Fragment,Fragment 里启动 ChatActivity 并传对方昵称。
3.2 聊天详情页:消息气泡与输入法适配
聊天页的难点是消息气泡左右对齐和输入法弹起时列表自动滚到底部。气泡用 9-patch 背景图或者 shape drawable 做圆角,左右靠 gravity 区分。
<!-- item_message.xml 消息气泡 --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:padding="8dp"> <ImageView android:id="@+id/iv_avatar" android:layout_width="40dp" android:layout_height="40dp" android:src="@drawable/ic_avatar_default" /> <TextView android:id="@+id/tv_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:background="@drawable/bg_bubble_left" android:maxWidth="240dp" android:padding="10dp" android:textColor="#000000" /> </LinearLayout>左右气泡的区分在 Adapter 里根据消息类型设置 gravity 和背景:自己发的消息把头像和气泡顺序调换、背景换成绿色bg_bubble_right。输入法适配在 AndroidManifest 里给 ChatActivity 配windowSoftInputMode="adjustResize",再监听布局变化滚到底:
// ChatActivity.java 输入法弹起时滚到底部 private void scrollToBottom() { if (messageList.size() > 0) { recyclerView.scrollToPosition(messageList.size() - 1); } } // 监听根布局高度变化,输入法弹起时触发 rootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> { int heightDiff = rootView.getRootView().getHeight() - rootView.getHeight(); if (heightDiff > 200) { // 经验值,超过 200px 视为输入法弹起 scrollToBottom(); } });adjustResize让内容区被压缩而不是被输入法盖住,heightDiff判断是常见的土办法,阈值 200 是踩过坑调出来的,太小会误判、太大反应迟钝。
3.3 通讯录与发现页:分组列表和宫格布局
通讯录用带字母索引的分组列表,简单做法是 RecyclerView 加两种 viewType:分组标题和联系人条目。发现页是宫格,用 GridLayout 或者 RecyclerView 配 GridLayoutManager 都行,我一般用后者,方便统一处理点击。
// ContactFragment.java 分组数据构建 private void buildGroupedData() { Map<String, List<Contact>> grouped = new TreeMap<>(); // TreeMap 自动按字母排序 for (Contact c : allContacts) { String letter = c.getPinyin().substring(0, 1).toUpperCase(); grouped.computeIfAbsent(letter, k -> new ArrayList<>()).add(c); } // 展开成扁平列表:标题 + 条目 for (Map.Entry<String, List<Contact>> entry : grouped.entrySet()) { items.add(new ContactItem(true, entry.getKey(), null)); for (Contact c : entry.getValue()) { items.add(new ContactItem(false, null, c)); } } adapter.notifyDataSetChanged(); }TreeMap保证字母顺序,computeIfAbsent省掉判空。拼音首字母这块如果不想引第三方库,可以给测试数据直接写好拼音字段,答辩演示够用,别在这上面耗太久。
4. 避坑与排查:这个界面项目最容易翻车的五个点
4.1 底部导航切换后列表滚动位置丢失
现象:从消息列表切到通讯录再切回来,列表回到顶部。原因:用了replace或者每次切换都new一个 Fragment。解决:统一用show/hide,Fragment 实例在 MainActivity 里只创建一次并缓存,切换时复用。
4.2 聊天页输入法弹起后布局被顶飞
现象:点输入框,整个页面往上窜,顶部标题栏跑出屏幕。原因:Activity 的windowSoftInputMode没配或者配成了adjustPan。解决:Manifest 里改成adjustResize,同时根布局用LinearLayout的layout_weight让列表区自适应压缩,别用固定高度。
4.3 RecyclerView 的 item 点击无响应
现象:列表能显示,点了没反应。原因:item 根布局里放了可点击的 Button 或 ImageView 抢了焦点,或者 item 布局高度写成wrap_content但内部有match_parent冲突。解决:给 item 根布局设android:clickable="true"和android:focusable="true",内部控件设focusable="false",点击事件统一在 Adapter 的itemView.setOnClickListener里处理。
4.4 头像圆角处理导致列表卡顿
现象:消息列表滑动掉帧。原因:在onBindViewHolder里每次都重新裁剪 Bitmap 做圆角。解决:用 Glide 的circleCrop()或者提前把圆角头像做成 drawable 资源,绑定阶段只做 setImageResource,别在主线程做图像运算。
4.5 深色模式下一片惨白或一片漆黑
现象:系统切深色模式,界面文字看不见。原因:颜色全写死成#000000,没走主题属性。解决:文字颜色用?attr/colorOnBackground这类主题属性,或者在values-night下单独配一套颜色。课程设计不要求深色模式的话,至少在themes.xml里把forceDarkAllowed设为 false,锁死浅色,避免演示时被系统设置坑到。
5. 验收与答辩:怎么证明这套界面真的能跑、能讲、能改
验收分三层,从「能跑」到「能讲」再到「能改」,逐层过一遍基本不会翻车。
第一层功能验收,按清单逐项点:
| 验收项 | 操作 | 通过标准 |
|---|---|---|
| 底部导航 | 依次点四个 Tab | 页面正确切换,无闪退 |
| 状态保留 | 消息列表滑到中间,切走再切回 | 滚动位置不变 |
| 聊天跳转 | 点消息列表任一条 | 进入聊天页,标题显示对方昵称 |
| 发消息 | 输入文字点发送 | 气泡出现在右侧,列表滚到底 |
| 输入法适配 | 点输入框 | 标题栏不动,列表区压缩 |
| 未读红点 | 查看有未读的条目 | 红点显示,99+ 截断正确 |
第二层代码讲解,答辩老师最爱问的三个问题提前准备好答案:为什么用 Fragment 不用 Activity(状态保留、切换轻量);RecyclerView 比 ListView 好在哪(ViewHolder 复用、局部刷新);adjustResize和adjustPan的区别(压缩内容区 vs 整体上移)。这三个答顺了,基本盘就稳了。
第三层可扩展性,留一两个口子证明这工程不是死代码。比如消息数据从静态 List 换成 Room 数据库、聊天页接入本地存储,答辩时说一句「当前数据层是内存 List,接口已经抽出来了,换数据源只改一个类」,比空谈架构有说服力。
我自己的血泪经验是:答辩前一天晚上一定要在真机上完整走一遍,模拟器上adjustResize的表现和真机经常不一致,我见过不止一个项目在模拟器上好好的,换真机输入法一弹布局就乱。从那以后我每次交项目前都强制在真机上把验收清单过一遍,模拟器只用来写代码。希望这套结构和清单能帮到你,照着搭一遍,界面能跑、代码能讲、答辩能过。
本文还有配套的精品资源,点击获取