安卓端简易FTP客户端APP开发
先上效果圖:
本文的FTP客戶端基于commons-net-3.3.jar庫實現。
實現了ftp服務器登錄。
單個文件的下載和上傳,以及本地復制和刪除文件。
一、登錄服務器活動模塊編寫:
這塊呢首先是要編寫一個登錄的界面的。
我的界面XML如下:
主要就是利用TextInputLayout這個控件來編寫的。不清楚這個控件的可以 百度/Google?學習一下。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout 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:orientation="vertical"tools:context=".FtpConnectActivity"><android.support.design.widget.TextInputLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><EditTextandroid:id="@+id/address"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="地址"android:maxLines="1"android:singleLine="true" /></android.support.design.widget.TextInputLayout><android.support.design.widget.TextInputLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><EditTextandroid:id="@+id/port"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="端口/默認21"android:maxLines="1"android:singleLine="true" /></android.support.design.widget.TextInputLayout><android.support.design.widget.TextInputLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><EditTextandroid:id="@+id/user"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="賬戶"android:maxLines="1"android:singleLine="true" /></android.support.design.widget.TextInputLayout><android.support.design.widget.TextInputLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"><EditTextandroid:id="@+id/password"android:layout_width="match_parent"android:layout_height="wrap_content"android:hint="密碼"android:inputType="textPassword"android:maxLines="1"android:singleLine="true" /></android.support.design.widget.TextInputLayout><Buttonandroid:id="@+id/connect_button"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="連接"/> </LinearLayout>接下來看一下我的登錄模塊活動的Java代碼:
public class FtpConnectActivity extends AppCompatActivity {private FTPClient mFtpClient;private Button Connect_Button;private String address,password,user;private int port=0;private EditText addressInput,portInput,userInput,passwordInput;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_ftp_connect);SharedPreferences pref =getSharedPreferences("connectData",MODE_PRIVATE);address=pref.getString("address","X");password=pref.getString("password","X");user=pref.getString("user","X");port=pref.getInt("port",0);addressInput=(EditText)findViewById(R.id.address);portInput=(EditText)findViewById(R.id.port);userInput=(EditText)findViewById(R.id.user);passwordInput=(EditText)findViewById(R.id.password);if(address!=null&&!address.equals("X")){addressInput.setText(address);}if(password!=null&&!password.equals("X")){passwordInput.setText(password);}if(user!=null&&!user.equals("X")){userInput.setText(user);}if(port!=0){portInput.setText(" "+port);}Connect_Button=(Button)findViewById(R.id.connect_button);Connect_Button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {address=addressInput.getText().toString();try {port = Integer.parseInt(portInput.getText().toString().trim());}catch (NumberFormatException e){port=0;e.printStackTrace();}user=userInput.getText().toString();password=passwordInput.getText().toString();//信息保存SharedPreferences.Editor editor=getSharedPreferences("connectData",MODE_PRIVATE).edit();editor.putString("address",address);editor.putString("user",user);editor.putString("password",password);editor.putInt("port",port);editor.apply();attemptLogin();}});}private void attemptLogin(){if(address.equals("")||port==0||user.equals("")||password.equals("")){Toast.makeText(this,"請填寫完整的信息",Toast.LENGTH_SHORT).show();return;}else{FtpLogin(address,port,user,password);}}private void FtpLogin(final String address, final int port, final String user, final String password ){FtpUtil.Init(address,port,user,password);if(FtpUtil.Connect()){//備注:后續,這里可以試著模仿okhttp的回調模式//如果連接成功Intent intent=new Intent();intent.setAction("com.app.bhk.connected");sendBroadcast(intent);runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(FtpConnectActivity.this, "success", Toast.LENGTH_SHORT).show();}});}else {//連接失敗runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(FtpConnectActivity.this, "failure", Toast.LENGTH_SHORT).show();}});}}}? ? ? 首先一般登錄模塊都會有記住密碼功能所以這里我實現了記住密碼功能,不過這里我并沒有添加記住密碼的這個勾選框,也就是默認記住密碼的,有需要的朋友的可以自己添加。記住密碼主要是利用SharePreference實現的。大概邏輯如下:當用戶點擊連接時,獲取到用戶的輸入信息將其存放在SharePreference數據庫內。下次啟動這個登錄活動時自動提取出上次的輸入信息,就可以了。具體可看代碼。
? ? ?然后就是說一下attemptLogin()這個方法,這個方法用于做登錄之前的準備,包括用戶輸入信息的格式判斷。有興趣研究登錄模塊編寫的朋友可以看我的另一篇博客《Android登錄模塊代碼解析》。
? ? ?最后FtpLogin()就是正式的關于連接ftp服務器的一些操作了。這里我寫了一個FtpUtil類專門來處理關于Ftp服務器的一些操作。這里直接調用相關方法進行登錄就行了。如果登錄成功這里還要寫一個廣播去通知主活動我們登錄成功了,還有將獲取到的FTPClient對象保存在FtpUtil中以便其他地方獲取!
二、工具類
? ? ? 在看主活動代碼之前我們先看一下我的兩個工具類:FileUtil(本地文件操作工具類)、FtpUtil(服務器文件操作工具類)的代碼。
public class FileUtil {private static File saveFile=null;private static FTPClient ftpClient;public static FTPClient getFtpClient() {return ftpClient;}public static void setFtpClient(FTPClient ftpClient) {FileUtil.ftpClient = ftpClient;}public static File getSaveFile() {return saveFile;}public static void setSaveFile(File saveFile) {FileUtil.saveFile = saveFile;}public static void Copy(File file, File directory){FileOutputStream outputStream=null;FileInputStream inputStream=null;if(!directory.isDirectory()||!file.isFile()||file.getParentFile().equals(directory))return;try {File newFile=new File(directory,file.getName());if(!newFile.exists()){newFile.createNewFile();}outputStream=new FileOutputStream(newFile);inputStream = new FileInputStream(file);byte[] buf = new byte[1024];int bytesRead;while((bytesRead=inputStream.read(buf))>0){outputStream.write(buf,0,bytesRead);}inputStream.close();outputStream.close();}catch (Exception e){e.printStackTrace();}} } public class FtpUtil {private static FTPClient mClient;private static String hostname;private static int port=21;//端口號默認21private static String account;private static String password;private static boolean check;private static FTPFile lists[];private static StringBuffer CurrentFile=new StringBuffer();private static FTPFile ftpFile;public static FTPFile getFtpFile() {return ftpFile;}public static void setFtpFile(FTPFile ftpFile) {FtpUtil.ftpFile = ftpFile;}public static void LastDirectory(){//切換目錄,沒用的原因是多個子線程沒法同步控制有待改進。思考可以用鎖來實現留待后續。try {int i = CurrentFile.toString().lastIndexOf('/');if (i >= 0) {CurrentFile.delete(i, CurrentFile.length());boolean f = mClient.changeToParentDirectory();Log.d("test1234", " " + f + " " + CurrentFile);}}catch (IOException e){e.printStackTrace();}}public static void NextDirectoty(final FTPFile file){//切換目錄,沒用的原因是多個子線程沒法同步控制有待改進。思考可以用鎖來實現留待后續。new Thread(new Runnable() {@Overridepublic void run() {try {CurrentFile.append("/" + file.getName());boolean f = mClient.changeWorkingDirectory(CurrentFile.toString());}catch (IOException e){e.printStackTrace();}}}).start();}public static void Init(String hostname1,int port1,String account1,String password1){hostname=hostname1;port=port1;account=account1;password=password1;if(mClient==null) {mClient = new FTPClient();}if(mClient.isConnected()){try {mClient.disconnect();}catch (IOException e){e.printStackTrace();}}}public static FTPClient getmClient() {return mClient;}public static boolean Connect(){check=true;new Thread(new Runnable() {@Overridepublic void run() {try {if (!mClient.isConnected()) {mClient.connect(hostname,port);boolean status = mClient.login(account, password);if (status) {check=true;try {mClient.setFileTransferMode(org.apache.commons.net.ftp.FTP.COMPRESSED_TRANSFER_MODE);// 使用被動模式設為默認mClient.enterLocalPassiveMode();// 二進制文件支持mClient.setFileType(FTP.BINARY_FILE_TYPE);//設置緩存mClient.setBufferSize(1024);//設置編碼格式,防止中文亂碼//中文亂碼問題后期再說吧mClient.setControlEncoding("UTF-8");//設置連接超時時間mClient.setConnectTimeout(10 * 1000);//設置數據傳輸超時時間mClient.setDataTimeout(10 * 1000);}catch (IOException e){e.printStackTrace();}}}} catch (IOException e) {e.printStackTrace();check=false;}}}).start();return check;}/*** 上傳.** @param localFilePath 需要上傳的本地文件路徑* @return 上傳結果* @throws IOException*/public static boolean uploadFile(String localFilePath) throws IOException {boolean flag = false;// 二進制文件支持mClient.setFileType(FTPClient.BINARY_FILE_TYPE);// 設置模式mClient.setFileTransferMode(FTPClient.STREAM_TRANSFER_MODE);File localFile = new File(localFilePath);if (localFile.exists() && localFile.isFile()) {flag = uploadingSingle(localFile);}// 返回值return flag;}private static boolean uploadingSingle(File localFile) throws IOException {boolean flag;// 創建輸入流InputStream inputStream = new FileInputStream(localFile);// 上傳單個文件flag = mClient.storeFile(localFile.getName(), inputStream);// 關閉文件流inputStream.close();return flag;}public boolean downloadFile(String localPath,FTPFile ftpFile) throws IOException {boolean result = false;//在本地創建對應文件夾目錄File localFile= new File(localPath);if (!localFile.exists()) {localFile.createNewFile();}result = downloadSingle(localFile, ftpFile);return result;}/*** 下載單個文件,此時ftpFile必須在ftp工作目錄下** @param localFile 本地目錄* @param ftpFile FTP文件* @return true下載成功, false下載失敗* @throws IOException*/public static boolean downloadSingle(File localFile, FTPFile ftpFile) throws IOException {boolean flag;// 創建輸出流File file=new File(localFile,ftpFile.getName());if(!file.exists()){file.createNewFile();}OutputStream outputStream = new FileOutputStream(file);// 下載單個文件flag = mClient.retrieveFile(ftpFile.getName(), outputStream);// 關閉文件流outputStream.close();return flag;}}?? 上述注釋基本都比較詳細就不再贅述其中細節了。下面進入核心的主活動以及其相關代碼部分。
三、主活動代碼
先說明我的代碼的一個重要部分。我們面臨這樣一個問題:因為服務器的文件是用FTPFile類表示的,而本地的文件是用File表示的。這樣我們主活動上用于顯示文件列表的Recyclerview就不能用一個適配器來解決了。因為這個兩各類沒有父子關系和亦或是同一個接口或者父類的子類關系。我的解決方法是:既然不能用一個適配器那就寫兩個適配器,在需要在服務器文件列表和本地文件列表之間切換時,主活動的recyclerview就使用setAdapter()進行切換。這樣就像一個插口一樣既可以插這個適配器,又可以插那個適配器。以下是我的主活動代碼:
public class MainActivity extends AppCompatActivity {private RecyclerView recyclerView;private FileAdapter adapter;private FtpFileAdapter adapter1;private List<File> FileList;private ProgressDialog progressDialog;private DrawerLayout mDrawerLayout;private ConnectBroadcastReceiver connectBroadcastReceiver;private FTPClient mClient;private List<FTPFile> Ftplist=new ArrayList<>();@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);getPermissions();mDrawerLayout=(DrawerLayout)findViewById(R.id.drawer_layout);ActionBar actionBar=getSupportActionBar();if(actionBar!=null){actionBar.setDisplayHomeAsUpEnabled(true);actionBar.setHomeAsUpIndicator(R.drawable.menu);}NavigationView navigationView=(NavigationView) findViewById(R.id.nav_view);navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {@Overridepublic boolean onNavigationItemSelected(@NonNull MenuItem item) {switch (item.getItemId()){case R.id.nav_setting:Intent intent=new Intent(MainActivity.this,FtpConnectActivity.class);startActivity(intent);break;case R.id.nav_FtpServerDirectory:if(mClient!=null&&mClient.isConnected()){new Thread(new Runnable() {@Overridepublic void run() {try {FTPFile files[] = mClient.listDirectories();for (int i = 0; i < files.length; i++) {boolean check = false;for (FTPFile ftpFile : Ftplist) {if (ftpFile.getName().equals(files[i].getName()))check = true;}if (!check)Ftplist.add(files[i]);}runOnUiThread(new Runnable() {@Overridepublic void run() {recyclerView.setAdapter(adapter1);adapter1.notifyDataSetChanged();}});} catch (IOException e) {e.printStackTrace();}}}).start();}break;case R.id.nav_LocalDirectory:recyclerView.setAdapter(adapter);adapter.notifyDataSetChanged();break;}return true;}});progressDialog=new ProgressDialog(this);progressDialog.setTitle("正在復制...");InitData();recyclerView=(RecyclerView)findViewById(R.id.file_list);LinearLayoutManager layoutManager=new LinearLayoutManager(this);recyclerView.setLayoutManager(layoutManager);adapter=new FileAdapter(FileList);adapter1=new FtpFileAdapter(Ftplist,this);recyclerView.setAdapter(adapter);IntentFilter intentFilter=new IntentFilter();intentFilter.addAction("com.app.bhk.connected");connectBroadcastReceiver=new ConnectBroadcastReceiver();registerReceiver(connectBroadcastReceiver,intentFilter);}private void getPermissions(){if(ContextCompat.checkSelfPermission(this,Manifest.permission.WRITE_EXTERNAL_STORAGE)!=PackageManager.PERMISSION_GRANTED){ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);}if(ContextCompat.checkSelfPermission(this,Manifest.permission.ACCESS_COARSE_LOCATION)!=PackageManager.PERMISSION_GRANTED)ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},2);}@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);switch (requestCode){case 1:if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){}else{Toast.makeText(this,"沒有權限打開相冊",Toast.LENGTH_SHORT).show();}case 2:}}private void InitData(){File file =Environment.getExternalStorageDirectory();FileList=new ArrayList<File>( Arrays.asList(file.listFiles()));}@Overridepublic void onBackPressed(){if(recyclerView.getAdapter()==adapter){adapter.LastFile();}else{adapter1.LastFile();}}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.main,menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item){switch (item.getItemId()){case R.id.copy:if(FileUtil.getSaveFile()!=null) {progressDialog.show();new Thread(new Runnable() {@Overridepublic void run() {if(recyclerView.getAdapter()==adapter) {FileUtil.Copy(FileUtil.getSaveFile(), adapter.getCurrentFile());runOnUiThread(new Runnable() {@Overridepublic void run() {progressDialog.dismiss();FileList.add(FileUtil.getSaveFile());adapter.notifyDataSetChanged();}});}else{try {FtpUtil.uploadFile(FileUtil.getSaveFile().getPath());Ftplist.clear();Ftplist.addAll( Arrays.asList(FtpUtil.getmClient().listFiles()));runOnUiThread(new Runnable() {@Overridepublic void run() {progressDialog.dismiss();adapter1.notifyDataSetChanged();}});}catch (IOException e){e.printStackTrace();}}}}).start();}break;case android.R.id.home:mDrawerLayout.openDrawer(GravityCompat.START);break;case R.id.copy_from_server:if(recyclerView.getAdapter()==adapter&&FtpUtil.getFtpFile()!=null) {progressDialog.show();new Thread(new Runnable() {@Overridepublic void run() {try {boolean result= FtpUtil.downloadSingle(adapter.getCurrentFile(), FtpUtil.getFtpFile());runOnUiThread(new Runnable() {@Overridepublic void run() {progressDialog.dismiss();}});if(result) {runOnUiThread(new Runnable() {@Overridepublic void run() {FileList.clear();FileList.addAll(Arrays.asList(adapter.getCurrentFile().listFiles()));adapter.notifyDataSetChanged();}});}else{runOnUiThread(new Runnable() {@Overridepublic void run() {Toast.makeText(MainActivity.this,"復制失敗",Toast.LENGTH_SHORT).show();}});}}catch (IOException e){e.printStackTrace();}}}).start();}break;}return true;}class ConnectBroadcastReceiver extends BroadcastReceiver{@Overridepublic void onReceive(Context context, Intent intent) {switch (intent.getAction().toString()){case "com.app.bhk.connected":Toast.makeText(MainActivity.this,"連接上了",Toast.LENGTH_SHORT).show();new Thread(new Runnable() {@Overridepublic void run() {mClient=FtpUtil.getmClient();adapter1.setFtpClient(mClient);}}).start();break;}}} } 其中:NavigationView是右滑的導航欄。需要提醒的一點是所有的關于ftp服務器的操作都必須要在子線程內進行否則就會報錯的,畢竟這是網絡操作,為了避免主線程阻塞是需要在子線程操作的。這里我寫了廣播接受器,用來接收登錄成功的廣播以便獲取FTPClient對象。關于退回上一級目錄的功能我是用重寫手機的返回鍵實現的,具體看onBackPressed()這個方法。?? 下面看看兩個適配器類:?
public class FtpFileAdapter extends RecyclerView.Adapter<FtpFileAdapter.ViewHolder> {private List<FTPFile> FtpFilelist;private StringBuffer CurrentFile=new StringBuffer();private FTPClient ftpClient;private MainActivity mainActivity;public void LastFile(){//切換到父目錄new Thread(new Runnable() {@Overridepublic void run() {try {int i= CurrentFile.toString().lastIndexOf('/');if(i>=0) {CurrentFile.delete(i, CurrentFile.length());boolean f=FtpUtil.getmClient().changeToParentDirectory();Log.d("test1234"," "+f+" "+CurrentFile);}FTPFile files[]=FtpUtil.getmClient().listFiles();FtpFilelist.clear();FtpFilelist.addAll(Arrays.asList(files));mainActivity.runOnUiThread(new Runnable() {@Overridepublic void run() {notifyDataSetChanged();return;}});}catch (Exception e){e.printStackTrace();}}}).start();}public FTPClient getFtpClient() {return ftpClient;}public void setFtpClient(FTPClient ftpClient) {this.ftpClient = ftpClient;}static class ViewHolder extends RecyclerView.ViewHolder{private ImageView File_imageview;private TextView File_textView;public ViewHolder(View v){super(v);File_imageview=(ImageView) v.findViewById(R.id.file_image);File_textView=(TextView) v.findViewById(R.id.file_name);}}public FtpFileAdapter(List<FTPFile> list,MainActivity activity){FtpFilelist=list;this.mainActivity=activity;}@Overridepublic int getItemCount() {return FtpFilelist.size();}@NonNull@Overridepublic ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.file_item1,parent,false);final ViewHolder viewHolder=new ViewHolder(view);view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {final FTPFile file=FtpFilelist.get(viewHolder.getAdapterPosition());if(file.isDirectory()) {new Thread(new Runnable() {@Overridepublic void run() {try {CurrentFile.append("/"+file.getName());boolean f= FtpUtil.getmClient().changeWorkingDirectory(CurrentFile.toString()); //更新文件列表FTPFile files[] = FtpUtil.getmClient().listFiles();FtpFilelist.clear();FtpFilelist.addAll(Arrays.asList(files));Log.d("test123", "11212"+f+" "+CurrentFile);mainActivity.runOnUiThread(new Runnable() {@Overridepublic void run() {notifyDataSetChanged();return;}});}catch (Exception e){e.printStackTrace();}}}).start();Log.d("ftpfile1","good");// Toast.makeText(getContext(),"hello",Toast.LENGTH_SHORT).show();}}});view.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {//創建彈出式菜單對象(最低版本11)PopupMenu popup = new PopupMenu(getContext(), v);//第二個參數是綁定的那個view//獲取菜單填充器final MenuInflater inflater = popup.getMenuInflater();//填充菜單inflater.inflate(R.menu.operation_ftp, popup.getMenu());//綁定菜單項的點擊事件popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {@Overridepublic boolean onMenuItemClick(MenuItem item) {FTPFile file=FtpFilelist.get(viewHolder.getAdapterPosition());switch (item.getItemId()){case R.id.copy_operate:FtpUtil.setFtpFile(file);break;}//notifyDataSetChanged();return true;}});popup.show();//顯示(這一行代碼不要忘記了)return true;}});return viewHolder;}@Overridepublic void onBindViewHolder(@NonNull ViewHolder holder, int position) {FTPFile ftpFile=FtpFilelist.get(position);holder.File_textView.setText(ftpFile.getName());if(ftpFile.isDirectory()){holder.File_imageview.setImageDrawable(getContext().getDrawable(R.drawable.directory));}else{holder.File_imageview.setImageDrawable(getContext().getDrawable(R.drawable.file));}}}?
public class FileAdapter extends RecyclerView.Adapter<FileAdapter.ViewHolder> {private List<File> arraylist;public File getCurrentFile() {return CurrentFile;}private File CurrentFile=Environment.getExternalStorageDirectory();//當前的文件夾static class ViewHolder extends RecyclerView.ViewHolder{ImageView imageView;TextView textView;public ViewHolder(View v){super(v);imageView=(ImageView)v.findViewById(R.id.file_image);textView=(TextView)v.findViewById(R.id.file_name);}}public FileAdapter(List<File> arraylist){this.arraylist=arraylist;}@Overridepublic FileAdapter.ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, int viewType) {View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.file_item,parent,false);final ViewHolder holder =new ViewHolder(view);view.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {File file=arraylist.get(holder.getAdapterPosition());if(file.isDirectory()) {CurrentFile=file;arraylist.clear();arraylist.addAll(Arrays.asList(file.listFiles()));notifyDataSetChanged();}}});view.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {//創建彈出式菜單對象(最低版本11)PopupMenu popup = new PopupMenu(getContext(), v);//第二個參數是綁定的那個view//獲取菜單填充器final MenuInflater inflater = popup.getMenuInflater();//填充菜單inflater.inflate(R.menu.operation, popup.getMenu());//綁定菜單項的點擊事件popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {@Overridepublic boolean onMenuItemClick(MenuItem item) {File file=arraylist.get(holder.getAdapterPosition());switch (item.getItemId()){case R.id.copy_operate:FileUtil.setSaveFile(file);break;case R.id.delete_operate:arraylist.remove(file);file.delete();break;}notifyDataSetChanged();return true;}});popup.show();//顯示(這一行代碼不要忘記了)return true;}});return holder;}@Overridepublic void onBindViewHolder(@NonNull FileAdapter.ViewHolder holder, int position) {File file=arraylist.get(position);holder.textView.setText(file.getName());if(file.isFile()){holder.imageView.setImageDrawable(getContext().getDrawable(R.drawable.file));}else{holder.imageView.setImageDrawable(getContext().getDrawable(R.drawable.directory));}}@Overridepublic int getItemCount() {return arraylist.size();}public void LastFile(){if (!CurrentFile.equals(Environment.getExternalStorageDirectory())){CurrentFile=CurrentFile.getParentFile();arraylist.clear();arraylist.addAll(Arrays.asList(CurrentFile.listFiles()));notifyDataSetChanged();}} }?
?在這兩個適配器類里我都定義了一個CurrentFile用來保存當前目錄地址,方便回到上一個目錄,以及下載文件和上傳文件的時的操作。
本地文件適配器主要編寫了長按復制以及刪除的功能。復制功能大概實現邏輯是,先把想要復制的文件存放到FileUtil中然后到了想要粘貼的目錄后再進行IO操作(我的粘貼功能寫在menu的觸發事件里),如果是要復制到服務器上就必須使用相關FtpUtil的上傳方法了,這里就需要在主活動里通過對當前recyclerview的適配器進行判斷以便判斷是復制到服務器還是本地以決定作何操作。刪除就很簡單了不贅述。
服務器文件適配器主要編寫了長按復制功能,這里主要是復制到本地文件。和本地差不多不過是先保存到FtpUtil然后切換到本地文件到想要的目錄進行調用FTPUtil的方法就可以了。
源碼有人需要可以評論。看情況更新文章。
項目地址:https://github.com/DhyanaCoder/FileFtp
給本文章或者github項目點個贊!(*^__^*) 嘻嘻……
總結
以上是生活随笔為你收集整理的安卓端简易FTP客户端APP开发的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 雷达物位计要如何选型
- 下一篇: 拓扑绝缘体 量子计算机,纳米人-一天3篇