안드로이드(스튜디오)/막 써

파일탐색기 샘플

어비서 2018. 11. 28. 20:19
반응형

본 샘플은 상위 폴더(fileexplorer_ic_dir_open), 하위 폴더(fileexplorer_ic_dir_close), 파일(fileexplorer_ic_file) 총 3가지의 이미지 파일이 필요합니다.

이미지 파일은 따로 준비해서 설정하여 사용하시면 됩니다.



FileExplorer_Sample_Activity.java

package com.abyser.activity;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.TextView;

import com.abyser.R;

/**
* 파일탐색기 샘플 액티비티.
*/
public class FileExplorer_Sample_Activity extends Activity implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener {

/**
* 로그 태그
*/
private static final String LOG_TAG = FileExplorer_Sample_Activity.class.getSimpleName();

//////////////
// 레이아웃 //
//////////////
private TextView tv_current_path;
private ListView lv_file_explorer;
private FileExplorer_Sample_Adapter mAdapter;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fileexplorer);

initValue();
initLayout();

}

@Override
protected void onResume() {
super.onResume();

mAdapter.refresh();

}

/**
* 초기 값 설정.
*/
private void initValue() {

}

/**
* 초기 레이아웃 설정.
*/
private void initLayout() {

tv_current_path = (TextView)findViewById(R.id.fileexplorer_tv_current_path);
lv_file_explorer = (ListView)findViewById(R.id.fileexplorer_lv_file_explorer);

mAdapter = new FileExplorer_Sample_Adapter(this, "/storage/emulated/0/Android/data");
tv_current_path.setText(mAdapter.getCurrentPath());
lv_file_explorer.setAdapter(mAdapter);
lv_file_explorer.setOnItemClickListener(this);
lv_file_explorer.setOnItemLongClickListener(this);

}

/////////////////////////////////////
// AdapterView.OnItemClickListener //
/////////////////////////////////////
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

mAdapter.click(position);
tv_current_path.setText(mAdapter.getCurrentPath());

}

/////////////////////////////////////////
// AdapterView.OnItemLongClickListener //
/////////////////////////////////////////
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {

mAdapter.longClick(position);

return true;

}

}


FileExplorer_Sample_Adapter.java

package com.abyser.activity;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Environment;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;

import com.abyser.R;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;

