Android Studio中如何防止获取子文件夹内的视频文件?
问题:视频播放器展示指定文件夹时包含子文件夹内的文件
开发视频播放器应用时,遇到以下问题:访问存储中的文件夹时,会同时获取到其子文件夹内的文件。例如创建了“Folder 1”文件夹及其子文件夹“Folder 2”,打开“Folder 1”时,“Folder 2”内的视频文件也会被展示出来,请问该如何修复?
VideoFilesActivity
public class VideoFilesActivity extends AppCompatActivity implements SearchView.OnQueryTextListener { public static final String MY_PREF = "my pref"; RecyclerView recyclerView; private ArrayList<MediaFiles> videoFilesArrayList = new ArrayList<>(); static VideoFilesAdapter videoFilesAdapter; String folder_name; String sortOrder; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_video_files); folder_name = getIntent().getStringExtra("folderName"); getSupportActionBar().setTitle(folder_name); recyclerView = findViewById(R.id.videos_rv); SharedPreferences.Editor editor = getSharedPreferences(MY_PREF, MODE_PRIVATE).edit(); editor.putString("playlistFOlderName", folder_name); editor.apply(); showVideoFiles(); } private void showVideoFiles() { videoFilesArrayList = fetchMedia(folder_name); videoFilesAdapter = new VideoFilesAdapter(videoFilesArrayList, this); recyclerView.setAdapter(videoFilesAdapter); recyclerView.setLayoutManager(new LinearLayoutManager(this, RecyclerView.VERTICAL,false)); videoFilesAdapter.notifyDataSetChanged(); } private ArrayList<MediaFiles> fetchMedia(String folderName) { SharedPreferences preferences = getSharedPreferences(MY_PREF, MODE_PRIVATE); String sort_value = preferences.getString("sort", "abcd"); ArrayList<MediaFiles> videoFiles = new ArrayList<>(); Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; if(sort_value.equals("sortName")) { sortOrder = MediaStore.MediaColumns.DISPLAY_NAME+" ASC"; }else if (sort_value.equals("sortSize")){ sortOrder = MediaStore.MediaColumns.SIZE+" DESC"; } else if (sort_value.equals("sorDate")) { sortOrder = MediaStore.MediaColumns.DATE_ADDED + " DESC"; }else { sortOrder = MediaStore.Video.Media.DURATION+ " DESC"; } String selection = MediaStore.Video.Media.DATA+" like?"; String[] selectionArg = new String[]{"%"+folderName+"%"}; Cursor cursor = getContentResolver().query(uri, null, selection, selectionArg, null); if (cursor!=null && cursor.moveToNext()) { do { @SuppressLint("Range") String id = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media._ID)); @SuppressLint("Range") String title = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.TITLE)); @SuppressLint("Range") String displayName = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)); @SuppressLint("Range") String size = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.SIZE)); @SuppressLint("Range") String duration = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DURATION)); @SuppressLint("Range") String path = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA)); @SuppressLint("Range") String dateAdded = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATE_ADDED)); MediaFiles mediaFiles = new MediaFiles(id, title, displayName, size, duration, path, dateAdded); videoFiles.add(mediaFiles); }while (cursor.moveToNext()); } return videoFiles; } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.video_menu, menu); MenuItem menuItem = menu.findItem(R.id.search_video); SearchView searchView = (SearchView) menuItem.getActionView(); searchView.setOnQueryTextListener(this); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { SharedPreferences preferences = getSharedPreferences(MY_PREF, MODE_PRIVATE); SharedPreferences.Editor editor = preferences.edit(); int id = item.getItemId(); switch (id) { case R.id.refresh_videos: finish(); startActivity(getIntent()); break; case R.id.sort_by: AlertDialog.Builder alertdialog = new AlertDialog.Builder(this); alertdialog.setTitle("Sort By"); alertdialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { editor.apply(); finish(); startActivity(getIntent()); dialog.dismiss(); } }); String[] items = {"Name (A to Z)", "Size (Big to Small)", "Date (New to Old)", "Length (Log to Short)"}; alertdialog.setSingleChoiceItems(items, -1, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { switch ( which){ case 0: editor.putString("sort", "sortName"); break; case 1: editor.putString("sort", "sortSize"); break; case 2: editor.putString("sort", "sortDate"); break; case 3: editor.putString("sort", "sortLenght"); break; } } }); alertdialog.create().show(); break; } return super.onOptionsItemSelected(item); } @Override public boolean onQueryTextSubmit(String query) { return false; } @Override public boolean onQueryTextChange(String newText) { String inputs = newText.toLowerCase(); ArrayList<MediaFiles> mediaFiles = new ArrayList<>(); for (MediaFiles media : videoFilesArrayList){ if (media.getTitle().toLowerCase().contains(inputs)){ mediaFiles.add(media); } } return true; } }
VideoFilesAdapter
public class VideoFilesAdapter extends RecyclerView.Adapter<VideoFilesAdapter.ViewHolder> { private ArrayList<MediaFiles> videoList; private Context context; public VideoFilesAdapter(ArrayList<MediaFiles> videoList, Context context) { this.videoList = videoList; this.context = context; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(context).inflate(R.layout.video_item, parent, false); return new ViewHolder(view); } @Override public void onBindViewHolder(@NonNull ViewHolder holder, int position) { holder.videoName.setText(videoList.get(position).getDisplayName()); String size = videoList.get(position).getSize(); holder.videoSize.setText(android.text.format.Formatter.formatFileSize(context, Long.parseLong(size))); double milliseconds = Double.parseDouble(videoList.get(position).getDuration()); holder.videoDuration.setText(timeConversion((long) milliseconds)); Glide.with(context).load(new File(videoList.get(position).getPath())).into(holder.thumbnail); holder.menu_more.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Toast.makeText(context, "Menu More", Toast.LENGTH_SHORT).show(); } }); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Intent intent = new Intent(context, VideoPlayerActivity.class); context.startActivity(intent); } }); } @Override public int getItemCount() { return videoList.size(); } public class ViewHolder extends RecyclerView.ViewHolder { ImageView thumbnail, menu_more; TextView videoName, videoSize, videoDuration; public ViewHolder(@NonNull View itemView) { super(itemView); thumbnail = itemView.findViewById(R.id.thumbnail); menu_more = itemView.findViewById(R.id.vide_menu_more); videoName = itemView.findViewById(R.id.video_name); videoSize = itemView.findViewById(R.id.video_size); videoDuration = itemView.findViewById(R.id.video_duration); } } public String timeConversion(long value){ String videoTime; int duration = (int) (value); int hrs = (duration / 36000); int min = (duration/ 60000 ) % 60000; int sec = duration % 60000 / 1000; if (hrs > 0 ) { videoTime = String.format("%02d:%02d:%02d", hrs, min, sec); }else { videoTime = String.format("%02d:%02d", min, sec); } return videoTime; } }
修复方案
问题根源在于fetchMedia方法中使用了模糊匹配MediaStore.Video.Media.DATA like ?,参数为%folderName%,导致所有路径包含该文件夹名称的文件(包括子文件夹内的)都被查询出来。需要修改查询条件,精确匹配文件所在的父目录为目标文件夹。
修改后的fetchMedia方法如下:
private ArrayList<MediaFiles> fetchMedia(String folderName) { SharedPreferences preferences = getSharedPreferences(MY_PREF, MODE_PRIVATE); String sort_value = preferences.getString("sort", "abcd"); ArrayList<MediaFiles> videoFiles = new ArrayList<>(); Uri uri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; if(sort_value.equals("sortName")) { sortOrder = MediaStore.MediaColumns.DISPLAY_NAME+" ASC"; }else if (sort_value.equals("sortSize")){ sortOrder = MediaStore.MediaColumns.SIZE+" DESC"; } else if (sort_value.equals("sorDate")) { sortOrder = MediaStore.MediaColumns.DATE_ADDED + " DESC"; }else { sortOrder = MediaStore.Video.Media.DURATION+ " DESC"; } // 获取目标文件夹的绝对路径,避免名称重复导致的匹配错误 File targetFolder = new File(folderName); String folderPath = targetFolder.getAbsolutePath(); // 构建精确匹配条件:只取目标文件夹下的直接子文件,排除子文件夹内的文件 String selection = MediaStore.Video.Media.DATA + " LIKE ? AND " + MediaStore.Video.Media.DATA + " NOT LIKE ?"; String[] selectionArg = new String[]{ folderPath + "/%", folderPath + "/%/%" }; Cursor cursor = getContentResolver().query(uri, null, selection, selectionArg, sortOrder); if (cursor!=null) { while (cursor.moveToNext()) { @SuppressLint("Range") String id = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media._ID)); @SuppressLint("Range") String title = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.TITLE)); @SuppressLint("Range") String displayName = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)); @SuppressLint("Range") String size = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.SIZE)); @SuppressLint("Range") String duration = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DURATION)); @SuppressLint("Range") String path = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATA)); @SuppressLint("Range") String dateAdded = cursor.getString(cursor.getColumnIndex(MediaStore.Video.Media.DATE_ADDED)); MediaFiles mediaFiles = new MediaFiles(id, title, displayName, size, duration, path, dateAdded); videoFiles.add(mediaFiles); } cursor.close(); } return videoFiles; }
关键修改说明
- 绝对路径匹配:先获取目标文件夹的绝对路径,避免同名文件夹导致的误匹配
- 双重条件过滤:
- 第一个条件确保文件路径以
目标文件夹路径/开头,即文件在目标文件夹范围内 - 第二个条件排除路径中包含
目标文件夹路径//的情况,确保只取目标文件夹的直接子文件,不包含子文件夹内的内容
- 第一个条件确保文件路径以
- 资源优化:添加
cursor.close()避免内存泄漏,同时将do-while循环改为更严谨的while循环
内容的提问来源于stack exchange,提问作者Zahid Ashraf
相关产品推荐
相关产品推荐

