如何寫一個檔案總管(FilesManager)-新增-刪除-修改

如何寫一個檔案總管(FilesManager)-新增-刪除-修改

情境

如何寫一個檔案總管(FilesManager),其實少了很多功能,只是單純能夠看而已,所以我們要加上新增資料夾,刪除檔案,修改檔名的功能。

完整程式碼

你可以到 GitHub 上面觀看或下載完整程式碼。

GitHub

程式碼說明

由於要加入儲存裝置的存取,因此,您需要加入一些權限。

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

因為 6.0 會有 Runtime Permission 的問題,所以要有權限的控管,可以參考如何使用Runtime Permission,不過,這不是這篇的重點,因此,你只需要到設定->應用程式->權限內把權限打開就好 。





因為根目錄沒有存取權限,因此我們把ROOT改成可讀寫的路徑,在Android有存取權限的部分改成以下程式碼。

private static String ROOT = Environment.getExternalStorageDirectory().getAbsolutePath();

關於存取檔案可以參考如何在Android存讀檔案





首先來製造一些對話框,用來負責產出跟使用者對話的內容,如果不會 AlertDialog,您可以參考如何使用AlertDialog這篇文章。
回到我們的程式,首先,建立一個選單列表,宣告一個字串陣列。

private static final String[] ACTION = {"修改", "刪除"};

對 ListView 增加一個 ItemLongClick 的事件。

listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
        new AlertDialog.Builder(MainActivity.this)
            .setItems(ACTION, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    String name = ACTION[which];
                    Toast.makeText(MainActivity.this, name, Toast.LENGTH_SHORT).show();
                }
            })
            .show();
        return true;
    }
});

回傳 true 代表事件到我這層就處理掉,不繼續往下傳,
否則會在觸發onClick的事件。
當長按某個檔案或資料夾,就會彈跳出AlertDialog的表單,讓你選擇剛剛宣告的字串陣列。






這意味著我們必須針對兩種情況進行判斷,因此,可以改寫成這樣。

new AlertDialog.Builder(MainActivity.this)
    .setItems(ACTION, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        switch(which){
            case 0://修改
                break;
            case 1://刪除

                break;
        }
    }
})
.show();

接著在 main_activity.xml 增加一個新增資料夾的 TextView 按鈕。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.givemepass.filemanagerdemo.MainActivity">
    <TextView
        android:layout_alignParentBottom="true"
        android:id="@+id/new_dir"
        android:gravity="center"
        android:layout_weight="1"
        android:text="@string/new_dir"
        android:layout_width="match_parent"
        android:layout_height="20dp" />
    <ListView
        android:layout_above="@id/new_dir"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/list_view" />
</RelativeLayout>

你可以看到下面檔案列表多了一個按鈕。





新增資料夾

當我們按下按鈕以後,就可以新增一個資料夾。

final View item = LayoutInflater.from(MainActivity.this).inflate(R.layout.add_new_dir, null);
new AlertDialog.Builder(MainActivity.this)
    .setTitle(R.string.input_dir_name)
    .setView(item)
    .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        EditText editText = (EditText) item.findViewById(R.id.edittext);
        if(editText.getText().equals("")){return;}
        String filePath = nowPath + File.separator + editText.getText().toString();
        File f = new File(filePath);
        Toast.makeText(MainActivity.this, filePath, Toast.LENGTH_SHORT).show();

        if(f.mkdir()){
            Toast.makeText(MainActivity.this, getString(R.string.create_dir_success) + filePath, Toast.LENGTH_SHORT).show();
            getFileDirectory(nowPath);
            simpleAdapter.notifyDataSetChanged();
        } else{
            Toast.makeText(MainActivity.this, R.string.create_dir_fail, Toast.LENGTH_SHORT).show();
        }
    }
})
.show();

首先會判斷 EdiText 內的字串是否有輸入,如果是空字串代表使用者按下新增資料夾卻沒有輸入資料夾名稱,此時,我們會直接 return,如果有,我們就可以直接處理,由於我可以可以透過目前的位置來新增一個資料夾,因此,下面這一行就可以先塞入File的建構子。

String filePath = nowPath + File.separator + editText.getText().toString();

如此來就可以透過 File 類別的方法來新增資料夾。

f.mkdir()

mkdir() 這個方法會回傳一個 boolean 值,用來判斷檔案是否有新增成功,如果有則會回傳 true,否則就回傳 false。

getFileDirectory(nowPath);
simpleAdapter.notifyDataSetChanged();

當新增資料夾成功,我們再一次的撈取該層資料夾以及檔案,再透過 adapter 來更新畫面,如此一來就可以成功看到資料夾是否有更新成功或失敗。

如下圖操作,按下新增按鈕。





輸入資料夾名稱。





如果新增成功可以看到畫面有更新一個資料夾,且跳出一個提示訊息顯示成功。





刪除資料夾或檔案