/**
* 파일탐색기 리스트를 보여 줄 어뎁터.
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} 또는
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} 권한 필요.
* @author abyser
*/
public class FileExplorer_Sample_Adapter extends BaseAdapter {

/**
* 로그 태그
*/
private static final String LOG_TAG = FileExplorer_Sample_Adapter.class.getSimpleName();

/**
* {@link Context}
*/
private Context mContext;
/**
* 리스트 아이템
*/
private ArrayList<File> mList;
/**
* 최상위 경로
*/
private String mRootFolderPath;
/**
* 내부 저장소 경로
*/
private String mInternalStoragePath;
/**
* 현재 경로
*/
private String mCurrentPath;

/**
* 생성자.
* @param context {@link Context}
*/
public FileExplorer_Sample_Adapter(Context context) {
this(context, null);
}

/**
* 생성자.
* @param context {@link Context}
* @param rootFolderPath 최상위 경로
*/
public FileExplorer_Sample_Adapter(Context context, String rootFolderPath) {

this.mContext = context;
this.mList = new ArrayList<File>();
this.mRootFolderPath = rootFolderPath;
this.mInternalStoragePath = null;
this.mCurrentPath = null;

init();

}

/**
* 초기 파일 리스트 설정.
*/
private void init(){

try {

mInternalStoragePath = getStoragePath(false);

} catch (Exception e) {
e.printStackTrace();
mInternalStoragePath = null;
}

File rootFolder = null;
if(mRootFolderPath == null || mRootFolderPath.length() <= 0){

if(mInternalStoragePath == null || mInternalStoragePath.length() <= 0){

rootFolder = null;
mRootFolderPath = null;

}else{

File storageFolder = new File(mInternalStoragePath);
rootFolder = storageFolder.getParentFile().getParentFile();
mRootFolderPath = rootFolder.getPath();

}

}else{

rootFolder = new File(mRootFolderPath);
if(!rootFolder.exists()){
rootFolder = null;
mRootFolderPath = null;
}

}

if(rootFolder == null){
mList = new ArrayList<File>();
mCurrentPath = "Unknown";
}else{
mList = new ArrayList<File>(Arrays.asList(rootFolder.listFiles()));
mCurrentPath = rootFolder.getPath();
}

}

/**
* 저장소 경로 반환.
* @param isGetExternalPath 외부 저장소 반환 여부
* @return 저장소 경로
*/
private String getStoragePath(boolean isGetExternalPath) throws IOException {

String storagePath = "";

String internalPath = "";
String externalPath = "";
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){

// /storage/저장소명/Android/data/패키지명 형식의 File 리스트 가져오기
File[] packageDirs = this.mContext.getExternalFilesDirs("");

// 우리나라는 드물지만 2개 이상의 저장소를 가진 디바이스가 있어
// 해당 디바이스는 따로 처리가 필요 할것으로 판단되어 오류 처리 함.
if (packageDirs.length > 2) {
throw new IOException("Can not find path. Cause device has more than 2 storage.");
}

for (int i = 0; i < packageDirs.length; i++) {

File packageDir = packageDirs[i];
if (packageDir != null) {

String tmpPackagePath = packageDir.getPath();
String tmpStoragePath = tmpPackagePath.substring(0, tmpPackagePath.indexOf("/Android"));

if (tmpStoragePath.toLowerCase().contains("emulated")) {
internalPath = tmpStoragePath;
} else {
externalPath = tmpStoragePath;
}

}

}

if(isGetExternalPath){

if(externalPath != null && externalPath.length() > 0){
storagePath = externalPath;
}else{
throw new IOException("Can not find path. Cause find storage path is null.");
}

}else{

if(internalPath != null && internalPath.length() > 0){
storagePath = internalPath;
}else{
throw new IOException("Can not find path. Cause find storage path is null.");
}

}

}else{

try {

internalPath = Environment.getExternalStorageDirectory().getPath();

File root = new File("/mnt");
File[] rootDir = root.listFiles();
for (File dir : rootDir) {

if (dir.canWrite()) {

String tmpPath = dir.getPath();
if (tmpPath.contains("legacy")){
continue;
}

if (!internalPath.equals(tmpPath)) {
externalPath = tmpPath;
break;
}

}

}

} catch (Exception e) {
throw new IOException("Can not find path. Cause " + e.getMessage());
}

if(isGetExternalPath){

if(externalPath != null && externalPath.length() > 0){
storagePath = externalPath;
}else{
throw new IOException("Can not find path. Cause find storage path is null.");
}

}else{

if(internalPath != null && internalPath.length() > 0){
storagePath = internalPath;
}else{
throw new IOException("Can not find path. Cause find storage path is null.");
}

}

}

return storagePath;

}

/**
* 토스트 보여주기.
* @param msg 보여줄 메세지
* @param isShowShort 짧게 보여줄지 여부
*/
public void showToast(String msg, boolean isShowShort){
Toast.makeText(this.mContext, msg, (isShowShort ? Toast.LENGTH_SHORT : Toast.LENGTH_LONG)).show();
}

/**
* 파일 확장자 반환.
* @param file 파일 확장자를 포함한 파일
* @return null : 파일이 null일 경우, 파일 확장자 : 정상 반환
*/
public String getFileExtension(String file) {

if(file == null){
return null;
}

return file.substring(file.lastIndexOf(".") + 1);

}

/**
* 파일 확장자를 제외한 파일명 반환.
* @param file 파일 확장자를 포함한 파일
* @return null : 파일이 null일 경우, 파일명 : 정상 반환
*/
public static String getFileName(String file) {

if(file == null){
return null;
}

String fileName = file.substring(0, file.lastIndexOf("."));

return fileName.substring(fileName.lastIndexOf("/") + 1);

}

@Override
public int getCount() {
return mList.size();
}

