-->

How to recreate a ListView in DialogFragment

2019-09-06 20:09发布

问题:

I'm writing a DialogFragment for browsing the filesystem, which works really nice by now. I just got one Problem.

The files are shown in an ListView, and when the user selects a file, this event is send to the Activity that has called the Fragment over an OnFileSelectedListener-Interface. This is fine for files, but it feels wrong to send out the directory names to the activity, then destroying and recreating the Fragment, when all that should happen is that the Fragment should show a new Directory. It also makes the whole Fragement disapearing and then reapearing which isn't really nice and smooth.

Furthermore every Activity using the Fragment has to use the logic for recreating the Fragment, which is far from "don't repeat yourself".

So, in short, is there a way to do a changeout of the Listview within the Fragment? Calling the AlertDialog.Builder more than once sadly doesn't work.

Heres my DialogFragment. I hope it's ok to post the whole thing:

package de.fusionsystems.firmenlaufmonitor;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class FileChooserFragment extends DialogFragment {
    private OnFileSelectedListener mCallback;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        return createDialog();
    }

    private AlertDialog createDialog(){
        // Create the AlertDialog object and return it
        SharedPreferences options = PreferenceManager.getDefaultSharedPreferences(getActivity());
        ArrayList<File> files = getFilesInDir(options.getString("BaseDir", ""));
        ArrayList<ListEntry> fileEntries = new ArrayList<ListEntry>();

        if (!isBaseDir(options.getString("BaseDir", ""))){
            fileEntries.add(new ListEntry("..", getResources().getDrawable( R.drawable.ic_folder)));
        }

        for (File file : files){            
            if (file.isDirectory()){
                fileEntries.add(new ListEntry(file.getName(),getResources().getDrawable(R.drawable.ic_folder)));
            }else{
                if (file.getName().endsWith(".kml")){
                    fileEntries.add(new ListEntry(file.getName(),getResources().getDrawable( R.drawable.ic_file)));
                }
            }   
        }

        final FileAdapter adapter = new FileAdapter(getActivity(), fileEntries);
        OnClickListener clickListener = new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String path;
                SharedPreferences options = PreferenceManager.getDefaultSharedPreferences(getActivity());
                if (adapter.getItem(which).name.equals("..")){
                    //navigate back
                    path = options.getString("BaseDir", "/");
                    path=path.substring(0, path.length());
                    path=path.substring(0,path.lastIndexOf("/"));
                    path = !path.equals("")?path:("/");
                }else {
                    path = options.getString("BaseDir", "");
                    path += ((path.equals("/"))?(""):("/"))+adapter.getItem(which).name;
                }
                Log.d("Path", path);
                Editor editor = options.edit();
                File dirTest = new File(path);
                if (dirTest.isDirectory()){
                    editor.putString("BaseDir", path);
                    editor.commit();
                    //mCallback.onFileSelected("");
                    //createDialog();
                    //*******************DO THE RIGHT THING HERE***********************
                }else{
                    mCallback.onFileSelected(path);
                }
            }
        };
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setAdapter(adapter, clickListener)
               .setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                       dismiss();
                   }
               });
        builder.setTitle("Datei wählen");
        return builder.create();
        }

    private ArrayList<File> getFilesInDir(String dir) {
        File folder = new File(dir);
        if (!folder.exists()){
            folder = new File("/");
            if (!folder.exists()){
                Log.e("FileBrowser","Something's really fishy");
            }
        }
        ArrayList<File> fileList = new ArrayList<File>(Arrays.asList(folder.listFiles()));
        return fileList;
    }

    private boolean isBaseDir(String dir) {
        File folder = new File(dir);
            if (!folder.exists()){
                folder = new File("/");
                if (!folder.exists()){
                    Log.e("FileBrowser","Something's really fishy");
                }
            }
        File baseDir = new File("/");
        if (folder.equals(baseDir)){
            return true;
        }else{
            return false;
        }
    }

    // Container Activity must implement this interface
    public interface OnFileSelectedListener {
        public void onFileSelected(String file);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try { 
            mCallback = (OnFileSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnHeadlineSelectedListener");
        }
    }

    class ListEntry {
        public String name;
        public Drawable item ;

        public ListEntry(String name, Drawable item) {
           this.name = name;
           this.item = item;
        }
    }

    class FileAdapter extends ArrayAdapter<ListEntry>{

        public FileAdapter(Context context, ArrayList<ListEntry> fileEntry) {
            super(context, R.layout.filechooser_list_item,fileEntry);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent){
                ListEntry entry = getItem(position);    
               // Check if an existing view is being reused, otherwise inflate the view
               if (convertView == null) {
                  convertView = LayoutInflater.from(getContext()).inflate(R.layout.filechooser_list_item, parent, false);
               }
               // Lookup view for data population
               TextView filechooserEntry = (TextView) convertView.findViewById(R.id.filechooser_entry);
               // Populate the data into the template view using the data object
               filechooserEntry.setText(entry.name);
               filechooserEntry.setCompoundDrawablesWithIntrinsicBounds(entry.item, null, null, null);
               // Return the completed view to render on screen
               return convertView;
        }   
    }
}

回答1:

Here's my solution for a Filebrowser as a DialogFragment. It turns out there are methods to add() remove() and clean() items to the adapter, so the answer to the initial question was real simple. The tricky part was to prevent the Dialog from closing after selecting a List item. This answer helped a lot: https://stackoverflow.com/a/15619098/3960095. Here's my working code for future visitors:

