简易视频预览器


📁 项目名称: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();
    }
}
相关推荐
Android系统攻城狮9 小时前
Android tinyalsa深度解析之pcm_close调用流程与实战(一百零四)
android·pcm·tinyalsa·音频进阶·音频性能实战·android hal
weixin_411191849 小时前
LifecycleEventObserver和DefaultLifecycleObserver使用
android
、BeYourself9 小时前
Intent :跳转与数据传递的正确打开方式
android·android-studio
灵感菇_9 小时前
Android 列表控件全面解析:ListView 与 RecyclerView
android·ui
2601_9498095910 小时前
flutter_for_openharmony家庭相册app实战+照片详情实现
android·java·flutter
fundroid10 小时前
Kotlin 泛型进阶:in、out 与 reified 实战
android·开发语言·kotlin
Android系统攻城狮10 小时前
Android tinyalsa深度解析之pcm_open调用流程与实战(一百零三)
android·pcm·tinyalsa·音频进阶·音频性能实战·android hal
2501_9444480010 小时前
Flutter for OpenHarmony衣橱管家App实战:意见反馈功能实现
android·javascript·flutter
风流倜傥唐伯虎10 小时前
./gradlew assembleDebug和gradle build区别
android·android studio
有位神秘人10 小时前
Android中获取当前设备的宽高与屏幕密度等数据的工具类
android