@Override
public File getItem(int position) {
return mList.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {

final int tmp_position = position;

if (convertView == null) {
convertView = ((Activity)mContext).getLayoutInflater().inflate(R.layout.item_fileexplorer, null);
}

ImageView iv_ic = (ImageView)convertView.findViewById(R.id.fileexplorer_list_item_iv_icon);
TextView tv_name = (TextView)convertView.findViewById(R.id.fileexplorer_list_item_tv_name);

File f = mList.get(tmp_position);

if("...".equals(f.getName())){

iv_ic.setImageResource(R.drawable.fileexplorer_ic_dir_open);
tv_name.setText("...");

return convertView;

}else{

if(f.isDirectory()){
iv_ic.setImageResource(R.drawable.fileexplorer_ic_dir_close);
}else{
iv_ic.setImageResource(R.drawable.fileexplorer_ic_file);
}

}

tv_name.setText(f.getName());

return convertView;

}

/**
* 현재 파일 리스트가 보여지고 있는 경로 반환.
* @return 현재 파일 리스트가 보여지고 있는 경로
*/
public String getCurrentPath(){
return this.mCurrentPath;
}

/**
* 현재 경로를 기준으로 파일 리스트 갱신.
*/
public void refresh(){

File currentPath = new File(mCurrentPath);
if(mRootFolderPath.equals(mCurrentPath)){

mList = new ArrayList<File>(Arrays.asList(currentPath.listFiles()));

}else{

File[] tmpFileList = currentPath.listFiles();
File[] fileList = new File[tmpFileList.length + 1];
fileList[0] = new File(mCurrentPath + "/...");
int size = 1;
for (File data : tmpFileList) {
fileList[size++] = data;
}

mList = new ArrayList<File>(Arrays.asList(fileList));

}

notifyDataSetChanged();

}

/**
* 파일 클릭 이벤트.
* @param position 클릭한 파일 포지션
*/
public void click(int position){

if(isClickFolder(mList.get(position))){
clickFolder(position);
}else{
clickFile(position);
}

}

/**
* 파일 롱클릭 이벤트.
* @param position 롱클릭한 파일 포지션
*/
public void longClick(int position){

final File longClickFile = mList.get(position);

if("...".equals(longClickFile.getName())){
return;
}

boolean isLongClickFolder = false;
if(isLongClickFolder(longClickFile)){
isLongClickFolder = true;
}else{
isLongClickFolder = false;
}
final boolean finalIsLongClickFolder = isLongClickFolder;

AlertDialog.Builder builder = new AlertDialog.Builder(this.mContext);
builder.setCancelable(false);
builder.setTitle((finalIsLongClickFolder ? "폴더" : "파일") + " 삭제");
builder.setMessage("선택한 " + (finalIsLongClickFolder ? "폴더를" : "파일을 ") + "삭제하시겠습니까?");
builder.setPositiveButton("삭제", new DialogInterface.OnClickListener() {

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

dialog.dismiss();

if(finalIsLongClickFolder){
deleteFolder(longClickFile);
}else{
deleteFile(longClickFile);
}

}

});
builder.setNegativeButton("취소", new DialogInterface.OnClickListener() {

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

dialog.dismiss();

}

});
builder.create();
builder.show();

}

/**
* 클릭한 파일이 폴더인지 여부 반환.
* @param clickFile 클릭한 파일
* @return true : 클릭한 파일이 폴더임, false : 클릭한 파일이 파일임
*/
private boolean isClickFolder(File clickFile){

if("...".equals(clickFile.getName()) || clickFile.isDirectory()){
return true;
}

return false;

}

/**
* 폴더 클릭 이벤트.
* @param position 클릭한 폴더 포지션
*/
private void clickFolder(int position){

File f = null;
File[] fileList = null;

if(isClickInFolder(mList.get(position))){

f = mList.get(position);
if(mInternalStoragePath.equals((f.getPath() + "/0"))){
f = new File(mInternalStoragePath);
}
File[] tmpFileList = f.listFiles();
if(tmpFileList == null){
showToast("파일 접근 권한이 없습니다.", true);
return;
}

fileList = new File[tmpFileList.length + 1];
fileList[0] = new File(f.getPath() + "/...");
int size = 1;
for (File data : tmpFileList) {
fileList[size++] = data;
}

}else{

f = new File(mCurrentPath);
if(mInternalStoragePath.equals(f.getPath())){
f = f.getParentFile().getParentFile();
}else{
f = f.getParentFile();
}
File[] tmpFileList = f.listFiles();

if(mRootFolderPath.equals(f.getPath())){

fileList = tmpFileList;

}else{

fileList = new File[tmpFileList.length + 1];
fileList[0] = new File(f.getPath() + "/...");
int size = 1;
for (File data : tmpFileList) {
fileList[size++] = data;
}

}

}

mCurrentPath = fileList[0].getParent();

refreshFileList(fileList);

}

