ディレクトリを指定するDialogPreference

Preference、設定画面で端末のディレクトリを指定するのに、EditTextPreference で入力させるのは、
ちょっとユーザにとっては辛い。

以下のようなダイアログで選択させたい。

f:id:posturan:20160313194531p:plain



DialogPreference で実現する。

package org.uran;
import java.io.File;
import java.io.FileFilter;
import java.util.ArrayList;
import java.util.List;
import org.uran.R;
import android.content.Context;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
/**
 * DirectorySelectPreference
 */

public class DirectorySelectPreference extends DialogPreference{
   static String mCurrentPath;
   static TextView mPathText;
   static ListView mListView;
   static List<File> mFileList = new ArrayList<File>();
   static String mRootPath;

   final static FileFilter dirGetFilter = new FileFilter(){
      @Override
      public boolean accept(File file){
         // 必要に応じてフィルタ条件を追加
         return file.isDirectory();
      }
   };
   final static Handler mHandler = new Handler(){
      @Override
      public void handleMessage(Message msg){
         String path = msg.obj.toString();
         removeMessages(0);
         mCurrentPath = path;
         mPathText.setText(path);
         mFileList.clear();
         if (path.equals(mRootPath)==false) mFileList.add(new File(path+"/../"));
         for(File file : new File(path).listFiles(
dirGetFilter)){
            mFileList.add(file);
         }
         *1.notifyDataSetChanged();
      }
   };

   public DirectorySelectPreference(Context context,AttributeSet attrs){
      super(context, attrs);
      mRootPath = Environment.getExternalStorageDirectory().getAbsolutePath();
   }

   @Override
   protected View onCreateDialogView(){
      LinearLayout layout = new LinearLayout(getContext());
      layout.setOrientation(LinearLayout.VERTICAL);


      mPathText = new TextView(getContext(),null, android.R.attr.textAppearanceMedium);
      //                   left, top, right, bottom
      mPathText.setPadding(10, 6, 2, 6);
      mPathText.setTextColor(0xff000000);
      mPathText.setBackgroundColor(0xffffffff);
      mPathText.setText(getPersistedString(mCurrentPath==null ? mRootPath : mCurrentPath));
      layout.addView(mPathText);

      mListView = new ListView(getContext());
      mListView.setChoiceMode(AbsListView.CHOICE_MODE_SINGLE);
      mListView.setAdapter(new ArrayAdapter<File>(getContext(), 0, mFileList){
         LayoutInflater inflater = (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
         @Override
         public View getView(int position,View convertView,ViewGroup parent){
            View view = convertView==null ? inflater.inflate(R.layout.directory_row, null) : convertView;
            TextView tv = (TextView)view.findViewById(R.id.fileTextView);
            File file = mFileList.get(position);
            tv.setText(file.getName());
            return view;
         }
      });
      mListView.setOnItemClickListener(new AdapterView.OnItemClickListener(){
         @Override
         public void onItemClick(AdapterView<?> parent, View view, int position, long id){
            // 選択後の処理
            BaseAdapter adapter = (BaseAdapter)parent.getAdapter();
            File file = (File)adapter.getItem(position);
            if (file.isDirectory()){
               if (file.canRead() && file.canWrite()){
                  String path = file.getAbsolutePath();
                  if (path.endsWith("/..")){
                     path = file.getParentFile().getParent();
                  }
                  Message message = Message.obtain();
                  message.obj = path;
                  message.arg1 = position;
                  mHandler.sendMessage(message);

               }else{
                  Toast.makeText(getContext(), "権限がありません", Toast.LENGTH_SHORT).show();
                  mListView.setItemChecked(position, false);
               }
            }
         }
      });
      layout.addView(mListView);
      mFileList.clear();
      String path = mCurrentPath==null ? getPersistedString(mRootPath) : mCurrentPath;
      if (path.equals(mRootPath)==false) mFileList.add(new File(path+"/../"));
      for(File f : new File(path).listFiles(dirGetFilter)){
         mFileList.add(f);
      }
      return layout;
   }
   @Override
   protected void onAttachedToActivity(){
      super.onAttachedToActivity();
      String path = getPersistedString(mRootPath);
      setSummary(path);

   }
   @Override
   protected void onDialogClosed(boolean positiveResult){
      if (positiveResult){
         persistString(mPathText.getText().toString());
         setSummary(mPathText.getText().toString());
      }

   }
}

これと、フォルダ、ファイルの画像と、以下、file_row.xml を用意する

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    android:orientation="horizontal" >
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/ic_menu_folder" />
    <TextView
        android:id="@+id/fileTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="#ffffff" />
</LinearLayout>

Preference の XMLは、以下のように書けばよい。

<org.uran.DirectorySelectPreference
            android:key="outdirectory"
            android:title="書込み先"
            android:dialogTitle="書込み先指定" >
</org.uran.DirectorySelectPreference>

*1:BaseAdapter)mListView.getAdapter(