在CSDN找了好几个目录选择器,都是用Dialog实现的。但都有一个巨大的问题,选择了半天,无法将选择的目录的String传回到调用者,因为没有太大意义。下面是我解决的历史思路。
1、用Activity实现,在调用Activity与被调用Activity之间传递参数,自然也可以传递选择的目录的String。这是可以实现的,也是我实现的第一个版本,比较简单(另文描述)。但还是不死心,就想通过Dialog来搞定。
2、通过StringBuffer等传递参数,不知为何失败,又因为不是OO的思路,失去兴趣。
3、通过AlertDialog的setPositiveButton来实现。但这个PositiveButton的处理代码只能在调用者的环境中,还是苦于无法传递回结果目录的字符串,又回到了问题本身。在这种情况下,反而不如Dialog更方便(因为在Dialog中可以任意添加Button,并处理Button的事件,而在AlertDialog,最好是顺着其设计目的来使用)。
4、将Dialog或AlertDialog的继承(或作为自定义类的成员变量)作为调用者的内部类,因为这个内部类可以方便地访问外部类的成员和方法,估计可以肯快就实现,但由于严重违反MVC编程风格,放弃。
5、通过向File、ContentProvider、SharedPreference一定也能实现(虽然有些同步呀、触发啦需要处理一下),不想这样做,放弃。
6、忽然有一天,发现Android3.0以上都不能直接访问HttpURLConnection,必须另起一个线程实现。受到启发,也另起一个线程,在这个线程中等待用户按下Button,修改mBtnOkIsPressed属性为true。一旦等到这个事件,获得选择的目录的String,再在Handler中主动关闭这个Dialog。比较复合OO思想,遂实现。
代码如下:
代码1:dialog_choose_dir.xml -- 这部分与网上其他博文里几乎一模一样。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="40dip"
android:orientation="horizontal" >
<Button
android:id="@+id/btn_home"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_gravity="left"
android:layout_weight="1"
android:text="@string/button_home" />
<LinearLayout
android:id="@+id/dir_layout"
android:layout_width="140dip"
android:layout_height="35dip"
android:layout_weight="1"
android:gravity="center"
android:orientation="horizontal" >
</LinearLayout>
<Button
android:id="@+id/btn_back"
android:layout_width="40dip"
android:layout_height="40dip"
android:layout_gravity="right"
android:layout_weight="1"
android:text="@string/button_back" />
</LinearLayout>
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<!-- 注意虽然这个Button在前面描述,但其位置却是在底部 -->
<Button
android:id="@+id/btn_ok"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="@string/button_ok" />
<ListView
android:id="@+id/list_dir"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_above="@id/btn_ok" />
</RelativeLayout>
</LinearLayout>
代码2:ChooseDirDialog.java
package com.acesage.android4try;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import android.app.Dialog;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.AdapterView.OnItemClickListener;
//public class ChooseDirDialog extends Dialog {//注意,继承方式会发生栈溢出错误!
public class ChooseDirDialog {
private Dialog mDialog; // 应采用成员变量形式
private Context mContext;
private ListView mListView;
ArrayAdapter<String> mAdapter;
ArrayList<String> mArrayList = new ArrayList<String>(); //不要用ArrayList,而用ArrayList<T>更安全
private String mPath; //选择的目录
private boolean btnOkIsPressed=false; //初始化时,btnOk没有按下,所以为false
public boolean getBtnOkIsPressed(){
return btnOkIsPressed;
}
private TextView titleTextView;
private EditText titleEditText;
private Button btnHome, btnBack, btnOk;
private LinearLayout titleLinearLayout;
private int mType = 1;
private String[] mFileType = null;
public final static int TypeOpen = 1;
public final static int TypeSave = 2;
public ChooseDirDialog(Context context, int type, String[] fileType) {
this.mContext = context;
this.mType = type;
this.mFileType = fileType;
mDialog = new Dialog(mContext); //在这里new并获得Dialog的实例
//填充界面并初始化各种控件
View view = LayoutInflater.from(mContext).inflate(
R.layout.dialog_choose_dir, null);
mDialog.setContentView(view);
mDialog.setTitle(R.string.title_choose_dir);
mPath = getRootDir();
mArrayList = (ArrayList<String>) getDirs(mPath);
mAdapter = new ArrayAdapter<String>(mContext,
android.R.layout.simple_list_item_1, mArrayList);
mListView = (ListView) view.findViewById(R.id.list_dir);
mListView.setAdapter(mAdapter);
mListView.setOnItemClickListener(lvLis);
btnHome = (Button) view.findViewById(R.id.btn_home);// Java多级目录的生成
btnHome.setOnClickListener(new HomeClickListener());
btnBack = (Button) view.findViewById(R.id.btn_back);
btnBack.setOnClickListener(new BackClickListener());
btnOk = (Button) view.findViewById(R.id.btn_ok);
btnOk.setOnClickListener(new OkClickListener());
titleLinearLayout = (LinearLayout) view.findViewById(R.id.dir_layout);
if (mType == TypeOpen) {
titleTextView = new TextView(mContext);
titleLinearLayout.addView(titleTextView);
titleTextView.setText(mPath);
} else if (mType == TypeSave) {
titleEditText = new EditText(mContext);
titleEditText.setWidth(240);
titleEditText.setHeight(70);
titleEditText.setGravity(Gravity.CENTER);
titleEditText.setPadding(0, 2, 0, 0);
titleLinearLayout.addView(titleEditText);
titleEditText.setText("FileName");
}
}
// 动态更新ListView
Runnable AddThread = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
mArrayList.clear();
// System.out.println("Runnable mPath:"+mPath);
// 必须得用这种方法为arr赋值才能更新
List<String> temp = getDirs(mPath);
for (int i = 0; i < temp.size(); i++)
mArrayList.add(temp.get(i));
mAdapter.notifyDataSetChanged();
}
};
private OnItemClickListener lvLis = new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
String temp = (String) arg0.getItemAtPosition(arg2);
// System.out.println("OnItemClick path1:"+mPath);
if (temp.equals(".."))
mPath = getFatherDir(mPath);
else if (mPath.equals("/"))
mPath = mPath + temp;
else
mPath = mPath + "/" + temp;
// System.out.println("OnItemClick path2"+mPath);
if (mType == TypeOpen) // 还应解决,如选择为文件,而非目录的情况:不应该做任何动作
titleTextView.setText(mPath);
// 还应解决,如为TypeSave,选择文件情况:应更新文件名
Handler handler = new Handler();
handler.post(AddThread);
}
};
private List<String> getDirs(String ipath) {
List<String> file = new ArrayList<String>();
File[] myFile = new File(ipath).listFiles();
if (myFile == null) {
file.add("..");
} else
for (File f : myFile) {
// 过滤目录
if (f.isDirectory()) {
String tempf = f.toString();
int pos = tempf.lastIndexOf("/");
String subTemp = tempf.substring(pos + 1, tempf.length());
// String subTemp =
// tempf.substring(mPath.length(),tempf.length());
file.add(subTemp);
// System.out.println("files in dir:"+subTemp);
}
// 过滤知道类型的文件
if (f.isFile() && mFileType != null) {
for (int i = 0; i < mFileType.length; i++) {
int typeStrLen = mFileType[i].length();
String fileName = f.getPath().substring(
f.getPath().length() - typeStrLen);
if (fileName.toLowerCase().equals(mFileType[i])) {
file.add(f.toString().substring(mPath.length() + 1,
f.toString().length()));
}
}
}
}
if (file.size() == 0)
file.add("..");
// System.out.println("file[0]:"+file.get(0)+" File size:"+file.size());
return file;
}
private String getSDPath() {
File sdDir = null;
boolean sdCardExist = Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED); // 判断sd卡是否存在
if (sdCardExist) {
sdDir = Environment.getExternalStorageDirectory();// 获取根目录
}
if (sdDir == null) {
return null;
}
return sdDir.toString();
}
private String getRootDir() {
String root = "/";
mPath = getSDPath();
if (mPath == null)
mPath = "/";
return root;
}
private String getFatherDir(String path) {
String subpath = null;
int pos = path.lastIndexOf("/");
if (pos == path.length()) {
path = path.substring(0, path.length() - 1);
pos = path.lastIndexOf("/");
}
subpath = path.substring(0, pos);
if (pos == 0)
subpath = path;
return subpath;
}
private final class HomeClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
mPath = getRootDir();
if (mType == TypeOpen)
titleTextView.setText(mPath);
Handler handler = new Handler();
handler.post(AddThread);
}
}
private final class BackClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
mPath = getFatherDir(mPath);
if (mType == TypeOpen)
titleTextView.setText(mPath);
Handler handler = new Handler();
handler.post(AddThread);
}
}
private final class OkClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
//仅仅修改btnOkIsPressed的状态,不尝试从这里传递任何参数给调用者,那是绝对不可能的(因为调用者对这个对象来讲,是不可见的)。
btnOkIsPressed=true;
}
}
public String getResultPath() {
String resultPath;
if (mType == TypeSave) {
resultPath = mPath + "/"
+ titleEditText.getEditableText().toString();
} else {
resultPath = mPath;
}
return resultPath;
}
public void show(){
mDialog.show();
}
public void dismiss(){
mDialog.dismiss();
}
}
代码片段3:(可以被放在某个Activity,也可以放在某个Fragment。这个案例是在一个Fragment,如要放在Activity中,要修改其中获得context的代码)(这个Fragment的布局及其简单,就不列出其xml文档了,至于02、04一类的编号,没有任何意义。那时因为当时做了好多调试,这里只摘出与本文有关的部分)
public class Fragment02 extends Fragment {
private EditText mMessage02;
private Button mButtonChoose04;
ChooseDirDialog mChooseDirDialog = null;
public final int MESSAGE_RESULT_PATH_IS_OK = 1;
private Handler mHandler = new Handler() { //处理由另起的线程返回的消息
@Override
public void handleMessage(Message msg) {
if (MESSAGE_RESULT_PATH_IS_OK == msg.what) {
String data = (String) msg.obj;
mMessage02.setText(data);
mChooseDirDialog.dismiss();
}
super.handleMessage(msg);
}
};
public Fragment02() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater
.inflate(R.layout.fragment_02, container, false);
return rootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mMessage02 = (EditText) this.getActivity().findViewById(
R.id.text_message_02);
mButtonChoose04 = (Button) this.getActivity().findViewById(
R.id.alert_choose_dir_04);
mButtonChoose04.setOnClickListener(new ButtonChoose04ClickListener());
}
private final class ButtonChoose04ClickListener implements
android.view.View.OnClickListener {
@Override
public void onClick(View v) {
mChooseDirDialog = new ChooseDirDialog(getActivity(),
ChooseDirDialog.TypeOpen, new String[] { "jpg" });
mChooseDirDialog.show();
new Thread(new TransferResultPathThread()).start();
}
}
public class TransferResultPathThread implements Runnable {
@Override
public void run() {
String resultStr = null;
try {
while (!mChooseDirDialog.getBtnOkIsPressed()) {
resultStr = mChooseDirDialog.getResultPath();
Thread.sleep(100);
}
Message message = Message.obtain(mHandler,
MESSAGE_RESULT_PATH_IS_OK, resultStr);
message.sendToTarget();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
JFileChooser fileChooser = new JFileChooser();
fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int returnVal = fileChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
// System.out.print(fileChooser.getSelectedFile().getAbsolutePath());
rif.setCurrentDirectoryAbsolutePath(fileChooser
.getSelectedFile().getAbsolutePath());
rif.openRecursiveImageFiles();
}