Tuesday, September 24, 2013

File Chooser

In this post, you learn to implement a File Chooser in Android. Some apps such as text viewer, text editor, and other apps that allow the user to select a file from the Android to open should provide a dialog that the user can select the file easily. In Java, we have JFileChooser and in VB.NET or C# we have OpenFileDialog. However, in Android, i have not found a file chooser library to do such the same thing.
To begin, please open the Eclipse and create a new Android project called FileChooser. In this File Chooser implementation, i use the AlertDialog class to show a file chooser dialog. On the dialog, there are one ListView, and two Buttons. The ListView displays the files and directories. The first Button takes the user back up one level of the directories structure. The second Button is pushed to close the dialog.

FileChooser for Android



You no need to create a layout file for the dialog to show the ListView, and the two Buttons as these components will be added to the dialog by code in AndFileChooser.java file.
Each item of the ListView contains both icon and text. If the item represents the directory, the icon will be directory icon. Otherwise, it is the file icon. In default, the ListView provided by Android is simple ListView that its items show only text. To customize the ListView to display both icon and text, first to do is changing the layout file that is applied to each item of the ListView. The layout file in this File Chooser implementation is called listlayout.xml in layout directory. Its content is shown below.

<?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="wrap_content"
    android:orientation="horizontal"
    android:padding="5dip"
    >


<ImageView
    android:id="@+id/icon"
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:padding="5sp"
    android:contentDescription="iconimage"
 />

<TextView
    android:id="@+id/label"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="10sp"
        android:textSize="20sp"
        android:textColor="#0000ff"
        android:textStyle="bold" >
</TextView>
</LinearLayout>


As you see in the listlayout.xml file, there are two components in this layout. First one is the ImageView to display the icon of an item. The second component is TextView to display the text of the item.
You should note that this layout is not the layout of the ListView itselft. Instead, it is the layout of an item of the ListView. The ListView will be defined in code (int AndFileChooser.java).

Another layout file that you need to create is called selection_style.xml. This layout is applied to the selected item of the list. The selection_style.xml file is stored in the drawable directory. In this directory, you also need two small images--directory and file images. These images represent the directory and file icons that show on the ListView (see the picture above).

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
 
    <item>
        <shape>
            <gradient
                android:startColor="#dfdfdf"
                android:endColor="#dfdfdf"            
                android:angle="180" />
        </shape>      
    </item>
</selector>

Now take a look at the AndFileChooser. java file. This file contains a class called AndFileChooser. The AndroidFileChooser class can be used anywhere in your app to display the file chooser dialog.

AndFileChooser.java

package com.example.filechooser;
import java.io.File;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class AndFileChooser {
private int RESULT=0;
private static final int RESULT_OK=1;
private String path;
private Context context;
private  String att_path;
private ListView l;

AndFileChooser(String path, Context context){
this.path=path;
this.context=context;
}

public void show(){
//create a dialog to show a list of files and directories
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setCancelable(true);
builder.setPositiveButton("OK", new OkOnClickListener());
builder.setNeutralButton("Up",null);
builder.setView(getList(path));
AlertDialog dialog = builder.create();      
dialog.show();
//override the default dialog default behavior when the Up button is pressed
dialog.getButton(Dialog.BUTTON_NEUTRAL).setOnClickListener(new View.OnClickListener() {

public void onClick(View arg0) {
upOneLevel();
}
});
}

private void upOneLevel(){
    //go back on directory
    path=path.substring(0, path.lastIndexOf("/"));
if(path.length()<=0)
path="/";
listFiles();
   }
   private final class OkOnClickListener implements DialogInterface.OnClickListener {
    public void onClick(DialogInterface dialog, int which) {
    RESULT=RESULT_OK;
    dialog.cancel();//close the dialog    
    }
   }
 
   private ListView getList(String path){  
    l=new ListView(context); //create new list object
    l.setBackgroundColor(Color.BLACK); //apply background to the ListView
    listFiles(); //add files and directories to the list for the first time
    l.setOnItemClickListener(new ItemList()); //register item click event
    return l;
   }  
 
 
   public void listFiles(){
   
    try{
    File f=new File(path);    
    if(f.isDirectory()){ //Check whether the path is a directory
    String[] contents=f.list();
    if(contents.length>0){ //list contents of the directory
    //ArrayAdapter<String> aa=new ArrayAdapter<String>(context,R.layout.listlayout,contents);
    ListAdapterModel aa=new ListAdapterModel(context,R.layout.listlayout,R.id.label,contents,path);
    l.setAdapter(aa);
    }
    else
    path=f.getParent(); //keep its parent directory for empty sub-directory
    //Toast.makeText(context,att_path, Toast.LENGTH_SHORT).show();
    }
    else if(f.isFile()){ //Otherwise, get the full path of the file and keep parent directories of the file.
    //This file will be attached to the email message
    att_path=f.getPath();
    //path=att_path.substring(0, att_path.lastIndexOf("/"));
    path=f.getParent();
    Toast.makeText(context,att_path, Toast.LENGTH_SHORT).show();
    }
    }catch(Exception e){e.printStackTrace();}  
   }
 
   private class ItemList implements OnItemClickListener{
   
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
     
    l.setSelector(R.drawable.selection_style); //apply background style to the selected item
    ViewGroup vg=(ViewGroup)view;
      String selectedItem = ((TextView) vg.findViewById(R.id.label)).getText().toString();
    path=path+"/"+selectedItem; //append the item to path
           listFiles();  //update files and directories of the list when a file or directory
       
      }
   }
 
   public String getFilePath(){
    if(RESULT==RESULT_OK)
    return att_path;
    else return null;
   }
}


