Handler を使ってエクスプローラを作る

端末内のファイル、フォルダを探索するアプリを作る時、Handler を使えば、
Activity 1つで構成できる。

f:id:posturan:20160313225008p:plain



こんな画面を作るケース、

まずは、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:orientation="vertical" >
    <TextView
        android:id="@+id/currentPathTextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="10dp"
        android:paddingRight="10dp"
        android:paddingTop="2dp"
        android:paddingBottom="2dp"
        android:text="Medium Text"
        android:background="#ffffff"
        android:textColor="#000000"
        android:textAppearance="?android:attr/textAppearanceMedium" />
    <ListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1" >
    </ListView>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp" >
        <Button
            android:id="@+id/backButton"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="BACK" />
    </LinearLayout>
</LinearLayout>

1行のレイアウトは、ここでは省略

Activity は、以下のとおり。。。

public class SdCardExplorerActivity extends Activity{
   TextView mCurrentPathTextView;
   ListView mListView;
   List<File> mFileList;
   Stack<String> mHistory;
   String mRootPath;

   Handler mHandler = new Handler(){
      @Override
      public void handleMessage(Message msg){

         String path = msg.obj.toString();
         removeMessages(0);
         mCurrentPathTextView.setText(path);
         mFileList.clear();
         if (path.equals(mRootPath)==false) mFileList.add(new File(path+"/../"));
         for(File file : new File(path).listFiles()){
            mFileList.add(file);
         }

         *1.notifyDataSetChanged();
      }
   };


   @Override
   protected void onCreate(Bundle savedInstanceState){
      super.onCreate(savedInstanceState);
      requestWindowFeature(Window.FEATURE_NO_TITLE);
      getWindow().setSoftInputMode(
         android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN
      );
      setContentView(R.layout.sd_explorer);

      mFileList = new ArrayList<File>();
      mHistory = new Stack<String>();


      mCurrentPathTextView = (TextView)findViewById(R.id.currentPathTextView);
      mListView = (ListView)findViewById(R.id.listView);
      mListView.setAdapter(new BaseAdapter(){
         LayoutInflater inflater = (LayoutInflater)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;
            final File file = mFileList.get(position);
            ImageView imageView = (ImageView)view.findViewById(R.id.imageView);
            imageView.setImageResource(file.isDirectory() ? R.drawable.ic_menu_folder : R.drawable.ic_menu_file);
            final TextView textView = (TextView)view.findViewById(R.id.fileTextView);
            textView.setText(file.getName());
            try{
            textView.setTextColor(ColorStateList.createFromXml(getResources(), getResources().getXml(R.color.text_selector)));
            }catch(Exception e){
               Log.e("sample",e.getMessage(),e);
            }
            view.setOnClickListener(new View.OnClickListener(){
               @Override
               public void onClick(View v){
                  textView.setSelected(false);
                  if (file.isDirectory()){
                     if (file.canRead()){
                        String path = file.getAbsolutePath();
                        if (path.endsWith("/..")){
                           path = file.getParentFile().getParent();
                        }
                        mHistory.add(mCurrentPathTextView.getText().toString());
                        Message message = Message.obtain();
                        message.obj = path;

                        mHandler.sendMessage(message);
                     }else{
                        Toast.makeText(getApplicationContext(),"読込み権限がありません",Toast.LENGTH_SHORT).show();
                     }
                  }
               }
            });
            view.setOnTouchListener(new View.OnTouchListener(){
               @Override
               public boolean onTouch(View v, MotionEvent event){
                  int action = event.getAction();
                  if (action==MotionEvent.ACTION_DOWN || action==MotionEvent.ACTION_MOVE){
                     textView.setSelected(true);
                  }else{
                     textView.setSelected(false);
                  }
                  return false;
               }
            });
            view.setBackgroundResource(R.color.list_selector);
            return view;
         }
         @Override
         public long getItemId(int position){
            return position;
         }
         @Override
         public Object getItem(int position){
            return mFileList.get(position);
         }
         @Override
         public int getCount(){
            return mFileList.size();
         }
      });
      *2.setOnClickListener(new View.OnClickListener(){
         @Override
         public void onClick(View v){
            if (mHistory.isEmpty()){
               finish();
            }else{
               Message message = Message.obtain();
               message.obj
 = mHistory.pop();

               mHandler.sendMessage(message);
            }
         }
      });
   }
   @Override
   protected void onResume(){
      super.onResume();
      String sdPath = getSdPath();
      if (sdPath != null){
         mRootPath = sdPath;
         mCurrentPathTextView.setText(sdPath);
         for(File f : new File(sdPath).listFiles()){
            mFileList.add(f);
         }
         *3.notifyDataSetChanged();

      }else{
         new AlertDialog.Builder(this).setTitle("Error").setIcon(android.R.drawable.ic_dialog_alert)
         .setMessage("SD card No found!").setPositiveButton("OK",new DialogInterface.OnClickListener(){
            @Override
            public void onClick(DialogInterface dialog,int which){
            }
         }).create().show();
      }
   }
   /* @see android.app.Activity#dispatchKeyEvent(android.view.KeyEvent) */
   @Override
   public boolean dispatchKeyEvent(KeyEvent event){
      if (event.getKeyCode()==KeyEvent.KEYCODE_BACK && event.getAction()==KeyEvent.ACTION_DOWN){
         if (mHistory.isEmpty()){
            finish();
         }else{
            Message message = Message.obtain();
            message.obj
 = mHistory.pop();

            mHandler.sendMessage(message);
         }
         return true;
      }
      return super.dispatchKeyEvent(event);
   }
   /*
    * SDカード path、mount されてなければ、null を返す
    */

   private String getSdPath(){
      File f = new File(System.getenv("EXTERNAL_STORAGE"));
      if (!f.exists()) return null;
      if (!f.isDirectory()) return null;
      if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) return null;
      try{
      if (f.listFiles().length > 0){
         return f.getAbsolutePath();
      }
      }catch(Throwable e){
          Log.e("sample",e.getMessage(),e);
      }
      return null;
   }
}

*1:BaseAdapter)mListView.getAdapter(

*2:Button)findViewById(R.id.backButton

*3:BaseAdapter)mListView.getAdapter(