介绍:
每一次你需要在Android应用程序中选择一个SD卡目录,你都得加载一种目录选择器对话框,由它呈现图形界面以便你选择需要的目录。
不幸的是,正如开发人员所预料的,Android不提供任何内置的目录选择器对话框。因此开发人员必须自己写。本文将讲述以增强创建对话框的能力,为Android SD卡实现简单的目录选择器对话框。该流程包含在一个单独的文件中并且不用任何额外资源,除了预定义的Android资源。
执行代码
目录选择器对话框是基于AlertDialog,由子目录的ListView所供给。当前目录路径是显示在AlertDialog标题上。通过点击子目录ListView中的项目完成导航向前到一个目录,然后通过按住“返回”键返回。清单中的子目录是按名称分类的。当通过按住OK键选中需要的目录,一个“注册回调”被唤起,它由已选择目录的完整路径所供给。
代码包含在一个单独的文件DirectoryChooserDialog.java中。它以当前目录的子目录的ListView 加载AlertDialog 并保持追踪导航目录。
执行DirectoryChooserDialog 类定义如下回调界面。
// Callback interface for selected directory public interface ChosenDirectoryListener { public void onChosenDir(String chosenDir); }
一个回调可以被注册于DirectoryChooserDialog类构造函数中。
public DirectoryChooserDialog(Context context, ChosenDirectoryListener chosenDirectoryListener);
通过默认创建能力,新目录能够被运用(通过点击“新文件夹”按钮),它可以通过setNewFolderEnabled的方式关闭,当禁用新文件夹按钮隐藏时。
/////////////////////////////////////////////////////////////////////// // setNewFolderEnabled() - enable/disable new folder button /////////////////////////////////////////////////////////////////////// public void setNewFolderEnabled(boolean isNewFolderEnabled) { m_isNewFolderEnabled = isNewFolderEnabled; } public boolean getNewFolderEnabled() { return m_isNewFolderEnabled; }
DirectoryChooserDialog指定两个公共chooseDirectory 方式来加载目录选择器对话框。一个带有初始目录参数;另一个没有。默认初始目录是SD卡的根目录。
////////////////////////////////////////////////////////////////////// // chooseDirectory() - load directory chooser dialog for initial // default sdcard root directory ////////////////////////////////////////////////////////////////////// public void chooseDirectory(); //////////////////////////////////////////////////////////////////////////////// // chooseDirectory(String dir) - load directory chooser dialog for initial // input 'dir' directory //////////////////////////////////////////////////////////////////////////////// public void chooseDirectory(String dir);
DirectoryChooserDialog 类全面执行如下:
// DirectoryChooserDialog.java package com.example.directorychooser; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnClickListener; import android.content.DialogInterface.OnKeyListener; import android.os.Environment; import android.text.Editable; import android.view.Gravity; import android.view.KeyEvent; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.LayoutParams; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; public class DirectoryChooserDialog { private boolean m_isNewFolderEnabled = true; private String m_sdcardDirectory = ""; private Context m_context; private TextView m_titleView; private String m_dir = ""; private List<String> m_subdirs = null; private ChosenDirectoryListener m_chosenDirectoryListener = null; private ArrayAdapter<String> m_listAdapter = null; ////////////////////////////////////////////////////// // Callback interface for selected directory ////////////////////////////////////////////////////// public interface ChosenDirectoryListener { public void onChosenDir(String chosenDir); } public DirectoryChooserDialog(Context context, ChosenDirectoryListener chosenDirectoryListener) { m_context = context; m_sdcardDirectory = Environment.getExternalStorageDirectory().getAbsolutePath(); m_chosenDirectoryListener = chosenDirectoryListener; try { m_sdcardDirectory = new File(m_sdcardDirectory).getCanonicalPath(); } catch (IOException ioe) { } } /////////////////////////////////////////////////////////////////////// // setNewFolderEnabled() - enable/disable new folder button /////////////////////////////////////////////////////////////////////// public void setNewFolderEnabled(boolean isNewFolderEnabled) { m_isNewFolderEnabled = isNewFolderEnabled; } public boolean getNewFolderEnabled() { return m_isNewFolderEnabled; } /////////////////////////////////////////////////////////////////////// // chooseDirectory() - load directory chooser dialog for initial // default sdcard directory /////////////////////////////////////////////////////////////////////// public void chooseDirectory() { // Initial directory is sdcard directory chooseDirectory(m_sdcardDirectory); } //////////////////////////////////////////////////////////////////////////////// // chooseDirectory(String dir) - load directory chooser dialog for initial // input 'dir' directory //////////////////////////////////////////////////////////////////////////////// public void chooseDirectory(String dir) { File dirFile = new File(dir); if (! dirFile.exists() || ! dirFile.isDirectory()) { dir = m_sdcardDirectory; } try { dir = new File(dir).getCanonicalPath(); } catch (IOException ioe) { return; } m_dir = dir; m_subdirs = getDirectories(dir); class DirectoryOnClickListener implements DialogInterface.OnClickListener { public void onClick(DialogInterface dialog, int item) { // Navigate into the sub-directory m_dir += "/" + ((AlertDialog) dialog).getListView().getAdapter().getItem(item); updateDirectory(); } } AlertDialog.Builder dialogBuilder = createDirectoryChooserDialog(dir, m_subdirs, new DirectoryOnClickListener()); dialogBuilder.setPositiveButton("OK", new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // Current directory chosen if (m_chosenDirectoryListener != null) { // Call registered listener supplied with the chosen directory m_chosenDirectoryListener.onChosenDir(m_dir); } } }).setNegativeButton("Cancel", null); final AlertDialog dirsDialog = dialogBuilder.create(); dirsDialog.setOnKeyListener(new OnKeyListener() { @Override public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { // Back button pressed if ( m_dir.equals(m_sdcardDirectory) ) { // The very top level directory, do nothing return false; } else { // Navigate back to an upper directory m_dir = new File(m_dir).getParent(); updateDirectory(); } return true; } else { return false; } } }); // Show directory chooser dialog dirsDialog.show(); } private boolean createSubDir(String newDir) { File newDirFile = new File(newDir); if (! newDirFile.exists() ) { return newDirFile.mkdir(); } return false; } private List<String> getDirectories(String dir) { List<String> dirs = new ArrayList<String>(); try { File dirFile = new File(dir); if (! dirFile.exists() || ! dirFile.isDirectory()) { return dirs; } for (File file : dirFile.listFiles()) { if ( file.isDirectory() ) { dirs.add( file.getName() ); } } } catch (Exception e) { } Collections.sort(dirs, new Comparator<String>() { public int compare(String o1, String o2) { return o1.compareTo(o2); } }); return dirs; } private AlertDialog.Builder createDirectoryChooserDialog(String title, List<String> listItems, DialogInterface.OnClickListener onClickListener) { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(m_context); // Create custom view for AlertDialog title containing // current directory TextView and possible 'New folder' button. // Current directory TextView allows long directory path to be wrapped to multiple lines. LinearLayout titleLayout = new LinearLayout(m_context); titleLayout.setOrientation(LinearLayout.VERTICAL); m_titleView = new TextView(m_context); m_titleView.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); m_titleView.setTextAppearance(m_context, android.R.style.TextAppearance_Large); m_titleView.setTextColor( m_context.getResources().getColor(android.R.color.white) ); m_titleView.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL); m_titleView.setText(title); Button newDirButton = new Button(m_context); newDirButton.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); newDirButton.setText("New folder"); newDirButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { final EditText input = new EditText(m_context); // Show new folder name input dialog new AlertDialog.Builder(m_context). setTitle("New folder name"). setView(input).setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { Editable newDir = input.getText(); String newDirName = newDir.toString(); // Create new directory if ( createSubDir(m_dir + "/" + newDirName) ) { // Navigate into the new directory m_dir += "/" + newDirName; updateDirectory(); } else { Toast.makeText( m_context, "Failed to create '" + newDirName + "' folder", Toast.LENGTH_SHORT).show(); } } }).setNegativeButton("Cancel", null).show(); } }); if (! m_isNewFolderEnabled) { newDirButton.setVisibility(View.GONE); } titleLayout.addView(m_titleView); titleLayout.addView(newDirButton); dialogBuilder.setCustomTitle(titleLayout); m_listAdapter = createListAdapter(listItems); dialogBuilder.setSingleChoiceItems(m_listAdapter, -1, onClickListener); dialogBuilder.setCancelable(false); return dialogBuilder; } private void updateDirectory() { m_subdirs.clear(); m_subdirs.addAll( getDirectories(m_dir) ); m_titleView.setText(m_dir); m_listAdapter.notifyDataSetChanged(); } private ArrayAdapter<String> createListAdapter(List<String> items) { return new ArrayAdapter<String>(m_context, android.R.layout.select_dialog_item, android.R.id.text1, items) { @Override public View getView(int position, View convertView, ViewGroup parent) { View v = super.getView(position, convertView, parent); if (v instanceof TextView) { // Enable list item (directory) text wrapping TextView tv = (TextView) v; tv.getLayoutParams().height = LayoutParams.WRAP_CONTENT; tv.setEllipsize(null); } return v; } }; } }
用途实例
如下实例表现如何通过点击按钮加载目录选择器对话框。先前选中的目录变为调用下一个对话框的初始目录。
结论:
本文陈述了以创建新的目录来增强目录选择器对话框。