안드로이드(스튜디오)/막 써
파일탐색기 샘플
어비서
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>
반응형