編輯:關於Android編程
好長時間沒更博客了,最近一直在做比賽的一個項目,就是實現客戶端和PC端的文件互傳,其實一開始在看到這個題目的時候,完全不知道怎麼去實現,感覺一臉懵逼,後來在查閱了資料以及相關書籍後了解到可以用Socket來進行通信,通過IO流來實現文件的互傳,於是開始著手寫這個項目。下面來詳細介紹
一.讀取手機文件資源。
要傳輸文件首先要有文件可傳,這就要先從手機數據庫中讀取各種文件資源,包括音樂、視頻、圖片、文檔、壓縮包、以及應用安裝包。當然我只是列舉了一部分常用的,還有很多類型,但是並不是很常用,所以就沒有列舉出來。
1.音樂
獲取手機音樂其實並不難,Android已經將這些都封裝好了,手機數據庫中音樂的地址是MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;所以直接在這個uri下查找就可以了。附上代碼:
public Cursor loadMusic(ContentResolvercontentResolver){ StringmusicSort = MediaStore.Audio.Media.TITLE; CursormusicCursor = contentResolver.query(FileFragment.musicUri, null, null, null,musicSort); if(musicCursor != null){ while(musicCursor.moveToNext()){ String title =musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE)); String artist =musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)); String size =musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.SIZE)); String path =musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.DATA)); MediaFiles song = new MediaFiles(); song.setFileName(title); song.setFileSize(size); song.setFilePath(path); song.setArtist(artist); musicList.add(song); } } returnmusicCursor; }
import android.widget.ImageView; import java.io.File; public class MediaFiles{ public intfileImage; publicString fileSize; publicString fileName; publicString artist; publicString filePath; publicImageView check; public intcount = 0; publicboolean isFile = false; public voidsetFileImage(int fileImage){ this.fileImage = fileImage; } public voidsetFileSize(String fileSize){ this.fileSize = fileSize; } public voidsetFileName(String fileName){ this.fileName = fileName; } public voidsetArtist(String artist){ this.artist = artist; } public voidsetFilePath(String filePath){ this.filePath = filePath; } public intgetFileImage(){ returnfileImage; } publicString getFileSize(){ returnfileSize; } publicString getFileName(){ returnfileName; } publicString getArtist(){ returnartist; } publicString getFilePath(){ returnfilePath; }
這樣獲取手機的音樂資源就完成了! 附上圖片
2.視頻
和獲取音樂資源一樣,視頻文件還是通過一個uri來在手機數據庫中查詢的。Uri videoUri =MediaStore.Video.Media.EXTERNAL_CONTENT_URI; 所以獲取視頻文件就照貓畫虎了。附上代碼:
public Cursor loadVideo(ContentResolvercontentResolver){ StringvideoSort = MediaStore.Video.Media.DISPLAY_NAME; CursorvideoCursor = contentResolver.query(FileFragment.videoUri, null, null, null,videoSort); if(videoCursor != null){ while(videoCursor.moveToNext()){ String title = videoCursor.getString(videoCursor.getColumnIndex(MediaStore.Video.Media.DISPLAY_NAME)); String size =videoCursor.getString(videoCursor.getColumnIndex(MediaStore.Video.Media.SIZE)); String path = videoCursor.getString(videoCursor.getColumnIndex(MediaStore.Video.Media.DATA)); MediaFiles video = new MediaFiles(); video.setFileName(title); video.setFileSize(size); video.setFilePath(path); videoList.add(video); } } returnvideoCursor; }
Title、文件標題,size、文件大小,path、文件路徑。全部包裝成MediaFiles對象,添加到videoList這個集合中,這樣視頻文件也就搞定了!附上圖片:
視頻只有這一個,所以看起來就不是很爽。。。
3.圖片
圖片文件還是一樣的套路,都是套路,圖片的uri : MediaStore.Images.Media.EXTERNAL_CONTENT_URI;只不過在處理圖片文件的時候我並沒有把全部文件都添加到一個集合當中,因為這樣看起來不是很直觀,而是通過文件夾來讀取的,如果圖片所在的文件夾都相同,就把這些圖片放到一個集合當中,這樣看起來就很清楚了。附上代碼:
public Cursor loadImage(ContentResolver contentResolver){ String imageSort = MediaStore.Images.Media.TITLE; Cursor imageCursor = contentResolver.query(FileFragment.imageUri, null, null, null, imageSort); if(imageCursor != null){ while(imageCursor.moveToNext()){ String path = imageCursor.getString(imageCursor.getColumnIndex(MediaStore.Images.Media.DATA)); File file = new File(path); if(file.exists()) { File parentFile = new File(path).getParentFile(); if (parentFile == null) { continue; } String folderPath = parentFile.getAbsolutePath(); if (!parentPath.contains(folderPath)) { parentPath.add(folderPath); ImageFolder imageFolder = new ImageFolder(); imageFolder.setFolderPath(folderPath); imageFolder.setFirstPath(path); imageFolder.setCount(getFileCount(folderPath)); imageFolders.add(imageFolder); } } } parentPath = null; } return imageCursor; }
首先獲取到一張圖片文件,如果該圖片文件對應的文件夾不為空,並且保存文件夾路徑的集合不包括該文件夾的路徑,就給這個文件夾設置它的路徑,第一張圖片的路徑,文件夾下圖片的個數,並把這個文件夾對象添加到保存文件夾對象的集合當中。這樣圖片的處理就完成了(下面附上ImageFolder類和getFileCount() 獲取文件夾下圖片個數的代碼)。比起音樂和視頻,圖片還是有那麼一點復雜,但是只要思路清楚,其實問題也並不大。
public class ImageFolder { /** * 圖片文件夾路徑 */ private String folderPath; /** * 第一張圖片的路徑 */ private String firstPath; /** * 圖片數量 */ private int count; public void setFolderPath(String folderPath){ this.folderPath = folderPath; } public void setFirstPath(String firstPath){ this.firstPath = firstPath; } public void setCount(int count){ this.count = count; } public String getFolderPath(){ return folderPath; } public String getFirstPath(){ return firstPath; } public int getCount(){ return count; } } /** * 獲取對應路徑下圖片的個數 */ public int getFileCount(String path){ int count = 0; File [] files = new File(path).listFiles(); if(files == null){ return 0; } for (File file : files) { if (file.isFile() && (file.getAbsolutePath().endsWith("jpg") || file.getAbsolutePath().endsWith(".png") || file.getAbsolutePath().endsWith("gif") || file.getAbsolutePath().endsWith("jpeg"))) { count++; } } return count; }
附上圖片
4.文檔
對於文檔來說,Android並沒有提供直接在數據庫中查找的方法,這就比較麻煩了,不過這也難不住我們,可以通過遍歷手機存儲空間來查找到我們想要的文件資源,不過這樣會有點慢,但是總體還是可行的。附上代碼:
public ListloadFiles (String path, String suffix, boolean isIterative){ File [] files = new File(path).listFiles(); for (File file : files) { if (file.isFile()){ if(file.getPath().endsWith(suffix)) { MediaFiles mediaFiles = new MediaFiles(); mediaFiles.setFileName(file.getName()); mediaFiles.setFilePath(file.getPath()); try { FileInputStream fis = new FileInputStream(file); mediaFiles.setFileSize(String.valueOf(fis.available())); //通過IO流獲取文件大小 fis.close(); } catch (IOException e) { e.printStackTrace(); } filesList.add(mediaFiles); } if (!isIterative) { break; } } else if(file.isDirectory()) { loadFiles(file.getPath(), suffix, isIterative); } } return filesList; }
這裡我寫了一個查找後綴名的文件的方法,有三個參數,查找路徑,文件後綴名,是否繼續查找(一般為true)。其中查找路徑就是手機存儲空間路徑Environment.getExternalStorageDirectory() +"/",這個沒什麼好說的,文件後綴名就是要查找那種類型的文件,這裡我們遞歸來查找文件,得到根目錄下的所有文件後,然後遍歷,如果是文件就判斷是否為我們想要的文件,如果是文件夾,就遞歸調用該方法繼續查找。這樣我們就可以把對應後綴名的文檔資源獲取到一個集合中。其中有一個需要注意的就是獲取文件大小,File類並沒有提供獲取文件大小的方法,這裡我們用IO流來處理,給對應文件對象搭一個輸入流FileInputStream(),然後用這個流的available()方法就可以得到文件的大小了,不過單位是字節,需要對其進行轉換,看起來更直觀。
文檔的類型我總共獲取了doc,xlsx,pptx,txt,zip,這幾種類型,這都是比較常用的。
附上圖片:
5.訪問內部存儲空間
內部存儲空間的路徑就是我們在訪問文檔時的路徑,即Environment.getExternalStorageDirectory() +"/",仔細看這個方法ExternalStorage,意思是外部存儲,但是很多手機廠商在區分這些的時候,把手機自身的存儲空間劃分到了這個區域,而SD卡的路徑一般都在storage/sdcard1或者storage/ext_sdcard下面,我們可以先判斷手機SD卡是否可用,
/** * 判斷SD卡是否可用 */ public Object externalStorageAvailable(){ File file = Environment.getExternalStorageDirectory().getParentFile().getParentFile(); File [] files = file.listFiles(); for (File file1:files) { if(file1.getName().contains("ext_sdcard") || file1.getName().contains("sdcard1") || file1.getName().contains("sdcard2")){ externalPath = file1.getAbsolutePath(); return file1.getAbsolutePath(); } } return null; }
如果SD卡不可用就返回空,否則就得到SD卡的目錄。然後我們就能得到內部存儲和SD卡下的所有文件。附上代碼;
/** * 獲取對應路徑下的所有文件 */ public ListloadStorage(String path){ File [] files = new File(path).listFiles(); for(File file : files){ MediaFiles mediaFiles = new MediaFiles(); mediaFiles.setFileName(file.getName()); mediaFiles.setFilePath(file.getPath()); if(file.isFile()){ try { FileInputStream fis = new FileInputStream(file); mediaFiles.setFileSize(String.valueOf(fis.available())); //獲取文件大小 fis.close(); } catch (IOException e) { e.printStackTrace(); } mediaFiles.isFile = true; } storage.add(mediaFiles); } return storage; }
跟讀取文檔一樣,先給這個方法傳一個路徑(內部存儲或SD卡)再是通過file.listFiles()方法獲取到該目錄文件下的所有文件,然後把name,path,size三個屬性賦給MediaFiles對象。再把這個對象添加到集合中,最後返回該集合。這樣就可以得到所有存儲空間下的所有文件了。還有一個要注意的就是我們在得到了存儲空間下的所有文件後,我們要繼續訪問它們的子目錄,一層層的深入,這就要給顯示file的listView設置監聽,如果當前是文件夾就繼續顯示它的字目錄下的所有文件。附上代碼:
public class ListListener implements AdapterView.OnItemClickListener{ @Override public void onItemClick(AdapterView parent, View view, int position, long id) { edit.setVisibility(View.GONE); MediaFiles file = loadFile.getStorage().get(position); if(!file.isFile){ MediaFiles files = loadFile.getStorage().get(0); path = files.getFilePath(); //保存當前第一個元素的路徑 loadFile.getStorage().clear(); loadFile.loadStorage(file.getFilePath() + "/"); StorageAdapter adapter = new StorageAdapter(getApplicationContext(), loadFile.getStorage()); listView.setAdapter(adapter); listView.setOnItemClickListener(new ListListener()); } } }
獲取到當前文件夾的路徑,然後用storage()方法獲取當前文件夾下的所有文件,然後再用listView顯示出來。這樣就能把所有文件都顯示出來了。
現在我們已經可以訪問到存儲空間下的各級文件了,但是如果我們想返回到上一級目錄,結果直接返回到了上一個界面,所以就要重寫activity的onKeyDown()方法,
附上代碼:
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { if(keyCode == KeyEvent.KEYCODE_BACK){ List lastFiles; if(loadFile.getStorage().isEmpty()){ lastFiles = loadFile.loadStorage(path.substring(0, path.lastIndexOf('/'))); } else { MediaFiles file1 = loadFile.getStorage().get(0); MediaFiles file2 = new LoadFile(Storage.this).loadStorage(Environment.getExternalStorageDirectory() + "/").get(0); if (file1.getFilePath().equals(file2.getFilePath())) { finish(); return true; } else { lastFiles = loadFile.getLastFile(loadFile.getStorage().get(0)); } } StorageAdapter adapter = new StorageAdapter(getApplicationContext(), lastFiles); listView.setAdapter(adapter); listView.setOnItemClickListener(new ListListener()); } return false; }
首先我們判斷當前目錄是否為空,如果為空,我們在上一段代碼中有一段粗體代碼,MediaFilesfiles = loadFile.getStorage().get(0); path =files.getFilePath(); //保存當前第一個元素的路徑,也就是當前目錄的上一級目錄的第一個文件,我們通過path.substring(0, path.lastIndexOf('/'))方法得到包含上一級目錄所有文件的文件夾的路徑,這樣顯示這個path下的所有文件就可以了。如果當前目錄不為空,先獲取到當前目錄下的第一個文件file1,然後拿到第一級目錄下的第一個文件file2,如果file1。getPath() == file2.getPath()就表示當前目錄是第一級目錄,然後直接finish當前activity,否則就用getLastFiles()得到上一級目錄的所有文件,
/** * 獲取上一級目錄的全部文件 */ public ListgetLastFile(MediaFiles file){ String path = file.getFilePath(); File aFile = new File(path).getParentFile().getParentFile(); File [] files = aFile.listFiles(); storage.clear(); for(File file1:files){ MediaFiles mediaFiles = new MediaFiles(); mediaFiles.setFilePath(file1.getPath()); mediaFiles.setFileName(file1.getName()); if(file1.isFile()){ try { FileInputStream fis = new FileInputStream(file1); mediaFiles.setFileSize(String.valueOf(fis.available())); //獲取文件大小 fis.close(); } catch (IOException e) { e.printStackTrace(); } mediaFiles.isFile = true; } storage.add(mediaFiles); } return storage; }
得到該文件的父文件的父文件,然後遍歷該目錄下的所有文件並添加到集合中,也就得到了該文件的上一級目錄下的所有文件。
至此我們就完成了手機常用文件的獲取以及內部存儲以及SD卡的所有目錄下文件的訪問。
附上存儲目錄圖片以及文件管理主界面:
附上文件操作的代碼:
/** * 復制文件 */ public int copyFiles(String sourcePath, String targetPath){ File file1 = new File(sourcePath); if(file1.isFile() && file1.exists()){ try { File file = new File(targetPath); InputStream fis = new FileInputStream(sourcePath); OutputStream fos = new FileOutputStream(file.getParent() + '/' + file1.getName()); byte [] b = new byte[1024]; int c; while((c = fis.read(b)) > 0){ fos.write(b, 0, c); } fos.close(); fis.close(); return 1; } catch (IOException e) { Log.e("e", e.toString()); return -1; } } else { File file2 = new File(targetPath + '/' + file1.getName()); if(!file2.exists()) { file2.mkdirs(); } else { return 0; } File [] files = file1.listFiles(); if(files.length == 0){ return 1; } else { for (File file : files) { copyFiles(file.getAbsolutePath(), file2.getPath()); } return 1; } } } /** * 刪除文件 */ public boolean deleteFile(File file) { if (!file.exists()) { return false; } else { if (file.isFile() && file.exists()) { return file.delete(); } if (file.isDirectory()) { File[] childFile = file.listFiles(); if (childFile == null || childFile.length == 0) { return file.delete(); } for (File f : childFile) { deleteFile(f); } return file.delete(); } } return false; } /** * 移動文件 */ public int moveFile(String oldPath, String newPath){ File oldFile = new File(oldPath); if(!oldFile.exists()){ return 0; } File newFile = new File(newPath); if(!newFile.exists()){ newFile.mkdirs(); } if(oldFile.isFile()){ oldFile.renameTo(new File(newPath + File.separator + oldFile.getName())); return 1; } else if(oldFile.isDirectory()){ File [] sourceFile = oldFile.listFiles(); for(File file : sourceFile){ if(file.isFile()){ oldFile.renameTo(new File(newPath + File.separator + file.getName())); } if(file.isDirectory()){ moveFile(file.getAbsolutePath(), newFile.getAbsolutePath() + File.separator + file.getName()); } } return 1; } return 0; }
(一)前言今天我們一起來看一下進度加載條ProgressBarAndroid控件的講解與基本使用。剛創建的React Native技術交流群(282693535),歡迎各
在網上查了好多資料,大致都雷同,大家都是互相抄襲的,看著很費勁,不好理解,自己總結一下,留著需要看的話來查找。代碼中的例子如下:復制代碼 代碼如下:<ImageVi
選擇要包裹的代碼塊,然後按下ctrl + alt + t
android app漢化與英化在res文件夾下面添加一個values-en-US文件夾,添加一個strings.xml文件,然後往裡面添加標簽對,系統語言換成英語就可以