Android面试总结之jet pack界面组件篇
一、RecyclerView 深入知识点
1. 视图复用机制与 ViewHolder 原理
核心原理:
- ViewHolder 缓存:通过
ViewHolder
缓存列表项视图,避免重复创建View
,减少findViewById
耗时。 - RecycledViewPool:RecyclerView 内部维护一个
RecycledViewPool
,回收的 ViewHolder 存入池中,滑动时优先从池中获取,而非新建。 - onCreateViewHolder vs onBindViewHolder:
onCreateViewHolder
:仅在需要新 ViewHolder(池中无可用)时调用,决定视图布局。onBindViewHolder
:每次列表项可见时调用,负责数据绑定,是性能优化关键。
源码相关:
// RecyclerView.Adapter 核心方法
public abstract class Adapter<VH extends ViewHolder> {// 创建ViewHolder(布局加载)public abstract VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType);// 绑定数据(视图更新)public abstract void onBindViewHolder(@NonNull VH holder, int position);// 获取Item类型(多类型列表)public int getItemViewType(int position) { return 0; }
}// RecyclerView 回收逻辑(简化版)
void recycleViewHolderInternal(ViewHolder holder) {if (holder.isRecyclable()) { // 判断是否可回收(如未被移除)mRecyclerPool.put(holder.getItemViewType(), holder); // 存入回收池}
}
面试高频问题:
- 如何优化 RecyclerView 性能?
- 复用
ViewHolder
,避免在onBindViewHolder
中执行耗时操作(如网络请求、复杂计算)。 - 使用
setHasFixedSize(true)
告知 RecyclerView 列表项尺寸不变,减少布局计算。 - 针对多类型列表,重写
getItemViewType
提高回收效率。 - 合理设置
RecycledViewPool
大小(默认每个类型缓存 5 个 ViewHolder):recyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool()); recyclerView.getRecycledViewPool().setMaxRecycledViews(viewType, maxCacheSize);
- 复用
2. LayoutManager 分类与适用场景
类型 | 特点 | 典型场景 |
---|---|---|
LinearLayoutManager | 线性排列(垂直 / 水平),支持预加载相邻项 | 普通列表、聊天消息列表 |
GridLayoutManager | 网格排列,可指定列数 | 图片列表、商品网格展示 |
StaggeredGridLayoutManager | 瀑布流布局,项高可变 | 新闻流、图文混排 |
CustomLayoutManager | 自定义布局逻辑(需重写布局算法) | 复杂动画列表、3D 效果布局 |
关键方法:
onLayoutChildren()
:定义子项的布局位置,需处理回收和定位。scrollVerticallyBy()
/scrollHorizontallyBy()
:处理滑动逻辑,决定可见项范围。
3. 动画与 ItemDecoration
- ItemAnimator:默认实现
DefaultItemAnimator
,支持增删改查动画,可自定义recyclerView.setItemAnimator(new SimpleItemAnimator()); // 简化版动画
- ItemDecoration:添加分割线、边距等装饰,重写
getItemOffsets
和onDraw
:// 垂直列表分割线示例 class DividerItemDecoration extends RecyclerView.ItemDecoration {private final Drawable divider;public DividerItemDecoration(Context context, int drawableResId) {divider = ContextCompat.getDrawable(context, drawableResId);}@Overridepublic void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {outRect.bottom = divider.getIntrinsicHeight(); // 预留分割线高度}@Overridepublic void onDraw(Canvas c, RecyclerView parent, State state) {int left = parent.getPaddingLeft();int right = parent.getWidth() - parent.getPaddingRight();int childCount = parent.getChildCount();for (int i = 0; i < childCount; i++) {View child = parent.getChildAt(i);RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();int top = child.getBottom() + params.bottomMargin;int bottom = top + divider.getIntrinsicHeight();divider.setBounds(left, top, right, bottom);divider.draw(c);}} }
二、ViewPager2 深入知识点
1. 与 ViewPager 的核心区别
特性 | ViewPager2 | ViewPager(旧版) |
---|---|---|
底层实现 | 基于 RecyclerView,支持 ViewHolder 复用 | 基于 PagerAdapter,手动管理视图 |
滑动方向 | 支持垂直滑动(setOrientation ) | 仅水平滑动 |
视图回收 | 高效复用,支持部分加载(类似 RecyclerView) | 全量回收,性能较差 |
双向滑动 | 支持 RTL(从右到左)滑动 | 需自定义实现 |
Fragment 支持 | 推荐使用FragmentStateAdapter | FragmentPagerAdapter |
2. 关键机制与源码解析
- Adapter 类型:
RecyclerView.Adapter
:与 RecyclerView 完全兼容,支持任意视图类型。FragmentStateAdapter
:专为 Fragment 设计,自动管理 Fragment 生命周期,适合大量页面(如引导页):// FragmentStateAdapter 示例(简化版) class MyFragmentAdapter(activity: Activity) : FragmentStateAdapter(activity) {private val fragmentList = listOf(FirstFragment(), SecondFragment())override fun createFragment(position: Int): Fragment {return fragmentList[position] // 创建Fragment}override fun getItemCount(): Int {return fragmentList.size // 页面数量} }
- 页面切换监听:
通过registerOnPageChangeCallback
替代旧版addOnPageChangeListener
,支持更精细的状态回调(STATE_IDLE
/STATE_DRAGGING
/STATE_SETTLING
):viewPager2.registerOnPageChangeCallback(object : OnPageChangeCallback() {override fun onPageSelected(int position) {// 页面选中时回调}override fun onPageScrollStateChanged(int state) {// 滑动状态变化(空闲/拖拽/ Settling)} });
- 平滑滚动与动画:
支持通过setCurrentItem(position, smoothScroll)
实现平滑滚动,动画由 RecyclerView 的ItemAnimator
控制。
3. 性能优化与常见问题
- 大量页面处理:
使用FragmentStateAdapter
而非FragmentPagerAdapter
,前者仅保留当前及相邻页面的 Fragment,避免内存泄漏。 - 方向设置:
viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL); // 垂直滑动
- 与 TabLayout 联动:
通过TabLayoutMediator
实现自动同步(需添加navigation-ui
依赖):new TabLayoutMediator(tabLayout, viewPager2, (tab, position) -> {tab.setText("Tab " + (position + 1)); }).attach();
三、带注释的代码实现
RecyclerView 完整注释版 Adapter
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {private final List<String> dataList; // 数据源// 构造函数:接收数据列表public MyRecyclerViewAdapter(List<String> dataList) {this.dataList = dataList;}// 创建ViewHolder(仅在需要新视图时调用,复用池无可用ViewHolder时)@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {// 加载列表项布局View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);return new ViewHolder(itemView); // 创建ViewHolder并缓存视图}// 绑定数据(列表项可见时调用,可能复用旧ViewHolder)@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {String item = dataList.get(position);holder.textView.setText(item); // 更新视图数据// 避免在此处执行耗时操作(如网络请求、图片加载需异步处理)}// 获取数据总条数@Overridepublic int getItemCount() {return dataList.size();}// ViewHolder:缓存列表项视图,避免重复findViewByIdstatic class ViewHolder extends RecyclerView.ViewHolder {final TextView textView; // 列表项中的TextViewViewHolder(@NonNull View itemView) {super(itemView);textView = itemView.findViewById(R.id.textView); // 仅初始化一次}}
}
ViewPager2 带 Fragment 的 Adapter(Kotlin)
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapterclass MyViewPager2Adapter(activity: FragmentActivity) : FragmentStateAdapter(activity) {private val fragmentList = listOf( // 页面Fragment列表FirstFragment(),SecondFragment(),ThirdFragment())// 创建Fragment(页面切换时,根据position创建对应的Fragment)override fun createFragment(position: Int): Fragment {return fragmentList[position] // 返回对应位置的Fragment}// 获取页面总数override fun getItemCount(): Int {return fragmentList.size}
}// 在Activity中使用
viewPager2.adapter = MyViewPager2Adapter(this)
viewPager2.orientation = ViewPager2.ORIENTATION_HORIZONTAL // 水平滑动(默认)
四、面试高频问题总结
RecyclerView 相关
-
ViewHolder 的作用是什么?为什么必须缓存视图?
- 缓存视图,避免重复调用
findViewById
,提升数据绑定效率;配合 RecycledViewPool 实现视图复用,减少内存占用。
- 缓存视图,避免重复调用
-
如何处理 RecyclerView 的多类型列表?
- 重写
getItemViewType(int position)
,根据数据类型返回不同 viewType;onCreateViewHolder
中根据 viewType 加载不同布局。
- 重写
-
RecyclerView 的 ItemDecoration 和 ItemAnimator 有什么区别?
ItemDecoration
:用于添加装饰(分割线、边距等),影响布局;ItemAnimator
:处理列表项增删改查的动画效果。
ViewPager2 相关
-
ViewPager2 为什么比旧版 ViewPager 性能更好?
- 基于 RecyclerView 的 ViewHolder 复用机制,减少视图创建和销毁;支持部分加载,仅维护可见及相邻页面的视图。
-
如何实现 ViewPager2 的垂直滑动和页面平滑切换?
- 垂直滑动:
viewPager2.orientation = ViewPager2.ORIENTATION_VERTICAL
; - 平滑切换:
viewPager2.setCurrentItem(targetPosition, true)
(第二个参数为 true 时启用平滑动画)。
- 垂直滑动:
-
ViewPager2 如何与 Fragment 配合使用?推荐哪种 Adapter?
- 使用
FragmentStateAdapter
(继承自 RecyclerView.Adapter),自动管理 Fragment 的生命周期,适合大量页面场景(对比旧版FragmentPagerAdapter
的全量加载,性能更优)。
- 使用