/**
* 클릭한 폴더 파일이 하위 폴더인지 여부 반환.
* @param clickFile 클릭한 폴더 파일
* @return true : 클릭한 폴더 파일이 하위 폴더임, false : 클릭한 폴더 파일이 상위 가기 폴더임
*/
private boolean isClickInFolder(File clickFile){
return (!"...".equals(clickFile.getName()));
}

/**
* 파일 리스트 갱신.
* @param fileList
*/
private void refreshFileList(File[] fileList){

mList = new ArrayList<File>(Arrays.asList(fileList));

notifyDataSetChanged();

}

/**
* 파일 클릭 이벤트.
* @param position 클릭한 파일 포지션
*/
private void clickFile(int position) {

File f = mList.get(position);

if (isClickTxtFile(f)) {
showTxtFile(f);
} else if(isClickPngFile(f)){
showPngFile(f);
} else {
showToast("보기를 지원하지 않는 파일 형식입니다.", true);
}

}

/**
* txt 확장자의 파일 클릭 여부 반환.
* @param clickFile 클릭한 파일
* @return true : 클릭한 파일이 txt 확장자의 파일임, false : 클릭한 파일이 txt 확장자의 파일이 아님
*/
private boolean isClickTxtFile(File clickFile){
return ("txt".equals(getFileExtension(clickFile.getName())));
}

/**
* png 확장자의 파일 클릭 여부 반환.
* @param clickFile 클릭한 파일
* @return true : 클릭한 파일이 png 확장자의 파일임, false : 클릭한 파일이 png 확장자의 파일이 아님
*/
private boolean isClickPngFile(File clickFile){
return ("png".equals(getFileExtension(clickFile.getName())));
}

/**
* txt 확장자의 파일 보여주기.
* @param txtFile 프로퍼티 파일
*/
private void showTxtFile(final File txtFile){

final ProgressDialog loadingDialog = new ProgressDialog(mContext);
loadingDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
loadingDialog.setMessage("파일을 읽는 중 입니다.");

AsyncTask<Void, Void, String> async = new AsyncTask<Void, Void, String>() {

@Override
protected void onPreExecute() {
super.onPreExecute();

loadingDialog.show();

}

@Override
protected String doInBackground(Void... voids) {

StringBuilder result = new StringBuilder();

BufferedReader br = null;
try {

br = new BufferedReader(new FileReader(txtFile));
String readLine = "";
while(((readLine = br.readLine()) != null)){
result.append(readLine + "\n");
}

}catch (Exception e){
e.printStackTrace();
}finally {

try{
br.close();
}catch (Exception e){
// ignore
}

}

return result.toString();

}

@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);

loadingDialog.dismiss();

View v = getShowView(false);
((TextView)v.findViewById(R.id.fileexplorer_show_tv_text)).setText(result);

AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setCancelable(false);
builder.setTitle(txtFile.getName() + " 파일 보기");
builder.setView(v);
builder.setPositiveButton("닫기", new DialogInterface.OnClickListener() {

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

dialog.dismiss();

}

});
builder.create();
builder.show();

}

};
async.execute();

}

/**
* png 확장자의 파일 보여주기.
* @param pngFile png 확장자의 파일
*/
private void showPngFile(File pngFile){

View v = getShowView(true);
((ImageView)v.findViewById(R.id.fileexplorer_show_iv_image)).setImageBitmap(getPngToBitmap(pngFile));

AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setCancelable(false);
builder.setTitle(pngFile.getName() + " 파일 보기");
builder.setView(v);
builder.setPositiveButton("닫기", new DialogInterface.OnClickListener() {

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

dialog.dismiss();

}

});
builder.create();
builder.show();

}

