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

Android 单VideoFragment实现CardView点击播放不同视频

运动教程多Card跳转单VideoFragment实现方案

问题核心原因

  • MediaController初始化要求传入Context类型参数,Fragment本身不是Context,直接传this会触发类型不匹配报错
  • 布局加载错误是onCreateView中inflate方法传入了错误的布局id,参数传递未使用Fragment标准Bundle传参机制,导致无法根据点击项加载对应视频

完整实现代码

1. VideoFragment 核心逻辑修复

统一用静态newInstance方法处理传参,修复布局加载、MediaController初始化问题,补充生命周期逻辑避免内存泄漏

public class VideoFragment extends Fragment {
    // 传参Key常量,避免硬编码拼写错误
    private static final String ARG_VIDEO_RES_ID = "arg_video_res_id";
    private static final String ARG_SPORT_NAME = "arg_sport_name";
    private VideoView videoView;
    private int currentVideoResId;
    private String currentSportName;

    // 静态实例化方法,外部跳转统一调用此方法传参
    public static VideoFragment newInstance(int videoResId, String sportName) {
        VideoFragment fragment = new VideoFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_VIDEO_RES_ID, videoResId);
        args.putString(ARG_SPORT_NAME, sportName);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // 修复:加载正确的fragment_video布局,替换之前错误的fragment_exercise
        View root = inflater.inflate(R.layout.fragment_video, container, false);
        // 读取传入的参数
        if (getArguments() != null) {
            currentVideoResId = getArguments().getInt(ARG_VIDEO_RES_ID);
            currentSportName = getArguments().getString(ARG_SPORT_NAME);
        }
        videoView = root.findViewById(R.id.video_view);
        initPlayer();
        return root;
    }

    private void initPlayer() {
        // 修复:传入requireContext()获取宿主Activity上下文,不再直接传this
        MediaController mediaController = new MediaController(requireContext());
        mediaController.setAnchorView(videoView);
        videoView.setMediaController(mediaController);
        // 拼接raw资源下对应视频的URI
        Uri videoUri = Uri.parse("android.resource://" + requireContext().getPackageName() + "/" + currentVideoResId);
        videoView.setVideoURI(videoUri);
        // 准备完成后自动播放
        videoView.setOnPreparedListener(mp -> videoView.start());
        // 绑定返回按钮逻辑,点击回到运动列表页
        mediaController.setPrevNextListeners(null, v -> {
            if (getParentFragmentManager() != null) {
                getParentFragmentManager().popBackStack();
            }
        });
    }

    // 页面不可见时暂停播放
    @Override
    public void onPause() {
        super.onPause();
        if (videoView != null && videoView.isPlaying()) {
            videoView.pause();
        }
    }

    // 页面销毁时释放播放器资源
    @Override
    public void onDestroyView() {
        super.onDestroyView();
        if (videoView != null) {
            videoView.stopPlayback();
            videoView = null;
        }
    }
}

2. ExerciseFragment Card点击跳转逻辑

抽离公共点击绑定方法,点击不同Card时传入对应视频参数,复用同一个VideoFragment

public class ExerciseFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_exercise, container, false);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        // 提前将8个运动教程视频放到res/raw目录下,文件名仅支持小写字母、数字、下划线
        // 按自己布局中的CardView id、对应视频资源、运动名称绑定即可
        bindCardEvent(view.findViewById(R.id.card_running), R.raw.running, "跑步教程");
        bindCardEvent(view.findViewById(R.id.card_squat), R.raw.squat, "深蹲教程");
        bindCardEvent(view.findViewById(R.id.card_pushup), R.raw.pushup, "俯卧撑教程");
        bindCardEvent(view.findViewById(R.id.card_situp), R.raw.situp, "仰卧起坐教程");
        bindCardEvent(view.findViewById(R.id.card_plank), R.raw.plank, "平板支撑教程");
        bindCardEvent(view.findViewById(R.id.card_jumpjack), R.raw.jumpjack, "开合跳教程");
        bindCardEvent(view.findViewById(R.id.card_lunge), R.raw.lunge, "箭步蹲教程");
        bindCardEvent(view.findViewById(R.id.card_stretch), R.raw.stretch, "拉伸教程");
    }

    private void bindCardEvent(CardView cardView, int videoResId, String sportName) {
        cardView.setOnClickListener(v -> {
            VideoFragment videoFragment = VideoFragment.newInstance(videoResId, sportName);
            requireActivity().getSupportFragmentManager().beginTransaction()
                    .replace(R.id.fragment_container, videoFragment)
                    .addToBackStack(null) // 加入返回栈,支持按返回键回到列表
                    .commit();
        });
    }
}

注意事项

  • 视频文件需要放在res/raw目录下,没有该目录手动新建即可,文件命名不能包含大写字母、中文、空格、特殊符号,否则R文件无法正常识别资源
  • 确认fragment_video.xml中VideoView的id为video_view,宿主Activity中放置Fragment的容器id为fragment_container,和代码保持一致
  • 如果需要播放网络视频,只需将传参的视频资源id替换为String类型的视频url,加载时调用videoView.setVideoURI(Uri.parse(videoUrl))即可,同时在AndroidManifest.xml中添加网络权限 <uses-permission android:name="android.permission.INTERNET" />

内容的提问来源于stack exchange,提问作者yeontan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 16:15:45