John Lombard granted permission to write a tutorial based on his FileBrowser-Tutorial here. Thank you
@His Source
Building an Android FileBrowser (list-based)
@His Source
Building an Android FileBrowser (list-based)
What is this: We create an really simple file browser using a ListView.
What this tutorial includes:
- Browse the FileSystem
- React on Clicks
- Sending Intents to the System (Open clicked files)
Difficulty: 1.75 of 5
( rumors say its just a: 1.74 of 5 )What it will look like:
Mode: ABSOLUTE

Mode: RELATIVE


Mode: RELATIVE

Description:
0. Create a new Project, you could use package names like the following...

1. Create a new layout called file_row.xml. It should look the same as below.
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- />
Parsed in 0.001 seconds, using GeSHi 1.0.8.4
2. Modify the onCreate() function in AndroidFileBrowser.java so that it reads as the following:
Using java Syntax Highlighting
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- // setContentView() gets called within the next line,
- // so we do not need it here.
- browseToRoot();
- }
Parsed in 0.030 seconds, using GeSHi 1.0.8.4
We added a call to the browseToRoot() method. We haven't written browseToRoot() yet, but we'll get there in a second.
3. Change the class that AndroidFileBrowser extends from "Activity" to "ListActivity", because our files/directories will be listed in a list. It should look like this:
Using java Syntax Highlighting
- public class FileList extends ListActivity {
Parsed in 0.030 seconds, using GeSHi 1.0.8.4
4. Create a private field, items, that holds the current directory and a list of the sub-Entries(Files/Dirs). This should go just under the "public class FileList" line. As we are planning to make Relative and Absolute Views available, we create a little enum and create a field of it. That part will look like the following:
Using java Syntax Highlighting
- //...
- public class AndroidFileBrowser extends ListActivity {
- private enum DISPLAYMODE{ ABSOLUTE, RELATIVE; }
- private final DISPLAYMODE displayMode = DISPLAYMODE.ABSOLUTE;
- private List<String> directoryEntries = new ArrayList<String>();
- private File currentDirectory = new File("/");
- //...
Parsed in 0.035 seconds, using GeSHi 1.0.8.4
5. Lets take a look at the browseToRoot()-Function we called in inCreate()...
As Android is Linux based, the filesystem-root-directory is called "/".
Using java Syntax Highlighting
- /**
- * This function browses to the
- * root-directory of the file-system.
- */
- private void browseToRoot() {
- browseTo(new File("/"));
- }
Parsed in 0.035 seconds, using GeSHi 1.0.8.4
6. Next is the browseTo-Function which determines, whether the passed parameter is a directory (calls "fill(aDirectory.listFiles());") or a File (calls "openFile(aDirectory);" if the user permitted.)
Using java Syntax Highlighting
- private void browseTo(final File aDirectory){
- if (aDirectory.isDirectory()){
- this.currentDirectory = aDirectory;
- fill(aDirectory.listFiles());
- }else{
- OnClickListener okButtonListener = new OnClickListener(){
- // @Override
- public void onClick(DialogInterface arg0, int arg1) {
- // Lets start an intent to View the file, that was clicked...
- openFile(aDirectory);
- }
- };
- OnClickListener cancelButtonListener = new OnClickListener(){
- // @Override
- public void onClick(DialogInterface arg0, int arg1) {
- // Do nothing
- }
- };
- AlertDialog.show(this,"Question", "Do you want to open that file?n"
- + aDirectory.getName(),
- "OK", okButtonListener,
- "Cancel", cancelButtonListener, false, null);
- }
- }
Parsed in 0.039 seconds, using GeSHi 1.0.8.4
7. The private fill() method takes a File[] array, and displays each file name on the screen in the list, including a switch on out Mode (Rel. or Abs.). It also displays two String for backward navigation (we define in strings.xml: 'up_one_level', ...)
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <string name="app_name">Android File Browser - by anddev.org</string>
- <string name="up_one_level">..</string>
- <string name="current_dir">.</string>
- </resources>
Parsed in 0.002 seconds, using GeSHi 1.0.8.4
Using java Syntax Highlighting
- private void fill(File[] files) {
- this.directoryEntries.clear();
- // Add the "." == "current directory"
- // and the ".." == 'Up one level'
- this.directoryEntries.add(getString(R.string.current_dir));
- if(this.currentDirectory.getParent() != null)
- this.directoryEntries.add(getString(R.string.up_one_level));
- switch(this.displayMode){
- case ABSOLUTE:
- for (File file : files){
- this.directoryEntries.add(file.getPath());
- }
- break;
- case RELATIVE: // On relative Mode, we have to add the current-path to the beginning
- int currentPathStringLenght = this.currentDirectory.getAbsolutePath().length();
- for (File file : files){
- this.directoryEntries.add(file.getAbsolutePath().substring(currentPathStringLenght));
- }
- break;
- }
Parsed in 0.038 seconds, using GeSHi 1.0.8.4
8. The lines in each switch-case take the files[] array and fill the class-field "this.directoryEntries" of the type ArrayList<String> with all the file's name. "this.directoryEntries" is a class field because it will be used later when the user clicks on one of the files or directories.
9. The last two lines (see below) create an ArrayAdapter<String> out of the items using the file_row layout. This is what marries the list of List of files to the list on the screen.
Using java Syntax Highlighting
- ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this,
- R.layout.file_row, this.directoryEntries);
- this.setListAdapter(directoryList);
Parsed in 0.035 seconds, using GeSHi 1.0.8.4
10. Hit Shift-Control-O to Organize Imports, from time to time...
11. Save all your files and run. You should have a list of directories on your emulated Android phone.
12. Click on them and nothing happens -- next step fixes that.
Navigating the Directory Tree:
Now that we can see the root directory structure, we somehow need to get 'into' the FileSystem...
1. When the user clicks on a list item, we have the opportunity to intercept that click and do something interesting with it. To that end we override the onListItemClick(...) method of ListActivity
1.1. The only thing we need to do now is switching, which item we have clicked ('current_dir', 'up_one_level', or another File/Directory)
Note: that using the "getString(R.string.<name>)" is preferable to including the string as a final field within your class (or even worse, just putting the string in-line). Moving all of your strings to the resource directory goes a long way toward internationalizing your code if you ever need to do so.
The Full Source:
res/layout/file_row.xml
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <TextView
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="fill_parent"
- android:layout_height="wrap_content"
- />
Parsed in 0.001 seconds, using GeSHi 1.0.8.4
res/values/strings.xml
Using xml Syntax Highlighting
- <?xml version="1.0" encoding="utf-8"?>
- <resources>
- <string name="app_name">Android File Browser - by anddev.org</string>
- <string name="up_one_level">..</string>
- <string name="current_dir">.</string>
- </resources>
Parsed in 0.002 seconds, using GeSHi 1.0.8.4
sry/your_package_strcuture/AndroidFileBrowser.java
Using java Syntax Highlighting
- package org.anddev.android.filebrowser;
- import java.io.File;
- import java.net.URISyntaxException;
- import java.util.ArrayList;
- import java.util.List;
- import android.app.AlertDialog;
- import android.app.ListActivity;
- import android.content.DialogInterface;
- import android.content.Intent;
- import android.content.DialogInterface.OnClickListener;
- import android.net.ContentURI;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.ArrayAdapter;
- import android.widget.ListView;
- public class AndroidFileBrowser extends ListActivity {
- private enum DISPLAYMODE{ ABSOLUTE, RELATIVE; }
- private final DISPLAYMODE displayMode = DISPLAYMODE.ABSOLUTE;
- private List<String> directoryEntries = new ArrayList<String>();
- private File currentDirectory = new File("/");
- /** Called when the activity is first created. */
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- // setContentView() gets called within the next line,
- // so we do not need it here.
- browseToRoot();
- }
- /**
- * This function browses to the
- * root-directory of the file-system.
- */
- private void browseToRoot() {
- browseTo(new File("/"));
- }
- /**
- * This function browses up one level
- * according to the field: currentDirectory
- */
- private void upOneLevel(){
- if(this.currentDirectory.getParent() != null)
- this.browseTo(this.currentDirectory.getParentFile());
- }
- private void browseTo(final File aDirectory){
- if (aDirectory.isDirectory()){
- this.currentDirectory = aDirectory;
- fill(aDirectory.listFiles());
- }else{
- OnClickListener okButtonListener = new OnClickListener(){
- // @Override
- public void onClick(DialogInterface arg0, int arg1) {
- try {
- // Lets start an intent to View the file, that was clicked...
- Intent myIntent = new Intent(android.content.Intent.VIEW_ACTION,
- new ContentURI("file://"
- + aDirectory.getAbsolutePath()));
- startActivity(myIntent);
- } catch (URISyntaxException e) {
- e.printStackTrace();
- }
- }
- };
- OnClickListener cancelButtonListener = new OnClickListener(){
- // @Override
- public void onClick(DialogInterface arg0, int arg1) {
- // Do nothing
- }
- };
- AlertDialog.show(this,"Question", "Do you want to open that file?n"
- + aDirectory.getName(),
- "OK", okButtonListener,
- "Cancel", cancelButtonListener, false, null);
- }
- }
- private void fill(File[] files) {
- this.directoryEntries.clear();
- // Add the "." and the ".." == 'Up one level'
- try {
- Thread.sleep(10);
- } catch (InterruptedException e1) {
- // TODO Auto-generated catch block
- e1.printStackTrace();
- }
- this.directoryEntries.add(".");
- if(this.currentDirectory.getParent() != null)
- this.directoryEntries.add("..");
- switch(this.displayMode){
- case ABSOLUTE:
- for (File file : files){
- this.directoryEntries.add(file.getPath());
- }
- break;
- case RELATIVE: // On relative Mode, we have to add the current-path to the beginning
- int currentPathStringLenght = this.currentDirectory.getAbsolutePath().length();
- for (File file : files){
- this.directoryEntries.add(file.getAbsolutePath().substring(currentPathStringLenght));
- }
- break;
- }
- ArrayAdapter<String> directoryList = new ArrayAdapter<String>(this,
- R.layout.file_row, this.directoryEntries);
- this.setListAdapter(directoryList);
- }
- @Override
- protected void onListItemClick(ListView l, View v, int position, long id) {
- int selectionRowID = (int) this.getSelectionRowID();
- String selectedFileString = this.directoryEntries.get(selectionRowID);
- if (selectedFileString.equals(".")) {
- // Refresh
- this.browseTo(this.currentDirectory);
- } else if(selectedFileString.equals("..")){
- this.upOneLevel();
- } else {
- File clickedFile = null;
- switch(this.displayMode){
- case RELATIVE:
- clickedFile = new File(this.currentDirectory.getAbsolutePath()
- + this.directoryEntries.get(selectionRowID));
- break;
- case ABSOLUTE:
- clickedFile = new File(this.directoryEntries.get(selectionRowID));
- break;
- }
- if(clickedFile != null)
- this.browseTo(clickedFile);
- }
- }
- }
Parsed in 0.054 seconds, using GeSHi 1.0.8.4
Regards,
plusminus