/**
* png 확장자의 파일을 {@link Bitmap}으로 만들어 반환.
* @param pngFile png 확장자의 파일
* @return {@link Bitmap}
*/
private Bitmap getPngToBitmap(File pngFile){
return BitmapFactory.decodeFile(pngFile.getPath());
}

/**
* 텍스트 파일, 또는 이미지 파일 클릭시 사용 될 커스텀 뷰 반환.
* @param isShowImage 이미지를 보여주는지 여부
* @return {@link View}
*/
private View getShowView(boolean isShowImage){

View v = ((Activity)mContext).getLayoutInflater().inflate(R.layout.item_fileexplorer_show, null);
if(isShowImage){
((ImageView)v.findViewById(R.id.fileexplorer_show_iv_image)).setVisibility(View.VISIBLE);
}else{
((ScrollView)v.findViewById(R.id.fileexplorer_show_sv_text)).setVisibility(View.VISIBLE);
}

return v;

}

/**
* 롱클릭한 파일이 폴더인지 여부 반환.
* @param longClickFile 롱클릭한 파일
* @return true : 롱클릭한 파일이 폴더임, false : 롱클릭한 파일이 파일임
*/
private boolean isLongClickFolder(File longClickFile){
return longClickFile.isDirectory();
}

/**
* 폴더 지우기.
* @param longClickFile 롱클릭한 파일
*/
private void deleteFolder(File longClickFile){

delete(longClickFile);

}

/**
* 파일 지우기.
* @param longClickFile 롱클릭한 파일
*/
private void deleteFile(File longClickFile){

delete(longClickFile);

}

/**
* 파일 지우기.
* @param longClickFile
*/
private void delete(final File longClickFile){

final ProgressDialog deleteProgressDialog = new ProgressDialog(mContext);
deleteProgressDialog.setCancelable(false);
deleteProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
deleteProgressDialog.setTitle((longClickFile.isDirectory() ? "폴더" : "파일") + " 삭제");
deleteProgressDialog.setMessage((longClickFile.isDirectory() ? "폴더를" : "파일을") + " 삭제 중 입니다.\n잠시만 기다려 주시기 바랍니다.");

AsyncTask<Void, Void, Void> asyncDelete = new AsyncTask<Void, Void, Void>(){

@Override
protected void onPreExecute() {
super.onPreExecute();

deleteProgressDialog.show();

}

@Override
protected Void doInBackground(Void... aVoid) {

longClickFile.delete();

return null;

}

@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);

refresh();

deleteProgressDialog.dismiss();

showToast("삭제를 완료하였습니다.", false);

}

};
asyncDelete.execute();

}

}


activity_fileexplorer.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:background="#000000"
android:orientation="vertical">

<TextView
android:id="@+id/fileexplorer_tv_current_path"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
android:gravity="center_vertical"
android:maxLines="1"
android:text="현재 경로"
android:textColor="#ffffff" />

<!-- 파일 탐색기 리스트 -->

<ListView
android:id="@+id/fileexplorer_lv_file_explorer"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="8" />

</LinearLayout>


Item_fileexplorer.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="wrap_content"
android:descendantFocusability="blocksDescendants"
android:orientation="horizontal"
android:paddingBottom="10dp"
android:paddingTop="10dp" >

<ImageView
android:id="@+id/fileexplorer_list_item_iv_icon"
android:layout_width="40dp"
android:layout_height="40dp"
android:src="@drawable/fileexplorer_ic_file"/>

<TextView
android:id="@+id/fileexplorer_list_item_tv_name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:gravity="center_vertical"
android:text="파일명"
android:textColor="#ffffff"
android:textSize="16dp" />

</LinearLayout>


Item_fileexplorer_show.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="15dp">

<ImageView
android:id="@+id/fileexplorer_show_iv_image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitCenter"
android:visibility="gone"/>

<ScrollView
android:id="@+id/fileexplorer_show_sv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<TextView
android:id="@+id/fileexplorer_show_tv_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="text"
android:textSize="16dp" />
</LinearLayout>
</ScrollView>

</RelativeLayout>


반응형