The AndFileChooser class has the show method to display the file chooser dialog. In the show method, the Builder class that is a sub-class of the AlertDialog is used to create a dialog. To add an OK button to the dialog, you can use the setPositiveButton method of the Builder class. You will need to specify the text to display on the button (OK), and the click listener to this method. The OkOnClickListener class defines the click listener for the OK button.

private final class OkOnClickListener implements DialogInterface.OnClickListener {
    public void onClick(DialogInterface dialog, int which) {
       RESULT=RESULT_OK; //set the RESULT to RESULT_OK
       dialog.cancel();//close the dialog
     }
}


The click listener registers the OK button with the click event and performs action when the button is clicked. We also need one more button (Up) to take the user back up one level of the directories structure. The setNeutralButton is called to define this button. In Android, the dialog closes immediately when the user clicks any button on the dialog. However in the File Chooser app, when the user clicks the Up button, the dialog must not close. Instead, it takes the user back up one level. To do what we want, we need to change this default behavior by setting null to its click listener when it is defined and later overriding its setOnClickListener method.

To display the ListView on the dialog, you need to use the setView method of the Builder. In this app, our ListView object is defined in the getList(String path) method. This method returns a ListView object with items. Its items are files and directories of the parent directory specified by the path argument. In the getList method, the listFiles method is called to retrieve the contents (files and directories) of the input path and show them on the list. When the user selects an directory item of the ListView, the contents of the directory will be listed (overriding the previous contents of the ListView). To perform this action, you need to register the ListView with the item click listener by using the setOnItemClickListener method. The ItemList class defines the item click listener for the ListView.

private class ItemList implements OnItemClickListener{

public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

    l.setSelector(R.drawable.selection_style); //apply background style to the selected item
    ViewGroup vg=(ViewGroup)view;
    String selectedItem = ((TextView) vg.findViewById(R.id.label)).getText().toString();
    path=path+"/"+selectedItem; //append the item to path
    listFiles(); //update files and directories of the list when a file or directory

    }
}
In the onItemClick method, the selection style is applied to the selected item of the ListView by using the setSelector method. You need to pass the layout that applies the selection style to the selected item (selection_style.xml). When an item of the ListView is selected, you can access this item by using the view argument. As you already know, an item of the ListView contains two parts--image and text. Each part is represented by different component (ImageView and TextView), So you need to cast the view to a ViewGroup object. Within the ViewGroup object, you can refer to any component that it contains.

The path variable is updated to reflect the current path of the selected file or directory. The listFiles method is called again to update the contents of the ListView.
In the listFiles method, a File object is created to point to the current path so you can determine that the current path is directory or file. If it is a directory, its contents will be listed. Otherwise, the current file path is captured and the path variable is updated to its parent directory. The selected path of the file can be accessed by using the getPath method.

In this method, you also see a class called ListAdapterModel. The ListAdapterModel is a class that extends the ArrayAdapter class. As you already know from my previous post (Web Downloader), ArrayAdapter is used as data source of the ListView. If an item of the ListView has only one text component, there is no need to extend the ArrayAdapter. However, in this app, an item of the ListView contains two parts--image and text. So, the ArrayAdapter has to be extended. To enable the item to display both image and text, you need to override the getView method of the ArrayAdapter class. Below is the content of the ListAdapterModel.java file.

ListAdapterModel.java

package com.example.filechooser;
import java.io.File;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

public class ListAdapterModel extends ArrayAdapter<String>{
int groupid;
String[] names;
Context context;
String path;
public ListAdapterModel(Context context, int vg, int id, String[] names, String parentPath){
super(context,vg, id, names);
this.context=context;
groupid=vg;
this.names=names;
this.path=parentPath;
}
public View getView(int position, View convertView, ViewGroup parent) {

        LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        View itemView = inflater.inflate(groupid, parent, false);
        ImageView imageView = (ImageView) itemView.findViewById(R.id.icon);
        TextView textView = (TextView) itemView.findViewById(R.id.label);
        String item=names[position];
        textView.setText(item);
        File f=new File(path+"/"+item);
        if(f.isDirectory())
        imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.diricon));
        else
        imageView.setImageDrawable(context.getResources().getDrawable(R.drawable.fileicon));
        return itemView;
     
}

}


Now, you have set up the FileChooser dialog. It is ready to use in your activity. For testing, in the onStart method of the MainActivity class, you can create an instance of the AndFileChooser class and call its show method to display the dialog.

MainActivity.java file

package com.example.filechooser;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.Toast;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    protected void onStart(){
    super.onStart();
    AndFileChooser filechooser=new AndFileChooser("/",this);
    filechooser.show();
   
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
}


Don't forget to run the app, otherwise the file chooser does not display.


FileChooser run on Emulator

Download apk file of the FileChooser app

No comments:

Post a Comment