如何实现Activity与Fragment复用同一套分页逻辑 避免重复代码
解决方案
核心思路采用委托组合模式实现代码复用,由于Java/Kotlin不支持多继承,Activity和Fragment本身已有各自的基类,该方案完全避免重复代码,也不需要强行嵌套Fragment增加不必要的层级。
针对你提到的两个前置问题先做说明:
- 在Activity中展示Fragment不会有可感知的性能损耗,只要不是频繁创建销毁、过度嵌套Fragment,性能影响基本可以忽略,但该场景下不需要用嵌套Fragment实现,委托方案更简洁易维护
- 原有
PaginableComponent接口可以保留,只需要把公共逻辑抽离到独立代理类,宿主(Activity/Fragment)仅做生命周期转发和必要依赖提供即可。
具体实现步骤
步骤1:抽离公共逻辑到代理类
把原有PaginableFragment里所有和Fragment特性无关的逻辑,全部抽成独立的PaginableDelegate类:
public class PaginableDelegate { private final AtomicBoolean mIsFirstLoadedList = new AtomicBoolean(false); private SwipeRefreshLayout mSwipeRefreshLayout; private View mStatusLayout; private ProgressBar mStatusProgressBar; private TextView mStatusMessageTextView; private TextView mTryAgainTextView; private final PaginableComponent mComponent; private final Context mContext; // 宿主提供是否可见的状态,适配Activity和Fragment不同的可见性判断逻辑 private final Supplier<Boolean> mIsHiddenSupplier; public PaginableDelegate(Context context, PaginableComponent component, Supplier<Boolean> isHiddenSupplier) { this.mContext = context; this.mComponent = component; this.mIsHiddenSupplier = isHiddenSupplier; } public void initViews(View rootView) { mSwipeRefreshLayout = rootView.findViewById(R.id.swipeRefreshLayout); mStatusLayout = rootView.findViewById(R.id.statusLayout); if (mStatusLayout != null) { mStatusProgressBar = mStatusLayout.findViewById(R.id.progressBar); mStatusMessageTextView = mStatusLayout.findViewById(R.id.text); mTryAgainTextView = mStatusLayout.findViewById(R.id.tryAgain); mTryAgainTextView.setOnClickListener(v -> mComponent.onTryLoadList()); } mComponent.getAdapter().setLoadListMoreListener(loadMoreListener); if (mSwipeRefreshLayout != null) { mSwipeRefreshLayout.setColorSchemeColors(UiUtils.getStyleColor(mContext, R.attr.colorPrimary)); mSwipeRefreshLayout.setOnRefreshListener(this::checkNetAndRequestList); } } protected void showStatus(String type) { if (mStatusLayout != null) { switch (type) { case Messages.MSG_LOADING: mStatusProgressBar.setVisibility(View.VISIBLE); mStatusMessageTextView.setVisibility(View.GONE); mTryAgainTextView.setVisibility(View.GONE); break; case Messages.MSG_NET: mStatusMessageTextView.setText(R.string.no_network_connection); mStatusProgressBar.setVisibility(View.GONE); mStatusMessageTextView.setVisibility(View.VISIBLE); mTryAgainTextView.setVisibility(View.VISIBLE); break; case Messages.MSG_ERROR: mStatusMessageTextView.setText(R.string.sorry_unexpected_error_occurred); mStatusProgressBar.setVisibility(View.GONE); mStatusMessageTextView.setVisibility(View.VISIBLE); mTryAgainTextView.setVisibility(View.GONE); break; } mStatusLayout.setVisibility(View.VISIBLE); } } private void hideStatus() { if (mStatusLayout != null) { mStatusLayout.setVisibility(View.GONE); } } private final PaginableAdapter.LoadListMoreListener loadMoreListener = new PaginableAdapter.LoadListMoreListener() { @Override public void onLoadListMore(int oldTotalCount) { mComponent.getAdapter().showEndMessage(Paginable.TYPE_SHIMMER); if (NetReceiver.isNetConnected(mContext)) { mComponent.onRequestLoadMoreList(oldTotalCount); } else { showStatus(Messages.MSG_NET); } } }; public void onSuccessLoadList(@NonNull List<Paginable> list) { mIsFirstLoadedList.set(true); if (mSwipeRefreshLayout != null) { mSwipeRefreshLayout.setRefreshing(false); } hideStatus(); mComponent.getAdapter().resetList(list); } public void onErrorLoadList(String errorMessage) { if (mSwipeRefreshLayout != null) { mSwipeRefreshLayout.setRefreshing(false); } showStatus(Messages.MSG_ERROR); mComponent.getAdapter().setIsLoadingMore(false); } public void onSuccessLoadMore(@NonNull List<Paginable> list) { if (mSwipeRefreshLayout != null) { mSwipeRefreshLayout.setRefreshing(false); } hideStatus(); mComponent.getAdapter().hideEndMessages(); mComponent.getAdapter().addList(list); mComponent.getAdapter().setIsLoadingMore(false); } public void onTryLoadList() { if (!mIsFirstLoadedList.get()) { checkNetAndRequestList(); } } public void onNetEvent(NetReceiver.NetEvent event) { if (event.isConnected()) { hideStatus(); if (!mIsFirstLoadedList.get()) { mComponent.getAdapter().clearAndInitShimmers(); mComponent.onRequestList(); } if (mComponent.getAdapter().isLoadingMore()) { mComponent.onRequestLoadMoreList(mComponent.getAdapter().getItemCount()); } } else { showStatus(Messages.MSG_NET); } } public void checkNetAndRequestList() { if (NetReceiver.isNetConnected(mContext)) { if (!mIsFirstLoadedList.get()) { mComponent.getAdapter().clearAndInitShimmers(); } mComponent.onRequestList(); } else { if (mSwipeRefreshLayout != null) { mSwipeRefreshLayout.setRefreshing(false); } showStatus(Messages.MSG_NET); if (!mIsFirstLoadedList.get()) { mComponent.getAdapter().clearAndInitNoInternet(); } } } }
步骤2:实现分页Activity基类
public abstract class PaginableActivity extends BaseActivity implements PaginableComponent { private PaginableDelegate mDelegate; private boolean isHidden = false; // Activity默认可见,退后台判定为隐藏 @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(getLayoutId()); // 初始化代理,传入可见性判断逻辑 mDelegate = new PaginableDelegate(this, this, () -> isHidden); mDelegate.initViews(findViewById(android.R.id.content)); } // 子类自行返回布局ID protected abstract int getLayoutId(); @Override protected void onPause() { super.onPause(); isHidden = true; } @Override protected void onResume() { super.onResume(); isHidden = false; } // 把接口方法转发给代理处理 @CallSuper @Override public void onSuccessLoadList(@NonNull List<Paginable> list) { mDelegate.onSuccessLoadList(list); } @CallSuper @Override public void onErrorLoadList(String errorMessage) { mDelegate.onErrorLoadList(errorMessage); } @CallSuper @Override public void onSuccessLoadMore(@NonNull List<Paginable> list) { mDelegate.onSuccessLoadMore(list); } @CallSuper @Override public void onTryLoadList() { mDelegate.onTryLoadList(); } @Override public void onNetEvent(NetReceiver.NetEvent event) { mDelegate.onNetEvent(event); } public void checkNetAndRequestList() { mDelegate.checkNetAndRequestList(); } }
步骤3:重构原有分页Fragment基类
删除原有PaginableFragment中的重复公共逻辑,替换为代理实现即可:
public abstract class PaginableFragment extends BaseFragment implements PaginableComponent { private PaginableDelegate mDelegate; public Boolean isHidden = true; @CallSuper @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); mDelegate = new PaginableDelegate(requireContext(), this, () -> isHidden); mDelegate.initViews(view); } @Override public void onPause() { super.onPause(); isHidden = true; } @Override public void onResume() { super.onResume(); isHidden = isHidden(); } @Override public void onHiddenChanged(boolean hidden) { super.onHiddenChanged(hidden); isHidden = hidden; } // 方法转发给代理 @CallSuper @Override public void onSuccessLoadList(@NonNull List<Paginable> list) { mDelegate.onSuccessLoadList(list); } @CallSuper @Override public void onErrorLoadList(String errorMessage) { mDelegate.onErrorLoadList(errorMessage); } @CallSuper @Override public void onSuccessLoadMore(@NonNull List<Paginable> list) { mDelegate.onSuccessLoadMore(list); } @CallSuper @Override public void onTryLoadList() { mDelegate.onTryLoadList(); } @Override public void onNetEvent(NetReceiver.NetEvent event) { mDelegate.onNetEvent(event); } public void checkNetAndRequestList() { mDelegate.checkNetAndRequestList(); } }
方案优势
- 完全消除重复代码,所有分页逻辑只在
PaginableDelegate里维护一次,修改时不需要同时调整Activity和Fragment的基类 - 不侵入原有基类继承结构,后续如果要给Dialog、自定义View、Compose界面加分页逻辑,也可以直接复用该代理类
- 性能优于嵌套Fragment的方案,没有额外的Fragment生命周期开销
内容的提问来源于stack exchange,提问作者Hussein Yaqoobi
相关产品推荐
相关产品推荐

