首页 > 其他分享 >android试题编号:2-2-8车载媒体播放 App

android试题编号:2-2-8车载媒体播放 App

时间:2024-10-12 23:20:19浏览次数:3  
标签:视频 试题 void binding private public android 播放 App

(1)任务描述

中控大屏仪表屏幕中间显示媒体组件,组件内上方显示正在播放的媒体信息例如“歌曲名称”“音视频名称”信息栏、“音量显示”。

信息栏下方显示播放控制按钮,“播放/暂停”“上一曲下一曲”“音量滑块”“静音”,在操控设备中点击“播放/暂停”,仪表屏中的按钮随之切换“播放/暂停”状态,点击操控设备中的“上一曲下一曲”按钮,仪表屏可切换正在播放媒体信息,点击操控设备中的“静音”按钮,仪表屏幕“音量显示”调节到静音模式。

中控大屏主屏中显示当前播放的歌曲名称、歌曲进度和歌词,显示“播放/暂停”“上一曲下一曲”“音量滑块”“静音”按钮,点击各自按钮可对当前播放的歌曲进行操作。

主页面用卡片列表展示该设备中所有的视频信息,卡片上半部分展示该视频的预览图,下半部分显示视频名称和“上次看到 xx 分xx 秒”信息。

点击对应的视频卡片,对应的屏幕会进入视频播放页面,并播放所选择卡片对应的视频。当点击正在播放的视频时,左上角显示【返回】按钮,点击【返回】则主屏回到影视娱乐 App 主页面。视频播放页面下方显示视频播放器工具栏,工具栏上半部分显示【快进】【快退】【暂停/继续播放】【其他视频】按钮,点击【其他视频】按钮可弹出视频列表弹层,以列表的形式展示其他视频,点击列表项可切换至对应的视频进行播放,页面播放工具栏下半部分显示视频的【当前播放时长】【总时长】和【视频进度条】,可拖动视频进度条调整当前视频播放进度。

步骤一:

首先创建一个android 项目名字自己想

步骤二:在build.gradle中给上ViewBinding等待Sync Now 构建就完成了

步骤三: AndroidManifest.xml中给上权限和文件保存的位置

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

步骤四:构建页面布局(activity_main.xml主界面布局,以及MusicFragment和VideoFragment音乐和视频界面布局 )

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/main"
    android:background="@drawable/activity_bg"
    android:orientation="horizontal">

    <LinearLayout
        android:layout_width="70dp"
        android:layout_height="150dp"
        android:layout_gravity="center_vertical"
        android:layout_margin="8dp"
        android:background="@drawable/text_bg"
        android:orientation="vertical"
        android:paddingTop="8dp"
        android:paddingBottom="8dp">

        <TextView
            android:id="@+id/music"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_gravity="center"
            android:layout_weight="1"
            android:gravity="center"
            android:text="音乐"
            android:textColor="@color/blue"
            android:textSize="30px"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/video"
            android:layout_width="wrap_content"
            android:layout_height="0dp"
            android:layout_gravity="center"
            android:layout_weight="1"
            android:gravity="center"
            android:text="视频"
            android:textColor="@color/black"
            android:textSize="30px"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    </LinearLayout>

    <FrameLayout
        android:id="@+id/fragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="50dp"
        android:layout_marginEnd="8dp"
        android:layout_marginBottom="50dp" />

</LinearLayout>

fragment_music.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="600px"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/left"
        android:layout_width="70px"
        android:layout_height="70px"
        android:layout_marginTop="32dp"
        app:layout_constraintEnd_toStartOf="@+id/play"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/seekBar"
        app:srcCompat="@drawable/left" />

    <ImageView
        android:id="@+id/play"
        android:layout_width="70px"
        android:layout_height="70px"
        app:layout_constraintBottom_toBottomOf="@+id/left"
        app:layout_constraintEnd_toStartOf="@+id/right"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/left"
        app:layout_constraintTop_toTopOf="@+id/left"
        app:srcCompat="@drawable/selector_play" />

    <ImageView
        android:id="@+id/right"
        android:layout_width="70px"
        android:layout_height="70px"
        app:layout_constraintBottom_toBottomOf="@+id/play"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toEndOf="@+id/play"
        app:layout_constraintTop_toTopOf="@+id/play"
        app:srcCompat="@drawable/right" />

    <TextView
        android:id="@+id/tv_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="32dp"
        android:text="歌曲名称"
        android:textColor="@color/black"
        android:textSize="50px"
        android:textStyle="bold"
        app:layout_constraintBottom_toTopOf="@+id/seekBar"
        app:layout_constraintEnd_toEndOf="@+id/seekBar"
        app:layout_constraintStart_toStartOf="@+id/seekBar" />