package de.yourCompany.yourProject;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class FileChooserFragment extends DialogFragment{
    private OnFileSelectedListener mCallback;

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
         // Create the AlertDialog object and return it
        final FileAdapter adapter = new FileAdapter(getActivity(), new ArrayList<ListEntry>());             
        adapter.getFiles();     
        OnClickListener clickListener = new OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
            //do nothing here to prevent dismiss after click    
            }
        };
        // Use the Builder class for convenient dialog construction
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setAdapter(adapter, clickListener)
               .setNegativeButton("Abbrechen", new DialogInterface.OnClickListener() {
                   public void onClick(DialogInterface dialog, int id) {
                   }
               });
        builder.setTitle("Datei wählen");
        final AlertDialog theDialog = builder.show();
        theDialog.getListView().setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                String path;
                SharedPreferences options = PreferenceManager.getDefaultSharedPreferences(getActivity());
                if (adapter.getItem(position).name.equals("..")){
                    //navigate back
                    path = options.getString("BaseDir", "/");
                    path=path.substring(0, path.length());
                    path=path.substring(0,path.lastIndexOf("/"));
                    path = !path.equals("")?path:("/");
                }else {
                    //get the Slashes right and navigate forward
                    path = options.getString("BaseDir", "");
                    path += ((path.equals("/"))?(""):("/"))+adapter.getItem(position).name;
                }
                Editor editor = options.edit();
                File dirTest = new File(path);
                if (dirTest.isDirectory()){
                    editor.putString("BaseDir", path);
                    editor.commit();
                    adapter.clear();
                    adapter.getFiles();
                }else{
                    mCallback.onFileSelected(path);
                    theDialog.dismiss();
                }

            }
        });
        return theDialog;
    }

    private boolean isBaseDir(String dir) {
        File folder = new File(dir);
            if (!folder.exists()){
                folder = new File("/");
                if (!folder.exists()){
                    Log.wtf("FileBrowser","Something's really fishy");
                }
            }
        File baseDir = new File("/");
        if (folder.equals(baseDir)){
            return true;
        }else{
            return false;
        }
    }

    // Container Activity must implement this interface
    public interface OnFileSelectedListener {
        public void onFileSelected(String file);
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        // This makes sure that the container activity has implemented
        // the callback interface. If not, it throws an exception
        try { 
            mCallback = (OnFileSelectedListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnHeadlineSelectedListener");
        }
    }

    class ListEntry {
        public String name;
        public Drawable item ;

        public ListEntry(String name, Drawable item) {
           this.name = name;
           this.item = item;
        }
    }

    class FileAdapter extends ArrayAdapter<ListEntry>{

        //show only files with the suffix FILE_SUFFIX, use "*" to show all files;
        private static final String FILE_SUFFIX = ".kml";

        public FileAdapter(Context context, ArrayList<ListEntry> fileEntry) {
            super(context, R.layout.filechooser_list_item,fileEntry);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent){
                ListEntry entry = getItem(position);    
               // Check if an existing view is being reused, otherwise inflate the view
               if (convertView == null) {
                  convertView = LayoutInflater.from(getContext()).inflate(R.layout.filechooser_list_item, parent, false);
               }
               // Lookup view for data population
               TextView filechooserEntry = (TextView) convertView.findViewById(R.id.filechooser_entry);
               // Populate the data into the template view using the data object
               filechooserEntry.setText(entry.name);
               filechooserEntry.setCompoundDrawablesWithIntrinsicBounds(entry.item, null, null, null);
               // Return the completed view to render on screen
               return convertView;
        }   

        private FileAdapter getFiles() {
            SharedPreferences options = PreferenceManager.getDefaultSharedPreferences(getActivity());
            ArrayList<File> files = getFilesInDir(options.getString("BaseDir", ""));

            if (!isBaseDir(options.getString("BaseDir", ""))){
                this.add(new ListEntry("..", getResources().getDrawable( R.drawable.ic_folder)));
            }

            for (File file : files){            
                if (file.isDirectory()){
                    this.add(new ListEntry(file.getName(),getResources().getDrawable(R.drawable.ic_folder)));
                }else{
                    if (file.getName().endsWith(FILE_SUFFIX)||FILE_SUFFIX.equals("*")){
                        this.add(new ListEntry(file.getName(),getResources().getDrawable(R.drawable.ic_file)));
                    }
                }   
            }
        return this;
        }

        private ArrayList<File> getFilesInDir(String dir) {
            File folder = new File(dir);
            if (!folder.exists()){
                folder = new File("/");
                if (!folder.exists()){
                    Log.wtf("FileBrowser","Something's really fishy");
                }
            }
            ArrayList<File> fileList;
            if (folder.listFiles()!=null){
                fileList = new ArrayList<File>(Arrays.asList(folder.listFiles()));
            }else{
                fileList = new ArrayList<File>();
            }
            return fileList;
        }
    }
}

and in your Activity:

public class YourActivity extends Activity implements FileChooserFragment.OnFileSelectedListener{

@Override
public void onFileSelected(String file) {
//Do whatever you want to do with the files
}

// And whereever you want to start the Fragment: 
FileChooserFragment fileFragment = new FileChooserFragment();
fileFragment.show(getFragmentManager(), "fileChooser");