先上效果图:

本文的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开发相关推荐

  1. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 4

    前文: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 3 导航: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 The E ...

  2. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 7

    前文: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 6 导航: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 The E ...

  3. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 8

    前文: ​​​​​​桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 7 导航: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 ...

  4. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 The End 导航页及收尾工作

    导航: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 1 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 2 桃词 ...

  5. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 1

    前言 由于笔者操作不当,将项目搞崩了,所以打算重写一遍,记下开发过程,作为学习记录.此软件能实现最普通的单词查询功能,也有启动动画.登录注册之类的功能,但笔者目前能力有限,未能将其完善,这是初学阶段的 ...

  6. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 9

    前文: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 8 导航: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 The E ...

  7. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 3

    前文: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 2 导航: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 The E ...

  8. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 5

    前文: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 4 导航: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 The E ...

  9. 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 6

    前文: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 Part 5 导航: 桃词典 Peach Dictionary 简易英语词典app开发 安卓软件开发 The E ...

最新文章

  1. 2014.12.01 B/S之windows8.1下安装IIS
  2. 青龙羊毛——利分闪电(搬运)
  3. Jquery.load() 使用
  4. CF183C:Diverse Permutation(构造)
  5. 华为oj题目c语言,华为OJ机试题目——24点游戏算法
  6. 【小程序】【Tips】【前端】交互【后端】从Json对象数组里面获取数据的方法 - PHP - JS 的原生方法
  7. iOS界面设计之基础控件的学习 --- UITextField
  8. 全网最全的 Java各类技术栈 架构图汇总(建议收藏)
  9. python中变量的生命周期
  10. 如何创造一门编程语言?
  11. js声明数组的四种方式
  12. 支付宝sdk——python-alipay-sdk
  13. 厦门大学353卫生综合考研参考书目
  14. 计算机9网络连接不上,本地连接连不上,教您电脑本地连接连不上怎么解决
  15. linux物联网项目,6个开源项目提升物联网开发效率
  16. vscode安装open in browser报错
  17. html大作业网页代码 ——2019凡客服装店铺商城(1页) HTML+CSS+JavaScript HTML+CSS大作业_ 服装店铺网页制作作业_购物网页设计...
  18. 真正的IT技术男是什么样的
  19. 计算机网络管理工程师含金量高吗,软考中级哪个含金量高?
  20. 基于人工智能算法实现AI足球比赛

热门文章

  1. 介绍 Golang 日志处理
  2. Android UI设计原则
  3. 【Tableau 图表大全30】之双柱图 和 折线图 的组合图表
  4. HTML实操-音乐播放器
  5. STM32CubeIDE不支持生成Keil工程
  6. 服务端如何推送消息给客户端?
  7. 如何清洁Mac键盘,显示器等
  8. Ant Design Pro V5精讲(实践篇一):自定义登录界面、主界面
  9. 使用 SQL 实现同比环比分析
  10. 【Qt】Qt MQTT文档