</androidx.constraintlayout.widget.ConstraintLayout>

fragment_video.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".VideoFragment">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rv_video"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
        app:spanCount="2" />

</FrameLayout>

外加item_video.xml用来设置展示视频的样式

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:cardCornerRadius="20px"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <ImageView
                android:id="@+id/iv_img"
                android:layout_width="match_parent"
                android:layout_height="90dp"
                android:scaleType="centerCrop"
                android:src="@drawable/ic_launcher_background" />

            <TextView
                android:id="@+id/tv_name"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:ellipsize="end"
                android:maxLines="1"
                android:padding="5dp"
                android:text="视频名称"
                android:textColor="@color/black"
                android:textSize="16dp" />
        </LinearLayout>
    </androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>

步骤五:构建完页面布局就要针对页面布局接下来处理逻辑

1:MainActivity代码的构建是为了实现fragment的切换

public class MainActivity extends AppCompatActivity {

    private com.example.musicandvideo.databinding.ActivityMainBinding binding;
    private MusicFragment musicFragment;
    private VideoFragment videoFragment;
    private FragmentManager supportFragmentManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });
        initView();
    }

    private void initView() {
        seleFragment(new MusicFragment());
        binding.music.setOnClickListener(v -> {
            setAllTextColor();
            binding.music.setTextColor(getResources().getColor(R.color.blue));
            seleFragment(new MusicFragment());
        });
        binding.video.setOnClickListener(v -> {
            setAllTextColor();
            binding.video.setTextColor(getResources().getColor(R.color.blue));
            seleFragment(new VideoFragment());
        });
    }

    public void seleFragment(Fragment fragment) {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.replace(binding.fragment.getId(), fragment).commit();
    }

    private void setAllTextColor() {
        binding.music.setTextColor(getResources().getColor(R.color.black));
        binding.video.setTextColor(getResources().getColor(R.color.black));
    }
  1. 类定义
    • MainActivity类继承自AppCompatActivity,这是实现活动(Activity)的标准方式,提供向后兼容的Activity功能。
  2. 成员变量
    • binding:使用ActivityMainBinding类(由View Binding生成)来访问布局中的视图,而不需要使用findViewById
    • musicFragmentvideoFragment:这两个变量预期用于存储MusicFragmentVideoFragment的实例,但在提供的代码段中未被使用。
    • supportFragmentManager:用于管理Fragment事务的FragmentManager实例,但在提供的代码段中未被初始化或使用;实际上,通过getSupportFragmentManager()方法可以直接获取。
  3. onCreate方法
    • 这是Activity生命周期中的一个关键方法,用于初始化Activity。
    • EdgeToEdge.enable(this);:这行代码可能是为了启用全屏模式或类似功能,但EdgeToEdge不是Android SDK的一部分,可能是某个库或项目中自定义的类。
    • 使用View Binding来绑定布局,并设置活动的内容视图。
    • 通过ViewCompat.setOnApplyWindowInsetsListener设置窗口内边距监听器,用于调整视图的内边距以适应系统栏(如状态栏和导航栏)。
  4. initView方法
    • 初始化视图,包括设置默认显示的Fragment(音乐Fragment)和设置音乐、视频按钮的点击监听器。
    • 当点击音乐或视频按钮时,首先将所有按钮的文本颜色设置为黑色,然后将被点击按钮的文本颜色设置为蓝色,并替换当前显示的Fragment。
  5. seleFragment方法
    • 接受一个Fragment对象作为参数,并使用FragmentTransaction来替换当前显示的Fragment。
    • 这里使用了binding.fragment.getId()来获取Fragment容器的ID,这表明布局文件中应该有一个具有相应ID的视图容器用于承载Fragment。
  6. setAllTextColor方法
    • 将音乐和视频按钮的文本颜色设置为黑色。

注意

  • seleFragment方法中,每次点击按钮时都会创建新的MusicFragmentVideoFragment实例。这通常不是最佳实践,因为它会导致不必要的Fragment创建和销毁,可能导致性能问题。更好的做法是使用FragmentManagerfindFragmentByTagfindFragmentById方法来检查Fragment是否已存在,如果存在则直接使用它,否则创建新的实例。
  • supportFragmentManager变量在代码中未被使用,可以直接在seleFragment方法中通过getSupportFragmentManager()调用获取FragmentManager

