
简介这份资源面向具备一定 Android 基础、希望借助 Android Studio 实践界面开发的开发者通过复刻微信主界面来串联布局设计、组件使用与视觉还原等技能点。内容围绕底部导航栏、聊天列表、个人信息页等典型模块展开涉及 ConstraintLayout 布局管理、BottomNavigationView 与 RecyclerView 等 Material Design 组件的应用并延伸到自定义聊天气泡视图、适配器数据绑定、Glide 图片加载、VectorDrawable 图标、主题配色调整以及页面切换动画等细节帮助读者理解一个仿微信界面从结构搭建到视觉打磨的完整过程。压缩包共 1041 个文件约 24.25MB以 flat、json、xml 布局与资源文件、png 图片、dex 与 class 编译产物、java 源码及 gradle 构建脚本为主另含 apk 成品与 Git 版本控制相关文件目录结构贴近真实工程。目前已有 3104 人学习适合作为界面练习与项目参考。1. 从一张“看起来像微信”的截图说起很多同学接到“用 Android Studio 制作微信的界面”这个题目时第一反应是打开手机截几张图然后照着截图往布局文件里堆控件。做出来的东西乍一看挺像一跑起来就露馅底部导航点不动、消息列表滑两下就错位、聊天页输入框被键盘顶飞。问题不在控件用得少而在于一开始就没把“界面”当成一个可拆解、可复用的工程结构来对待。这套方案要解决的就是这件事把微信主界面拆成可独立开发、可单独验收的模块用 Android Studio 的原生控件和标准工程结构落地让一个课程设计级别的项目既有“像”的观感也有“能用”的交互。适合正在做 Android 课设、毕设界面部分或者需要交一份完整 UI 方案的同学参考。下面从需求、架构、模块实现一路讲到怎么验收中间该贴的布局和代码都会给到。2. 需求与工程结构先定四个页面和一套目录动手写 XML 之前先把“要做几个页面、每个页面管什么”定死。微信主界面看着复杂拆开其实就四块底部导航承载的四个主 Tab微信、通讯录、发现、我加上从微信 Tab 点进去的聊天详情页。这个划分决定了后面所有代码的组织方式定错了后面全是返工。2.1 功能边界四个主 Tab 加一个详情页主界面用Fragment承载四个 TabMainActivity只负责底部导航切换和 Fragment 的显示隐藏。聊天详情页单独一个ChatActivity从消息列表点击条目跳转。这样切分的好处是每个 Fragment 的布局和逻辑互不干扰验收时可以一个一个过。页面承载内容关键交互微信 Tab消息列表头像、昵称、摘要、时间点击条目跳转聊天页通讯录 Tab字母索引的联系人列表侧边字母条快速定位发现 Tab朋友圈、扫一扫等入口的九宫格条目点击反馈我 Tab头像、昵称、设置项列表列表项点击聊天详情页消息气泡列表、底部输入栏发送消息、返回这张表就是需求基线。后面每写一个布局都要能对回表里某一行对不上的功能要么砍掉要么补进表里再动手。2.2 工程目录按功能分包而不是按类型分包新手常犯的错是把所有 Activity 塞一个包、所有 Adapter 塞一个包。项目一大找一个页面的三件套要翻三个目录。我一般按功能分包每个 Tab 一个包包内放自己的 Fragment、Adapter 和布局引用com.example.wechatui ├── MainActivity.java # 只做导航切换 ├── chat/ # 微信 Tab │ ├── ChatListFragment.java │ ├── ChatListAdapter.java │ └── ChatActivity.java # 聊天详情页 ├── contact/ # 通讯录 Tab │ └── ContactFragment.java ├── discover/ # 发现 Tab │ └── DiscoverFragment.java ├── mine/ # 我 Tab │ └── MineFragment.java ├── model/ # 数据实体 │ └── ChatMessage.java └── widget/ # 自定义控件 └── AvatarView.java这样分包后改微信 Tab 的列表样式只动chat包不会误伤别的页面。model包放纯数据类widget包放复用控件边界清晰。布局文件仍按layout目录平铺但命名统一加前缀fragment_chat_list.xml、item_chat_message.xml靠命名而不是目录来区分归属。2.3 依赖与主题少引第三方先把原生控件吃透这个项目的定位是界面复刻不是造轮子依赖越少越好。build.gradle里除了 AndroidX 基础库和RecyclerView、ConstraintLayout不建议再引图片加载或 UI 框架——头像用本地 drawable 占位即可列表用RecyclerView原生实现。主题上把colorPrimary设成微信绿约#07C160状态栏和导航栏颜色统一观感立刻接近。dependencies { implementation androidx.appcompat:appcompat:1.6.1 implementation androidx.constraintlayout:constraintlayout:2.1.4 implementation androidx.recyclerview:recyclerview:1.3.2 implementation com.google.android.material:material:1.9.0 }版本号按本机 SDK 实际情况调整关键是别为了“高级感”引入一堆用不上的库。依赖少编译快答辩时被问“这个库干嘛的”也不会卡壳。主题色、字体大小这些视觉参数集中放在colors.xml和dimens.xml后面调样式只改一处。3. 主框架实现底部导航与 Fragment 切换主框架是整个界面的骨架骨架歪了里面填得再漂亮也站不住。这一章解决三件事底部导航怎么搭、Fragment 怎么切、切换时状态怎么保。很多项目在这里翻车表现是点 Tab 没反应、或者切回来列表回到顶部。3.1 底部导航用 BottomNavigationView 而不是手写 LinearLayout手写四个LinearLayout加ImageView也能做但选中态、文字颜色切换、水波纹反馈全要自己写费力还容易漏。BottomNavigationView自带这些配合菜单资源文件声明四个条目即可。!-- res/menu/menu_bottom_nav.xml -- menu xmlns:androidhttp://schemas.android.com/apk/res/android item android:idid/nav_chat android:icondrawable/ic_chat android:title微信 / item android:idid/nav_contact android:icondrawable/ic_contact android:title通讯录 / item android:idid/nav_discover android:icondrawable/ic_discover android:title发现 / item android:idid/nav_mine android:icondrawable/ic_mine android:title我 / /menu菜单里android:title就是底部文字android:icon是图标。选中态颜色通过app:itemIconTint和app:itemTextColor指向一个selector颜色资源未选中灰、选中绿。这样不用写一行 Java 代码选中反馈就有了。图标建议用矢量图VectorDrawable不同分辨率下不糊。3.2 Fragment 切换show/hide 而不是 replaceFragmentTransaction有两种切法replace和show/hide。replace每次销毁重建 Fragment列表滚动位置、输入内容全丢切回来像重新进页面。show/hide保留实例只控制可见性体验接近原生微信。private void switchFragment(Fragment target) { FragmentTransaction tx getSupportFragmentManager().beginTransaction(); // 先隐藏当前显示的 Fragment再显示目标 Fragment if (currentFragment ! null currentFragment ! target) { tx.hide(currentFragment); } if (!target.isAdded()) { tx.add(R.id.fragment_container, target); } else { tx.show(target); } tx.commit(); currentFragment target; }R.id.fragment_container是主布局里FrameLayout的 id四个 Fragment 都往这个容器里加。第一次切换时isAdded()为 false走add之后再切回来走show实例一直在内存里。代价是四个 Fragment 同时驻留内存占用略高但界面复刻项目页面轻这点开销可以接受。如果页面很重再考虑replace加状态保存那是另一个话题。3.3 状态保持配置变更和返回栈的处理横竖屏旋转、切后台再回来Activity 可能重建Fragment 状态跟着丢。常规做法是在MainActivity里给BottomNavigationView设置选中监听同时在onCreate里根据savedInstanceState恢复当前 TabOverride protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BottomNavigationView nav findViewById(R.id.bottom_nav); nav.setOnItemSelectedListener(item - { switchFragment(resolveFragment(item.getItemId())); return true; }); // 重建时恢复上次选中的 Tab避免回到第一个 if (savedInstanceState ! null) { int lastId savedInstanceState.getInt(nav_id, R.id.nav_chat); nav.setSelectedItemId(lastId); } else { nav.setSelectedItemId(R.id.nav_chat); } }setSelectedItemId会触发监听所以恢复逻辑不用再手动调switchFragment。这里有个细节setOnItemSelectedListener要在setSelectedItemId之前设置否则第一次选中不触发。这个顺序问题很隐蔽表现是启动后第一个 Tab 内容空白排查时容易怀疑布局其实是监听注册晚了。4. 核心页面实现消息列表与聊天详情主框架搭好后工作量集中在两个页面微信 Tab 的消息列表和聊天详情页。前者考验RecyclerView的用法后者考验键盘与输入框的配合。这两块做扎实整个项目的完成度就上来了。4.1 消息列表RecyclerView 加 ViewHolder 复用消息列表用RecyclerView不要用ListView。ListView的复用机制老旧嵌套滚动也麻烦。列表项布局用ConstraintLayout头像、昵称、摘要、时间四个元素定位清楚!-- res/layout/item_chat_list.xml -- androidx.constraintlayout.widget.ConstraintLayout xmlns:androidhttp://schemas.android.com/apk/res/android xmlns:apphttp://schemas.android.com/apk/res-auto android:layout_widthmatch_parent android:layout_height72dp android:paddingHorizontal16dp ImageView android:idid/iv_avatar android:layout_width48dp android:layout_height48dp android:srcdrawable/ic_avatar_default app:layout_constraintStart_toStartOfparent app:layout_constraintTop_toTopOfparent app:layout_constraintBottom_toBottomOfparent / TextView android:idid/tv_name android:layout_width0dp android:layout_heightwrap_content android:textSize16sp android:textColor#191919 android:layout_marginStart12dp app:layout_constraintStart_toEndOfid/iv_avatar app:layout_constraintEnd_toStartOfid/tv_time app:layout_constraintTop_toTopOfid/iv_avatar / TextView android:idid/tv_time android:layout_widthwrap_content android:layout_heightwrap_content android:textSize12sp android:textColor#B2B2B2 app:layout_constraintEnd_toEndOfparent app:layout_constraintTop_toTopOfid/iv_avatar / TextView android:idid/tv_preview android:layout_width0dp android:layout_heightwrap_content android:textSize14sp android:textColor#999999 android:maxLines1 android:ellipsizeend android:layout_marginTop4dp app:layout_constraintStart_toStartOfid/tv_name app:layout_constraintEnd_toEndOfparent app:layout_constraintTop_toBottomOfid/tv_name / /androidx.constraintlayout.widget.ConstraintLayouttv_name和tv_preview的宽度设成0dp加左右约束让它们自动撑满剩余空间摘要超长时ellipsizeend显示省略号。头像固定 48dp整行高度 72dp这是微信列表的常见比例照着调视觉上就对了。Adapter 里onBindViewHolder只做数据填充不在这里做耗时操作Override public void onBindViewHolder(NonNull ChatHolder holder, int position) { ChatMessage msg dataList.get(position); holder.name.setText(msg.getName()); holder.preview.setText(msg.getPreview()); holder.time.setText(msg.getTime()); // 点击跳转聊天详情把对方昵称带过去 holder.itemView.setOnClickListener(v - { Intent intent new Intent(v.getContext(), ChatActivity.class); intent.putExtra(peer_name, msg.getName()); v.getContext().startActivity(intent); }); }ChatHolder继承RecyclerView.ViewHolder在构造里findViewById一次后续复用不再查找。点击事件绑在itemView上而不是某个子控件整行都能点符合用户预期。数据源用ListChatMessageChatMessage是model包里的简单实体字段就昵称、摘要、时间、头像资源 id 四个。4.2 聊天详情页消息气泡与输入栏的布局聊天页分上下两部分上面是消息列表RecyclerView下面是输入栏EditText加发送按钮。难点在键盘弹出时输入栏要跟着上移消息列表要能滚到底。布局上用ConstraintLayout把输入栏约束在底部再在AndroidManifest.xml里给ChatActivity设windowSoftInputModeadjustResizeactivity android:name.chat.ChatActivity android:windowSoftInputModeadjustResize android:exportedfalse /adjustResize让窗口在键盘弹出时缩小输入栏被顶上去列表可视区域变小但不会遮挡。如果设成adjustPan整个窗口上推顶部标题栏会被推出屏幕体验很差。这个属性设错是聊天页最常见的翻车点现象就是键盘一弹标题栏没了。消息气泡分左右两种自己发的靠右绿底对方发的靠左白底。用一个item_msg_left.xml和item_msg_right.xml两套布局Adapter 里根据消息类型getItemViewType返回不同类型onCreateViewHolder按类型加载对应布局。气泡背景用shapedrawable 画圆角矩形比自己切图省事改颜色也方便。4.3 发送消息本地回显与列表滚动发送按钮点击后把输入框内容包成一条ChatMessage加进数据源调notifyItemInserted刷新然后清空输入框、滚动到底部btnSend.setOnClickListener(v - { String text etInput.getText().toString().trim(); if (text.isEmpty()) return; // 空消息不发送 ChatMessage msg new ChatMessage(text, ChatMessage.TYPE_SELF); adapter.addMessage(msg); etInput.setText(); // 滚动到最后一条让新消息可见 rvMessages.scrollToPosition(adapter.getItemCount() - 1); });addMessage内部调notifyItemInserted(dataList.size() - 1)只刷新新增那条比notifyDataSetChanged高效。scrollToPosition在数据更新后调用才有效顺序反了会滚到旧位置。这里不接真实网络消息只存在内存里退出页面就没了——课程设计到这个程度够用如果要求持久化再补一个Room或SharedPreferences那是加分项不是必选项。5. 避坑与排查五个最容易翻车的地方界面项目看着简单实际做起来坑不少。下面五条是这类项目里出现频率最高的每条按现象、原因、解决写照着排查能省不少时间。5.1 底部导航点了没反应现象启动后点其他 Tab内容不切换或者只有第一次能切。原因多半是setOnItemSelectedListener返回了false事件没被消费或者switchFragment里commit之后又重复add了同一个 Fragment。解决监听里return true切换前判断target.isAdded()已添加的走show不走add。另外确认fragment_container的 id 在布局里真实存在拼错 id 时add会静默失败。5.2 列表滑动卡顿、图片错位现象快速滑动消息列表头像串行或者明显掉帧。原因是在onBindViewHolder里做了图片解码或网络请求复用时旧任务没取消。解决本项目头像用本地资源直接在 XML 里设srconBind里不碰图片加载如果后续换成网络图必须用带生命周期感知的加载库并在onViewRecycled里取消请求。列表项布局层级也要控制ConstraintLayout扁平化别套三层LinearLayout。5.3 键盘弹出遮挡输入框现象点输入框键盘盖住发送按钮要手动滑才能看到。原因windowSoftInputMode没设或设成了adjustPan。解决在AndroidManifest.xml对应 Activity 上设adjustResize同时确保根布局不是固定高度输入栏用约束贴底。如果用了全屏沉浸式状态栏还要处理fitsSystemWindows否则adjustResize可能失效。5.4 旋转屏幕后页面重置现象横竖屏切换当前 Tab 回到第一个列表滚动位置丢失。原因Activity 重建Fragment 重新走onCreate没恢复状态。解决onSaveInstanceState里存当前 Tab idonCreate里读出来setSelectedItemId列表滚动位置可以用LinearLayoutManager.onSaveInstanceState存取。更省事的做法是在AndroidManifest.xml给 Activity 加android:configChangesorientation|screenSize阻止重建但这是权宜之计正规做法还是状态保存。5.5 深色模式下文字看不见现象系统切深色模式白底布局变黑但文字颜色写死成#191919几乎看不清。原因颜色硬编码在布局里没走主题资源。解决把文字色、背景色抽到colors.xml建values-night/colors.xml给深色模式一套值布局里引用color/text_primary而不是写死色值。这个项目如果只要求浅色模式至少也要保证颜色集中管理后面加深色适配只改资源文件。6. 验收与答辩怎么证明这套界面“能用”界面项目的验收最怕的是“看着像但说不清”。答辩时老师不会只盯着截图看通常会让你现场操作点 Tab、滑列表、进聊天页、发一条消息、转个屏。所以验收准备要围绕“可演示的交互路径”来而不是背功能列表。我一般会准备一张验收清单自己先走一遍每条都过再上台验收项操作合格标准导航切换依次点四个 Tab内容正确切换选中态高亮列表滚动快速上下滑消息列表不卡顿、头像不错位页面跳转点消息条目进入聊天页标题显示对方昵称消息发送输入文字点发送气泡出现在右侧输入框清空列表滚到底键盘适配点输入框输入栏上移不被遮挡旋转屏幕横竖屏切换当前 Tab 不丢布局不错乱返回聊天页按返回回到列表列表位置保持答辩陈述控制在三分钟先说页面拆成几个模块、为什么这么拆再演示一遍主流程最后讲一个你踩过的坑和怎么解决的。那个“坑”往往比功能列表更能体现你真的动手做过。比如讲adjustResize和adjustPan的区别或者show/hide和replace对状态的影响老师一听就知道不是抄的。代码层面提前把关键类打开放在编辑器里MainActivity的切换逻辑、ChatListAdapter的复用、ChatActivity的发送逻辑。被问到“列表为什么用 RecyclerView 不用 ListView”答复用机制和 ViewHolder 就行别展开成八股。被问到“数据从哪来”如实说本地模拟数据并说明如果要接后端会在哪一层加接口——这比硬编一个假接口诚实得多。从那以后我每次带界面类项目验收前都强制自己按清单走一遍完整路径尤其是键盘和旋转这两个容易被忽略的场景走一遍能提前暴露大半问题。希望这套结构能帮你把课设稳稳交上去。本文还有配套的精品资源点击获取