You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现Activity与Fragment复用同一套分页逻辑 避免重复代码

解决方案

核心思路采用委托组合模式实现代码复用,由于Java/Kotlin不支持多继承,Activity和Fragment本身已有各自的基类,该方案完全避免重复代码,也不需要强行嵌套Fragment增加不必要的层级。

针对你提到的两个前置问题先做说明:

  1. 在Activity中展示Fragment不会有可感知的性能损耗,只要不是频繁创建销毁、过度嵌套Fragment,性能影响基本可以忽略,但该场景下不需要用嵌套Fragment实现,委托方案更简洁易维护
  2. 原有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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 20:27:04