2:实现音乐播放界面MusicFragment.java

public class MusicFragment extends Fragment {

    private FragmentMusicBinding binding;
    private static final int STORAGE_PERMISSION_CODE = 1;
    private List<File> audioFiles;
    private int currentSongIndex = 0;
    private MediaPlayer mediaPlayer;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = FragmentMusicBinding.inflate(inflater, container, false);
        View view = binding.getRoot();

        // 初始化点击事件
        binding.left.setOnClickListener(v -> playPreviousSong());
        binding.right.setOnClickListener(v -> playNextSong());
        binding.play.setOnClickListener(v -> togglePlayPause());

        // 申请权限
        if (checkStoragePermission()) {
            loadAudioFiles();
        } else {
            requestStoragePermission();
        }

        return view;
    }

    // 检查存储权限是否已授予
    private boolean checkStoragePermission() {
        return ContextCompat.checkSelfPermission(getContext(),
                Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
    }

    // 申请存储权限
    private void requestStoragePermission() {
        requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
    }

    // 处理权限申请结果
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == STORAGE_PERMISSION_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                // 权限授予后加载音频文件
                loadAudioFiles();
            } else {
                binding.left.setOnClickListener(null);
                binding.right.setOnClickListener(null);
                binding.play.setOnClickListener(null);
                Toast.makeText(getContext(), "存储权限被拒绝,无法加载音频文件", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 动态加载指定文件夹中的音频文件
    private void loadAudioFiles() {
        File downloadFolder = new File(Environment.getExternalStorageDirectory() + File.separator + "Download");
        audioFiles = new ArrayList<>();

        if (downloadFolder.exists() && downloadFolder.isDirectory()) {
            File[] files = downloadFolder.listFiles();
            if (files != null) {
                for (File file : files) {
                    if (file.isFile() && file.getName().endsWith(".mp3")) {
                        audioFiles.add(file);
                    }
                }
            }

            if (!audioFiles.isEmpty()) {
                playSong(audioFiles.get(currentSongIndex));
            } else {
                Toast.makeText(getContext(), "没有找到音频文件", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 播放音频文件
    private void playSong(File audioFile) {
        if (mediaPlayer != null) {
            mediaPlayer.release();
        }
        mediaPlayer = MediaPlayer.create(getContext(), Uri.fromFile(audioFile));
        binding.tvName.setText(audioFile.getName());

        mediaPlayer.setOnPreparedListener(mp -> {
            binding.seekBar.setMax(mediaPlayer.getDuration());
            binding.play.setSelected(false);  // 确保初始状态为暂停状态
            binding.seekBar.setProgress(0);   // 进度条初始化为0
        });

        mediaPlayer.setOnCompletionListener(mp -> playNextSong());

        // 更新进度条
        binding.seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                if (fromUser) {
                    mediaPlayer.seekTo(progress);
                }
            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {
            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
            }
        });
    }

    // 播放/暂停切换
    private void togglePlayPause() {
        if (mediaPlayer != null) {
            if (mediaPlayer.isPlaying()) {
                mediaPlayer.pause();
                binding.play.setSelected(false); // 更新按钮状态
                binding.seekBar.removeCallbacks(this::updateSeekBar);  // 停止进度条更新
            } else {
                mediaPlayer.start();
                binding.play.setSelected(true);
                updateSeekBar();  // 继续播放时重新启动进度条更新
            }
        }
    }

    // 播放上一首歌曲
    private void playPreviousSong() {
        if (!audioFiles.isEmpty()) {
            currentSongIndex = (currentSongIndex - 1 + audioFiles.size()) % audioFiles.size();
            playSong(audioFiles.get(currentSongIndex));
        }
    }

    // 播放下一首歌曲
    private void playNextSong() {
        if (!audioFiles.isEmpty()) {
            currentSongIndex = (currentSongIndex + 1) % audioFiles.size();
            playSong(audioFiles.get(currentSongIndex));
        }
    }

    // 更新进度条
    private void updateSeekBar() {
        if (mediaPlayer != null) {
            binding.seekBar.setProgress(mediaPlayer.getCurrentPosition());
            if (mediaPlayer.isPlaying()) {
                binding.seekBar.postDelayed(this::updateSeekBar, 1000);  // 每秒更新一次进度条
            }
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer = null;
        }
        binding.seekBar.removeCallbacks(this::updateSeekBar);  // 清除所有的回调
    }

3:实现视频播放器部分代码

VideoAdapter.java

public class VideoAdapter extends RecyclerView.Adapter<VideoAdapter.VideoViewHolder> {

    private List<File> videoFiles;
    private OnVideoClickListener onVideoClickListener;

    public VideoAdapter(List<File> videoFiles, OnVideoClickListener onVideoClickListener) {
        this.videoFiles = videoFiles;
        this.onVideoClickListener = onVideoClickListener;
    }

    @NonNull
    @Override
    public VideoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new VideoViewHolder(ItemVideoBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull VideoViewHolder holder, int position) {
        File videoFile = videoFiles.get(position);
        holder.itemView.setOnClickListener(v -> onVideoClickListener.onVideoClick(videoFile));
        holder.onBind(videoFile);
    }

    @Override
    public int getItemCount() {
        return videoFiles.size();
    }

    public static class VideoViewHolder extends RecyclerView.ViewHolder {
        ItemVideoBinding binding;

        public VideoViewHolder(@NonNull ItemVideoBinding itemView) {
            super(itemView.getRoot());
            binding = itemView;
        }

        public void onBind(File file) {
            Bitmap thumbnail = getVideoThumbnail(file.getPath());
            if (thumbnail != null) {
                binding.ivImg.setImageBitmap(thumbnail);
            }
            binding.tvName.setText(file.getName());
        }

        // 获取视频封面缩略图
        private Bitmap getVideoThumbnail(String filePath) {
            MediaMetadataRetriever retriever = new MediaMetadataRetriever();
            try {
                retriever.setDataSource(filePath);
                // 提取视频的第一帧作为缩略图
                return retriever.getFrameAtTime(0, MediaMetadataRetriever.OPTION_CLOSEST_SYNC);
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            } finally {
                try {
                    retriever.release();
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }
        }

4:VideoFragment代码实现

public class VideoFragment extends Fragment {

    private FragmentVideoBinding binding;
    private List<File> videoList = new ArrayList<>();
    private VideoAdapter videoAdapter;
    private static final String[] REQUIRED_PERMISSIONS = new String[]{
            Manifest.permission.READ_EXTERNAL_STORAGE // 读取外部存储的权限
    };
    private static final int REQUEST_PERMISSION_CODE = 1;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        binding = FragmentVideoBinding.inflate(inflater, container, false);
        return binding.getRoot();
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        videoAdapter = new VideoAdapter(videoList, this::onVideoClick);
        binding.rvVideo.setAdapter(videoAdapter);

        loadVideoFiles();  // 加载视频文件
    }

    private void loadVideoFiles() {
        if (!hasPermissions()) {
            requestPermissions();
            return;
        }

        File downloadFolder = new File(Environment.getExternalStorageDirectory() + File.separator + "Download");
        if (downloadFolder.exists() && downloadFolder.isDirectory()) {
            File[] videoFiles = downloadFolder.listFiles((dir, name) -> name.endsWith(".mp4") || name.endsWith(".avi") || name.endsWith(".mkv"));
            if (videoFiles != null && videoFiles.length > 0) {
                videoList.clear();
                Collections.addAll(videoList, videoFiles);
                videoAdapter.notifyDataSetChanged();
            } else {
                Toast.makeText(getContext(), "未找到视频文件", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private boolean hasPermissions() {
        for (String permission : REQUIRED_PERMISSIONS) {
            if (ContextCompat.checkSelfPermission(getContext(), permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }

    private void requestPermissions() {
        requestPermissions(REQUIRED_PERMISSIONS, REQUEST_PERMISSION_CODE);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        if (requestCode == REQUEST_PERMISSION_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                loadVideoFiles();
            } else {
                Toast.makeText(getContext(), "无法访问存储,请授予权限", Toast.LENGTH_SHORT).show();
            }
        }
    }

    // 点击播放视频
    private void onVideoClick(File videoFile) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri videoUri = FileProvider.getUriForFile(requireContext(), getContext().getPackageName() + ".provider", videoFile);
        intent.setDataAndType(videoUri, "video/*");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivity(intent);
    }
}

这就是逻辑部分代码最后在

中添加视频音频

标签:视频,试题,void,binding,private,public,android,播放,App
From: https://blog.csdn.net/2401_83566316/article/details/142890878

相关文章

  • FreqFed: A Frequency Analysis-Based Approach for Mitigating Poisoning Attacks in
    FreqFed:AFrequencyAnalysis-BasedApproachforMitigatingPoisoningAttacksinFederatedLearning--FreqFed:一种基于频率分析的联邦学习中缓解中毒攻击的方法来源摘要威胁模型设计目标所用方法FreqFed总结思考来源NetworkandDistributedSystemSecurity......
  • JAVASE进阶面试题大总结
    ​ 面向对象1.解释一下什么是继承在编程领域,“继承”是面向对象编程中的一个重要概念。继承是指一个类(称为子类或派生类)可以从另一个类(称为父类或基类)获取属性和方法。通过继承,子类能够重用父类的代码和功能,同时还可以添加新的属性和方法,或者修改父类中已有的方法的实现,以......
  • 瑞芯微RK3566/RK3568 Android11使用OTA升级固件方法,深圳触觉智能鸿蒙开发板演示,备战第
    本文介绍瑞芯微RK3562/RK3568在Android11系统OTA(U盘/TF卡)升级固件方法,使用的是触觉智能的PurplePiOH鸿蒙开源主板,搭载了瑞芯微RK3566芯片,类树莓派设计,是Laval官方社区主荐的一款鸿蒙开发主板。1、OTA包生成在源码根目录上执行以下命令编译OTA包makeinstallclean#make-j4#......
  • 手机解锁方法:8个顶级的 Android 手机解锁软件
    一般来说,太简单的密码是不安全的,所以我们设置一个安全的密码,可能会稍微复杂一点。然而,我们可能经常会忘记复杂的密码并锁定我们的Android智能手机。8个顶级的Android手机解锁软件如果您遇到过这种情况并且正在寻找一种有效的方法来解锁您的Android设备而不丢失数据,您......
  • Android中的View绘制流程
    Android中的View绘制流程是一个复杂而精细的过程,它确保了应用程序中的用户界面能够准确、高效地呈现在用户眼前。以下将详细阐述AndroidView的绘制流程,包括测量(Measure)、布局(Layout)和绘制(Draw)三个核心阶段,以及一些相关的优化策略和回调方法。一、View绘制流程概述Android中......
  • 了解Android中的事件分发机制
    Android中的事件分发机制详解在Android开发中,事件分发机制是处理用户输入事件(如触摸、点击、滑动等)的核心部分。深入理解这一机制对于开发者来说至关重要,它有助于我们更好地处理用户输入,提升应用的交互体验。以下是对Android事件分发机制的详细解释,涵盖了事件的产生、传递与拦......
  • Top6 最好的 Android 数据恢复软件免费获取
    虽然在智能手机上随身携带您最喜爱的音乐收藏或珍贵的录音很方便,但如果您的设备出现技术问题或您不小心删除了文件,文件也有可能丢失。不管文件是如何删除或丢失的,丢失那些珍贵的音频文件的痛苦对每个人来说都是一样的。这就是我们创建本指南以帮助您摆脱这种不幸情况并取回音......
  • [Android] Handler 倒计时和界面更新
    问题在PlyRP中需要在界面上实时显示目前媒体的时长/剩余时长,TimeTask本身是一个子线程,但在Android的子线程去更新UI的内容,会导致不确定的异常。在非UI线程中刷新界面的时候,UI线程(或者其他非UI线程)也在刷新界面,这样就导致多个界面刷新的操作不能同步,导致线程......
  • AIGC产品经理面试,看这里!送你20道高频面试题及分析PDF文件!
    作者简介小6,世界五百强产品出身,从0到1搭建公司IT团队,现任深圳某互联网公司IT负责人,<极客时间>课程讲师。愿景:希望可以让你在这里从对产品经理的一无所知到至少能找份相关工作!聊点AI面试的,这两年最火的产业,分享20道AIGC产品经理高频面试题,文章后面会有送PDF文件的方式......
  • 多客最新游戏陪玩源码APP开源提供独特丝滑陪玩体验
    多客最新游戏陪玩源码APP开源提供独特丝滑陪玩体验系统基于TP6+Uni-app框架开发;客户移动端采用uni-app开发,管理后台TH6开发。系统支持微信公众号端、微信小程序端、H5端、PC端多端账号同步,可快速打包生成APP;优秀全面功能和独立完善个性功能,非常适合陪玩、技能服务等领域使用......