Android: 可用的简单目录选择器对话框, 可创建新文件夹






4.90/5 (20投票s)
Android:一个开箱即用的简单目录选择对话框,支持创建新文件夹,采用单个 Java 类实现。
介绍
每次需要在 Android 应用程序中选择某个 SD 卡目录时,都需要加载一种目录选择对话框,以便提供图形界面来选择所需的目录。
不幸的是,Android 没有提供开发者期望的内置目录选择对话框。因此,开发者必须编写自己的目录选择对话框。 在本文中,我介绍了一个简单的 Android SD 卡目录选择对话框的实现,并增强了创建目录的功能。 该实现包含在一个文件中,并且除了预定义的 Android 资源之外,不使用任何额外的资源。 这使得该对话框非常容易集成到 Android 应用程序中。
实现代码
该目录选择对话框基于 AlertDialog
,并带有子目录的 ListView
。 当前目录路径显示在 AlertDialog
标题中。 通过点击 ListView 中的子目录项可以导航到目录,通过按下返回按钮可以返回。 列表中的子目录按名称排序。 当通过按下确定按钮选择所需的目录时,将调用注册的回调函数,并提供所选目录的完整路径。 请注意,较长的当前目录路径和子目录名称都以多行视图正确显示。
代码包含在一个名为 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;
}
};
}
}
使用示例:
以下示例演示了如何在按钮点击时加载目录选择对话框。 创建新目录的功能在对话框出现之间切换。 之前选择的目录变为下一次对话框调用的初始目录。
// DirectoryChooserActivity.java
package com.example.directorychooser;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
public class DirectoryChooserActivity extends Activity
{
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_directory_chooser);
Button dirChooserButton = (Button) findViewById(R.id.chooseDirButton);
dirChooserButton.setOnClickListener(new OnClickListener()
{
private String m_chosenDir = "";
private boolean m_newFolderEnabled = true;
@Override
public void onClick(View v)
{
// Create DirectoryChooserDialog and register a callback
DirectoryChooserDialog directoryChooserDialog =
new DirectoryChooserDialog(DirectoryChooserActivity.this,
new DirectoryChooserDialog.ChosenDirectoryListener()
{
@Override
public void onChosenDir(String chosenDir)
{
m_chosenDir = chosenDir;
Toast.makeText(
DirectoryChooserActivity.this, "Chosen directory: " +
chosenDir, Toast.LENGTH_LONG).show();
}
});
// Toggle new folder button enabling
directoryChooserDialog.setNewFolderEnabled(m_newFolderEnabled);
// Load directory chooser dialog for initial 'm_chosenDir' directory.
// The registered callback will be called upon final directory selection.
directoryChooserDialog.chooseDirectory(m_chosenDir);
m_newFolderEnabled = ! m_newFolderEnabled;
}
});
}
}
结论
本文介绍了一个增强了新目录创建功能的目录选择对话框的 Java 类实现,可以直接在 Android 应用程序中使用。 享受吧!