有時候你會想要把某些資料夾或者檔案刪除,因此,我們也要提供一個刪除的功能,而當要刪除的時候,要讓使用者指定要刪除哪一個檔案或資料夾,則要判斷使用者按下哪一個,因此,我們選擇使用長按功能。
listview 本身就有提供長按的處理事件,還記得我們上面有顯示兩種功能表,一種是刪除,另一個是修改,所以我們透過長按事件來跳出彈跳視窗可以這樣寫。

listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
    @Override
    public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
        new AlertDialog.Builder(MainActivity.this)
        .setItems(ACTION, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                String path = paths.get(position);
                switch(which){
                    case 0:

                        break;
                    case 1:
                        delFile(path);
                        break;
                }
            }
        })
        .show();
        return true;
    }
});

首先,判斷出哪一個列被使用者長按。

String path = paths.get(position);

可以透過外部該層資料夾的 paths 陣列取出使用者按下哪一個位置,就可以抓到該列的資料夾路徑,透過AlertDialog彈出一個對話視窗。





透過 delFile 這個方法就可以將檔案刪除,將路徑傳入 delFile 方法。

new AlertDialog.Builder(MainActivity.this)
    .setTitle(R.string.make_sure_del)
    .setNegativeButton(R.string.ok, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            File file = new File(path);
            if(file.exists()){
                if(file.delete()){
                    Toast.makeText(MainActivity.this, R.string.del_success, Toast.LENGTH_SHORT).show();
                    getFileDirectory(nowPath);
                    simpleAdapter.notifyDataSetChanged();
                } else{
                    Toast.makeText(MainActivity.this, R.string.del_fail, Toast.LENGTH_SHORT).show();
                }
            } else{
                Toast.makeText(MainActivity.this, R.string.file_is_not_exist, Toast.LENGTH_SHORT).show();
            }
        }
    }).setPositiveButton(R.string.cancel, new DialogInterface.OnClickListener(){

        @Override
        public void onClick(DialogInterface dialog, int which) {

        }
    })
    .show();

在這邊我們可以看到傳入一個字串,我們把它塞入到 File 建構子,透過 File 類別的 exists 方法來判斷該檔案是否存在,如果存在則可以呼叫 delete 方法來進行刪除,delete 方法會回傳一個 boolean,
來判斷是否刪除成功?
跟新增資料夾相同,如果刪除成功則重新讀取,且該曾目錄會更新畫面。

getFileDirectory(nowPath);
simpleAdapter.notifyDataSetChanged();

如此一來,你就可以看到下面的畫面,再一次的詢問是否要刪除檔案。





將剛剛新增的資料夾刪除了。





修改名稱

最後剩下修改名稱的這個功能,在前面我們 ListView 有實做長按功能,其中,在 item 0 的部分並未處理,這邊是用來處理重新命名的功能。

new AlertDialog.Builder(MainActivity.this)
.setItems(ACTION, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        String path = paths.get(position);
        switch(which){
            case 0:
                rename(path);
                break;
            case 1:
                delFile(path);
                break;
        }
    }
})
.show();

可以看到我們呼叫了 rename 這個方法,並且把我們要處理的路徑傳入這個方法。

final View item = LayoutInflater.from(MainActivity.this).inflate(R.layout.add_new_dir, null);
new AlertDialog.Builder(MainActivity.this)
.setTitle(R.string.input_you_rename)
.setView(item)
.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
        EditText editText = (EditText) item.findViewById(R.id.edittext);
        if(editText.getText().equals("")){
            Toast.makeText(MainActivity.this, R.string.input_dir_name, Toast.LENGTH_SHORT).show();
            return;
        }
        String newPath = nowPath + File.separator + editText.getText();
        File f = new File(path);
        if(f.renameTo(new File(newPath))){
            Toast.makeText(MainActivity.this, R.string.modify_success, Toast.LENGTH_SHORT).show();
            getFileDirectory(nowPath);
            simpleAdapter.notifyDataSetChanged();
        } else{
            Toast.makeText(MainActivity.this, R.string.modify_fail, Toast.LENGTH_SHORT).show();
        }
    }
})
.show();

在這邊就跟新增資料夾雷同,首先,判斷是否輸入要變更的名稱
如果使用者沒有輸入 則直接 return,反之,就進行處理。
跟處理新資料夾不太一樣,變更名稱會先將原本的檔案或資料夾刪除掉,再新增一個檔案或資料夾,因此,要透過原有的檔案或資料夾來進行 rename 的方法。

String newPath = nowPath + File.separator + editText.getText();
File f = new File(path);

先將舊的檔案把變成一個 File 物件。

f.renameTo(new File(newPath))

再透過 rename 方法將新 File 物件丟給舊有的 File 物件,一樣會回傳一個 boolean 來判斷是否 rename 成功?

操作上選擇要修改。





跳出詢問視窗。



輸入要修改的名稱。



成功將 qqq 修改成 givemepass 資料夾 。



這樣就是一個簡易版的檔案總管了。