简易视频预览器


📁 项目名称:SimpleVideoPreviewer(Java 版)

技术栈
  • 语言:Java(兼容 Android Studio 最新版本)
  • 核心组件:VideoViewIntent.ACTION_PICKActivityResultLauncher
  • 权限:READ_EXTERNAL_STORAGE(适用于 Android 10 及以下;如需支持 Android 11+,建议后续升级到 SAF,但 Java 实现复杂度略高,实习生项目可暂不涉及)

📄 1. AndroidManifest.xml

复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="视频预览器"
        android:theme="@style/Theme.AppCompat.Light.DarkActionBar">
        
        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

⚠️ 注意:Android 13(API 33)起需使用 READ_MEDIA_VIDEO,但为简化,此处以通用权限为例。面试时可说明"了解新存储模型"。


📄 2. activity_main.xml

复制代码
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <Button
        android:id="@+id/btnPickVideo"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="选择视频" />

    <VideoView
        android:id="@+id/videoView"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:layout_marginTop="16dp" />

</LinearLayout>

📄 3. MainActivity.java(核心逻辑 - Java 版)

复制代码
package com.example.simplevideopreviewer;

import android.Manifest;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.widget.Button;
import android.widget.Toast;
import android.widget.VideoView;

import androidx.activity.result.ActivityResultLauncher;
import androidx.activity.result.contract.ActivityResultContracts;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {

    private static final int REQUEST_CODE_PERMISSION = 100;
    private VideoView videoView;
    private ActivityResultLauncher<String> pickVideoLauncher;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        videoView = findViewById(R.id.videoView);
        Button btnPick = findViewById(R.id.btnPickVideo);

        // 注册 Activity Result Launcher(替代 startActivityForResult)
        pickVideoLauncher = registerForActivityResult(
                new ActivityResultContracts.GetContent(),
                uri -> {
                    if (uri != null) {
                        playVideo(uri);
                    } else {
                        Toast.makeText(this, "未选择视频", Toast.LENGTH_SHORT).show();
                    }
                }
        );

        btnPick.setOnClickListener(v -> {
            if (checkStoragePermission()) {
                pickVideoLauncher.launch("video/*");
            } else {
                requestStoragePermission();
            }
        });
    }

    private boolean checkStoragePermission() {
        return ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.READ_EXTERNAL_STORAGE
        ) == PackageManager.PERMISSION_GRANTED;
    }

    private void requestStoragePermission() {
        ActivityCompat.requestPermissions(
                this,
                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                REQUEST_CODE_PERMISSION
        );
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_PERMISSION) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                pickVideoLauncher.launch("video/*");
            } else {
                Toast.makeText(this, "需要存储权限才能选择视频", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void playVideo(Uri uri) {
        videoView.setVideoURI(uri);
        videoView.start();
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 暂停视频,避免后台继续播放
        if (videoView.isPlaying()) {
            videoView.pause();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 停止并释放资源
        videoView.stopPlayback();
    }
}
相关推荐
Bigger18 小时前
Flutter 开发实战:解决华为 HarmonyOS 任务列表不显示 App 名称的终极指南
android·flutter·华为
利剑 -~21 小时前
mysql面试题整理
android·数据库·mysql
梁同学与Android1 天前
Android ---【经验篇】ArrayList vs CopyOnWriteArrayList 核心区别,怎么选择?
android·java·开发语言
沐怡旸1 天前
【翻译】adb screenrecord 帮助文档
android
lienyin1 天前
Android 简单的SFTP服务端+客户端通信传文件
android
fatiaozhang95271 天前
中兴B860AV5.2-U_原机安卓4.4.2系统专用_晶晨S905L3SB处理器_线刷固件包
android·电视盒子·刷机固件·机顶盒刷机·中兴b860av5.2-u
儿歌八万首1 天前
Android 自定义 View 实战:打造一个跟随滑动的丝滑指示器
android·kotlin
我有与与症1 天前
Kuikly 实战:手把手撸一个跨平台 AI 聊天助手 (ChatDemo)
android
恋猫de小郭1 天前
Flutter UI 设计库解耦重构进度,官方解答未来如何适配
android·前端·flutter
apihz1 天前
全球IP归属地查询免费API详细指南
android·服务器·网络·网络协议·tcp/ip