简介面向Android初学者的微信界面仿制项目基于Android Studio完整演示从创建工程到实现底部导航、聊天列表、个人信息页等核心模块。包内含完整源码、XML布局、资源文件与Gradle配置共1041个文件其中Java源码、XML布局、JSON配置及PNG图标为主要组成部分压缩包约24.25MB适合对照学习或二次开发。已有3103人学习下载。资源重点覆盖ConstraintLayout布局、Material Design组件、RecyclerView适配器、图片加载与动画过渡等关键技术并附有界面美化更新记录可帮助读者快速掌握Android应用的UI搭建思路与常见优化技巧。1. 用 Android Studio 复刻微信界面先搞清楚这份资源能让你走到哪一步如果你跟我一样照着网上的仿微信教程敲完一遍却发现状态栏是黑的、切页后列表直接回到顶部、点击消息行毫无反应——那问题根源往往不在某个控件而在整套布局骨架的拆法。这份用 Android Studio 制作微信界面的资源核心价值就是把微信首页从状态栏到底部 TabBar 拆成五层可执行的布局结构顺带把 Fragment 切换、侧滑抽屉和气泡绘制的实际参数给你标清楚。适合刚学完四大组件想动手练手的入门者也适合想补齐 Fragment 管理和自定义 View 细节的进阶开发者。2. 把微信首页拆成布局树状态栏、标题栏、TabBar 的尺寸与配色参数2.1 微信界面拆解从状态栏到 TabBar 的五层结构仿微信界面第一步不是写代码而是先在纸上把页面拆成五层。很多人翻车是因为一上来就塞一个 RecyclerView忽略了微信首页其实是“状态栏 标题栏 内容区 底部 TabBar 侧滑抽屉”五个独立层级叠出来的。层级组件方案关键参数状态栏系统 StatusBar白色背景Android 6.0 深色图标标题栏自定义 LinearLayout高度 44dp白底中央标题 17sp内容区FrameLayout高度 0dplayout_weight1底部 TabBar自定义 LinearLayout高度 49dp白底顶部 0.5dp 分割线侧滑抽屉DrawerLayout 子布局宽 240dp背景 #EDEDED这套拆法的一个关键决策标题栏和底部 TabBar 都不用系统组件而是手写 LinearLayout。原因是 Android 原生的 Toolbar 默认最小高度 56dp、自带左侧返回区和右侧菜单区BottomNavigationView 则带 98dp 固定高度和水波纹效果这两者都跟微信的视觉比例差得很远。手写布局虽然多几行代码但每一个像素都受控。2.2 根布局与状态栏DrawerLayout、fitsSystemWindows 和沉浸式的配合仿微信首页的根布局建议直接用 DrawerLayout 包一层内容区用垂直 LinearLayout 组织。下面这段 XML 是我拆这套资源时最常用的一套骨架androidx.drawerlayout.widget.DrawerLayout android:idid/drawer_layout android:layout_widthmatch_parent android:layout_heightmatch_parent !-- 主内容区标题栏 内容 TabBar 垂直排列 -- LinearLayout android:layout_widthmatch_parent android:layout_heightmatch_parent android:orientationvertical !-- 微信标题栏中央标题 右侧加号 -- LinearLayout android:idid/title_bar android:layout_widthmatch_parent android:layout_height44dp android:background#FFFFFF android:orientationhorizontal TextView android:layout_width0dp android:layout_heightmatch_parent android:layout_weight1 android:gravitycenter android:text微信 android:textColor#000000 android:textSize17sp / ImageView android:idid/iv_add android:layout_width40dp android:layout_heightmatch_parent android:padding10dp android:srcdrawable/ic_plus / /LinearLayout !-- 内容区四个 Tab 对应的 Fragment 都往这里放 -- FrameLayout android:idid/fragment_container android:layout_widthmatch_parent android:layout_height0dp android:layout_weight1 / !-- 底部 TabBar横向线性布局四个 Item -- LinearLayout android:idid/tab_bar android:layout_widthmatch_parent android:layout_height49dp android:background#FFFFFF android:orientationhorizontal / /LinearLayout !-- 侧滑抽屉宽度固定 240dp -- LinearLayout android:layout_width240dp android:layout_heightmatch_parent android:layout_gravitystart android:background#EDEDED / /androidx.drawerlayout.widget.DrawerLayout逻辑说明标题栏用 LinearLayout 而不是 Toolbar因为 Toolbar 的 title 默认靠左微信的标题是严格居中的右侧加号图标在 Toolbar 里要额外处理 action 菜单自定义布局里只需要一个 ImageView。FrameLayout 用layout_weight1把剩余空间全吃掉这样 TabBar 始终贴底不会被内容区顶下去。侧滑抽屉背景用了微信抽屉常见的浅灰色而不是纯白。状态栏处理是这套资源里比较容易踩坑的点。微信的状态栏是白色底、深色图标需要在 Kotlin 侧配合两行代码if (Build.VERSION.SDK_INT Build.VERSION_CODES.M) { window.statusBarColor Color.WHITE window.decorView.systemUiVisibility View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR }参数说明SYSTEM_UI_FLAG_LIGHT_STATUS_BAR是 Android 6.0 才有的 flag设置后状态栏图标会从白色变成深色。低版本手机没这个 flag所以Build.VERSION.SDK_INT M的判断必须写否则 5.x 模拟器上图标会看不见。如果你的项目用了 EdgeToEdge 或者更高版本的enableEdgeToEdge()这两个 API 会被替代但思路一样状态栏颜色归状态栏内容区不要向上顶。2.3 自定义标题栏与 TabBar尺寸、颜色和分割线的具体参数微信标题栏的字体大小和 TabBar 的选中色是这套资源里出现频率最高的两个参数。标题栏不是随手写的 16sp微信用的是 17sp颜色是纯黑#000000。TabBar 高度更讲究不同分辨率下微信的实测高度约为 49dp比很多仿写教程里的 56dp 要矮一截视觉上更紧凑。TabBar 每个 Item 的选中状态用 selector 实现这是 Android 里比较标准的做法。在res/color/tab_text_color.xml里定义selector xmlns:androidhttp://schemas.android.com/apk/res/android item android:color#07C160 android:state_selectedtrue / item android:color#999999 / /selector#07C160是微信的绿色用在这里做选中态未选中是#999999的灰。图标同样用一组 selector选中时用绿色图标未选中用灰色图标。两个 selector 组合起来才能做到“图标和文字同时变色”如果你只在代码里setTextColor图标颜色不会跟着变看起来就会很别扭。TabBar 顶部那条分割线别忘。微信的 TabBar 上方有一条很细的线颜色接近#E5E5E5高度 0.5dp。在 LinearLayout 前面加一个 View 即可View android:layout_widthmatch_parent android:layout_height0.5dp android:background#E5E5E5 /2.4 侧滑抽屉与手势冲突DrawerLayout 的两个隐蔽参数侧滑抽屉用 DrawerLayout 写起来简单但仿微信时有两个细节容易忽略。第一个是抽屉打开后状态栏颜色跟着变微信的抽屉背景是灰的#EDEDED拉出来时状态栏会自然过渡成灰色。第二个是抽屉的宽高比240dp 这个宽度是资源里反复验证过的太窄显得挤太宽会把内容区盖太多。3. Fragment 切换与侧滑抽屉show/hide 替换 replace 的三个理由3.1 为什么不用 BottomNavigationView微信 TabBar 的特殊风格这套资源里最值得学习的设计决定是四个主 Tab 不用 Material 组件 BottomNavigationView而是手写。理由有三个一是 BottomNavigationView 默认 98dp 高度微信只有 49dp用起来要重写的高度和 padding 太多二是自带水波纹和缩放动画微信的 Tab 切换是瞬时的没有这些反馈三是 BottomNavigationView 的 Item 通过菜单资源驱动图标文字间距不好调微信那种 11sp 小字 28dp 图标紧凑排列的效果很难还原。手写 TabBar 之后每个 Item 的点击逻辑集中在 Activity 里处理代码反而更直观Override public void onClick(View v) { int id v.getId(); if (id R.id.tab_message) { switchFragment(0); } else if (id R.id.tab_contacts) { switchFragment(1); } else if (id R.id.tab_discover) { switchFragment(2); } else if (id R.id.tab_me) { switchFragment(3); } for (int i 0; i tabItems.size(); i) { tabItems.get(i).setSelected(i position); } }逻辑说明setSelected(true)会触发前面定义的 selector让图标和文字同时切换颜色。这里一定要遍历所有 Item只对被点击的 Item 调setSelected(true)是不够的其他三个 Item 必须显式setSelected(false)重置否则会出现两个 Tab 同时高亮的 bug。这个坑在仿写项目里很常见因为 findViewById 拿到的引用容易让你下意识只操作当前项。3.2 用 show/hide 代替 replace列表滚动位置与 Fragment 重建新手最容易犯的错是每次切换 Tab 都用replace()。replace()会销毁旧 Fragment 的视图再创建新 Fragment结果就是消息列表每次切回来都重新加载、滚动位置丢失而且 Fragment 重建过程中容易出现空指针。微信的体验是切换 Tab 后列表保持原样所以这套资源的做法是add hide showprivate void switchFragment(int index) { FragmentTransaction ft getSupportFragmentManager().beginTransaction(); Fragment target fragments.get(index); if (!target.isAdded()) { ft.add(R.id.fragment_container, target, TAG_PREFIX index); } for (Fragment fragment : fragments) { if (fragment ! target) { ft.hide(fragment); } } ft.show(target); ft.commit(); }参数说明TAG_PREFIX index是给每个 Fragment 一个固定标签比如TAG_0、TAG_1。这是为了 Activity 因旋转或内存回收重建时系统能通过标签找到已存在的 Fragment避免重复创建。isAdded()判断是否第一次添加第一次才走add之后都走show。hide不会销毁视图只是把 Fragment 的 View 从窗口中移除所以 RecyclerView 的滚动位置、列表数据全部保留。我一般会在 Activity 的onCreate里配合findFragmentByTag做一次恢复Fragment target getSupportFragmentManager() .findFragmentByTag(TAG_PREFIX index); if (target null) { target createFragment(index); }3.3 侧滑菜单与状态栏渐变DrawerLayout 的监听回调前面提到抽屉拉出来时状态栏会变色这个效果通过addDrawerListener做渐变。用slideOffset可以拿到抽屉滑动的进度值范围从 0 到 1把它换算成状态栏颜色drawerLayout.addDrawerListener(new DrawerLayout.SimpleDrawerListener() { Override public void onDrawerSlide(View drawerView, float slideOffset) { Window window getWindow(); int color (int) (0xFFFFFFFF * (1 - slideOffset) 0xFFEDEDED * slideOffset); window.setStatusBarColor(color); } Override public void onDrawerOpened(View drawerView) { drawerView.requestFocus(); } });这段代码的坑在于slideOffset在手势拖动过程中回调频率很高颜色计算别用复杂的插值库直接按比例换算最稳。requestFocus()是为了防止抽屉里的控件抢焦点虽然仿微信的抽屉里没有输入框但如果你加了搜索框不处理焦点的话键盘会自动弹出来体验很怪。3.4 右上角“”菜单的锚点与偏移量微信标题栏右侧的“”按钮点击后会弹出一个四行菜单用 PopupWindow 实现时锚点要放在加号图标上而不是标题栏上PopupWindow popup new PopupWindow(menuView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); popup.setOutsideTouchable(true); popup.setFocusable(true); popup.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); popup.showAsDropDown(ivAdd, -dp2px(52), dp2px(8));参数说明showAsDropDown的三个参数是锚点 View、x 偏移、y 偏移。微信的菜单是右对齐到加号图标的所以第二个参数通常是负值把菜单向左拉让菜单右侧边缘和加号对齐。y 偏移 8dp 是菜单和加号图标之间的间距。setBackgroundDrawable必须写否则setOutsideTouchable(true)不生效点击菜单外部区域时 PopupWindow 不会消失。4. 常见问题与踩坑五个真机才会暴露的细节4.1 状态栏是深色的跟白色标题栏割裂现象竖屏启动后状态栏是深绿色的标题栏是白色的顶部看起来像是被砍了一截。原因Android 主题默认的colorPrimaryDark是深绿色仿微信界面通常只改了colorPrimary和windowBackground没改状态栏颜色所以状态栏沿用了主题里的默认值。解决在values/styles.xml里显式设置状态栏颜色再配合代码里那两行SYSTEM_UI_FLAG_LIGHT_STATUS_BARitem nameandroid:statusBarColor#FFFFFF/item改完后在 Android 6.0 以上的真机上验证一下图标颜色如果状态栏变白但图标还是白色的说明systemUiVisibility没执行或者被主题覆盖了需要在onCreate里decorView.post {}延后一帧设置。4.2 屏幕旋转后 Fragment 出现两份现象竖屏切横屏消息列表变成了两个叠在一起底部 Tab 点击乱跳。原因Activity 重建时系统会自动恢复 Fragment而onCreate里又new了一个新的 Fragment 实例导致同一个容器里有两个同职责的 Fragment。解决创建 Fragment 前先用findFragmentByTag查找找到就用旧的找不到才新建MessageFragment fragment (MessageFragment) getSupportFragmentManager() .findFragmentByTag(TAG_0); if (fragment null) { fragment new MessageFragment(); }这也是为什么switchFragment里要给每个 Fragment 打 tag 的原因。从那以后我写任何 Fragment 切换都会先写findFragmentByTag再写new已经成为固定习惯。4.3 RecyclerView 的 Item 点击无响应长按却有效现象消息列表的行点击没有高亮也没有回调但长按能触发 ContextMenu。原因DrawerLayout 的内容区布局里某个父容器设置了clickabletrue或者在onInterceptTouchEvent里拦截了ACTION_DOWN。RecyclerView 的点击事件依赖完整的按下-抬起序列DOWN 被拦截后整个点击链就断了。解决检查根布局内容区的 LinearLayout 和 FrameLayout 都不要设置 clickable 和 focusable。在 Item 布局上设置前景色android:foreground?attr/selectableItemBackground这样点击有水波纹反馈又不会影响触摸事件的正常分发。另外点击回调里拿 position 要注意别用getAdapterPosition()新版 RecyclerView 用holder.getBindingAdapterPosition()否则列表删除条目后点击位置会对不上。4.4 圆角头像四个角发黑现象Glide 加载圆角头像后四角出现黑色填充或者圆角边缘发毛。原因ImageView 的background默认是黑色圆角效果只裁了图片本身背景没有跟着一起裁。另外你如果直接对 Bitmap 做drawRoundRect后没有设置SRC_IN混合模式黑色背景会从透明区域透出来。解决统一用 BitmapShader 做圆角这样背景和图片是一起渲染的不会出现黑边。具体代码在第 5 章会完整展开当时解决这个问题的核心就一句抗锯齿必须开像素格式必须用ARGB_8888。4.5 真机底部虚拟导航栏把 TabBar 顶上去现象模拟器上布局正常真机上 TabBar 被底部白色导航栏盖住一半或者 TabBar 底部留出一块白。原因全面屏手势和虚拟导航栏会占据底部区域普通布局没有处理WindowInsetsTabBar 的 49dp 并不包含导航栏高度。解决给根布局设置OnApplyWindowInsetsListener把底部插入高度加到 TabBar 的 padding 里rootLayout.setOnApplyWindowInsetsListener((v, insets) - { int bottom insets.getSystemWindowInsetBottom(); tabBar.setPadding(0, 0, 0, bottom); return insets; });注意这个 listener 在用户上拉呼出导航栏时会反复回调底部 inset 会突变。稳妥的做法是只取第一次回调的值用一个布尔变量记录之后的回调直接忽略避免 TabBar 高度一直跳。5. 提升仿真度的一个技巧聊天气泡与圆角头像的绘制边界微信聊天页里最考验细节的两个地方是气泡和头像。气泡的圆角不是对称的自己发的绿色气泡左上角大、右上角小因为右上角要顶着头像边缘收消息的白色气泡则反过来。仿写常用资源里直接放几张固定背景图但一换文字长度就露馅。我在这套资源的后面几个练习里统一用 Shape 画气泡底再用 BitmapShader 画圆角头像。绿色气泡的 Shape 定义shape xmlns:androidhttp://schemas.android.com/apk/res/android android:shaperectangle solid android:color#95EC69 / corners android:bottomLeftRadius3dp android:bottomRightRadius3dp android:topLeftRadius3dp android:topRightRadius18dp / /shape左上角 3dp、右上角 18dp这个不对称比例是微信绿色气泡的特征。白色气泡反过来写就行。气泡的宽度不固定所以 LinearLayout 的minWidth要设一个值比如minWidth80dp文字短的时候不至于缩成一根细条。最大宽度我一般限制在屏幕宽度的 70%超过就换行这样长文本不会撑满整个屏幕。圆角头像用 BitmapShader 比较稳Glide 自带的不够灵活fun toRoundCornerBitmap(src: Bitmap, radius: Float): Bitmap { val output Bitmap.createBitmap(src.width, src.height, Bitmap.Config.ARGB_8888) val canvas Canvas(output) val paint Paint(Paint.ANTI_ALIAS_FLAG) val shader BitmapShader(src, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP) paint.shader shader canvas.drawRoundRect(RectF(0f, 0f, src.width.toFloat(), src.height.toFloat()), radius, radius, paint) return output }ARGB_8888是为了保留透明通道如果被系统转换成RGB_565JPG 图的默认格式透明度丢失后四个角会发黑这是第 4 章那条坑的正解。CLAMP模式保证 Bitmap 尺寸和输出尺寸一致时不会出现边缘拉伸。气泡尾巴那条小三角纯 Shape 画不出来需要单独用 Path 画或者用点九图。我的做法是教学场景里直接用 Shape 加一个小 View 顶在气泡左上角不追求像素级还原但视觉上已经很接近。等到你需要做真项目时再换成静态 .9 图两端拉伸都不会变形这才是微信实际用的方案。从那以后我每次仿写字里行间类似的 App都会先写一张尺寸表把状态栏、标题栏、TabBar 的高度颜色全部定死再动手写 XML。状态栏那类玄学问题能提前固化成参数就提前固化不要在十个 Activity 里各写一份实现。这套资源里最值得抄的不是某一个页面而是“标题栏手写、Tab 手写、Fragment 用 show/hide、所有变色走 selector”这一整套习惯。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?