赞
踩
该项目是一个Android项目,适合新手,运用了很多的常见组件和布局。
项目亮点在于运用了百度网盘的API,通过授权可以在APP中展示授权账号的百度网盘里面的内容,展示的效果如文中图片所示。除此之外还有登录页运用缓存登录,还有历史记录页等等。
适用范围:
1.用于完成学校Android作业的朋友。(代码量大,逼格拉满)
2.需要一个Android项目来学习技术的朋友。
3.想要写一个网盘之类软件但是自己不想写界面的朋友,可以再此之上继续自己添加内容。
4.想要使用百度网盘API,但是具体不知道怎么操作的朋友。
写这个项目是为了继续打打android的基础,另外还了解到了一些新方法和小技巧所以想来用用练练手,比如调用百度api来访问个人百度网盘中的头像名称项目文件等等,继续深入还可以在App中修改个人百度网盘中的文件。
结构是
只是主观简单的模仿了一下,可能有点丑。
activity_main
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".activity.MainActivity"> <TextView app:layout_constraintTop_toTopOf="parent" android:id="@+id/txt_title1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="登录" android:gravity="center" android:textSize="30sp" android:textColor="#fff" android:background="@color/colorBtnEnable" android:padding="5dp" /> <EditText android:padding="10dp" android:id="@+id/et_login_user" android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/txt_title" android:hint="请输入手机号/用户名/邮箱" android:background="@drawable/bac_1" android:layout_margin="@dimen/marginSize" tools:ignore="MissingConstraints" /> <EditText android:padding="10dp" android:id="@+id/et_login_psw" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="密码" android:background="@drawable/bac_1" android:layout_margin="@dimen/marginSize" app:layout_constraintTop_toBottomOf="@+id/et_login_user" tools:ignore="MissingConstraints" /> <Button android:id="@+id/btn_login" android:layout_width="match_parent" android:layout_height="48dp" android:enabled="false" android:text="登录" android:background="@drawable/bac_2" android:layout_margin="@dimen/marginSize" app:layout_constraintTop_toBottomOf="@+id/et_login_psw" tools:ignore="MissingConstraints" /> <ImageButton android:foregroundGravity="center" android:id="@+id/ib_login_psw_visible" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintTop_toBottomOf="@id/et_login_user" app:layout_constraintBottom_toBottomOf="@+id/et_login_psw" app:layout_constraintRight_toRightOf="@id/et_login_psw" /> <ImageView android:id="@+id/img" android:src="@mipmap/baidu_resultlogo" app:layout_constraintTop_toBottomOf="@+id/txt_title1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="@dimen/marginSize" tools:ignore="MissingConstraints" /> <TextView android:id="@+id/txt_title" android:layout_width="wrap_content" app:layout_constraintTop_toBottomOf="@id/img" android:layout_height="wrap_content" android:text="欢迎登录百度账号" android:textColor="#000" android:textSize="35sp" android:layout_margin="@dimen/marginSize" tools:ignore="MissingConstraints" /> </androidx.constraintlayout.widget.ConstraintLayout>
然后在这个界面中做一些设置。
在这里插入代码片import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.text.method.HideReturnsTransformationMethod; import android.text.method.PasswordTransformationMethod; import android.view.View; import android.view.Window; import android.view.WindowManager; import android.widget.Button; import android.widget.EditText; import android.widget.ImageButton; import android.widget.Toast; import com.example.baidudisk.R; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ EditText mUserEt,mPswEt; Button mLoginBtn; private ImageButton mPswVisibleBtn; private boolean mPswVisible=false; private static final String SP_IS_LOGIN="is_login"; private static final String USER="user"; private static final String PSW="psw"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setStatusBarFullTransparent(); mPswVisibleBtn=findViewById(R.id.ib_login_psw_visible); mLoginBtn=findViewById(R.id.btn_login); mUserEt=findViewById(R.id.et_login_user); mPswEt=findViewById(R.id.et_login_psw); setPswVisible(); mUserEt.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { setBtnBg(s); } }); mPswEt.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { setBtnBg(s); } }); mLoginBtn.setOnClickListener(this); mPswVisibleBtn.setOnClickListener(this); } private void setPswVisible() { mPswVisibleBtn.setBackgroundResource(mPswVisible?R.mipmap.open:R.mipmap.hide); mPswEt.setTransformationMethod(mPswVisible? HideReturnsTransformationMethod.getInstance(): PasswordTransformationMethod.getInstance()); mPswEt.setSelection(mPswEt.getText().toString().length()); mPswVisible=!mPswVisible; } private void setBtnBg(Editable s) { if(mUserEt.getText().length()>0&&mPswEt.getText().length()>0) { mLoginBtn.setEnabled(true); mLoginBtn.setBackgroundColor(getResources().getColor(R.color.colorBtnEnable)); mLoginBtn.setTextColor(Color.WHITE); } else { mLoginBtn.setEnabled(false); mLoginBtn.setBackgroundColor(getResources().getColor(R.color.colorBtnDissablr)); mLoginBtn.setTextColor(getResources().getColor(R.color.colorTextDisable)); } } //状态栏透明 protected void setStatusBarFullTransparent() { if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.LOLLIPOP) { Window window=getWindow(); window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN|View.SYSTEM_UI_FLAG_LAYOUT_STABLE); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); window.setStatusBarColor(Color.TRANSPARENT); }else if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.KITKAT){ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_login: if("swust".equals(mPswEt.getText().toString().trim())&&"swust".equals(mUserEt.getText().toString().trim())) { saveUserNameAndPsw(); Toast.makeText(MainActivity.this,"登录成功",Toast.LENGTH_SHORT).show(); Intent intent =new Intent(MainActivity.this,HomeActivity.class); intent.putExtra("str1",mUserEt.getText().toString()); startActivity(intent); }else Toast.makeText(MainActivity.this,"登录失败",Toast.LENGTH_SHORT).show(); break; case R.id.ib_login_psw_visible: setPswVisible(); break; } } /** * 保存用户名和密码 */ private void saveUserNameAndPsw(){ getSharedPreferences(SP_IS_LOGIN, Context.MODE_PRIVATE) .edit() .putString(USER,mUserEt.getText().toString()) .putString(PSW,mPswEt.getText().toString()) .apply(); } /** * 填充用户名和密码 */ private void fillUserNameAndPsw(){ String user= getSharedPreferences(SP_IS_LOGIN,MODE_PRIVATE) .getString(USER,""); String psw =getSharedPreferences(SP_IS_LOGIN,MODE_PRIVATE) .getString(PSW,""); mUserEt.setText(user); mPswEt.setText(psw); mUserEt.setSelection(mUserEt.getText().length()); } @Override protected void onStart() { super.onStart(); fillUserNameAndPsw(); } }
该页面中实现的功能有:
1.当账号和密码都有内容时按钮会变成蓝色。
2.点击密码右侧的眼睛按钮可以实现密码可见与隐藏的切换。
3.使用SharedPreferences实现对账号和密码的存储并在下次启动时直接填充在文本框中。这次并没有实现注册功能,在上个项目中已经有例子了。
在这个项目主页面只实现两个子页面,上个项目中运用的是viewpager+fragment的方法,在这个项目中,为了简单起见只采用两个fragment实现页面即可。
一个是文件列表,一个是个人中心。
登录成功之后跳转到HomeActivity
import androidx.appcompat.app.AppCompatActivity; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentTransaction; import android.os.Bundle; import android.view.View; import com.example.baidudisk.R; public class HomeActivity extends AppCompatActivity{ FileFragment fileFragment=new FileFragment(); ProFileFragment profileFragment=new ProFileFragment(); FragmentManager fragmentManager=getSupportFragmentManager(); FragmentTransaction transaction =fragmentManager.beginTransaction(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_home); transaction.add(R.id.fragment,fileFragment); transaction.commit(); } public void mePage(View view) { changeFragment(R.id.fragment,profileFragment); } public void filePage(View view) { changeFragment(R.id.fragment,fileFragment); } void changeFragment(int resource,Fragment fragment) { FragmentManager fragmentManager=getSupportFragmentManager(); FragmentTransaction transaction =fragmentManager.beginTransaction(); transaction.replace(resource,fragment); transaction.commit(); } }
实现了两个fragment之间的切换。(我认为这算是最简单的子界面的切换方式)
然后是他的布局文件
activity_home
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/re_1" android:layout_width="match_parent" android:layout_height="match_parent" > <FrameLayout android:id="@+id/fragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/daohang" > </FrameLayout> <LinearLayout android:id="@+id/daohang" android:layout_alignParentBottom="true" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" android:layout_gravity="bottom"> <LinearLayout android:id="@+id/ll_file" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" android:onClick="filePage" android:foreground="?attr/selectableItemBackground" > <ImageView android:id="@+id/iv_bottom_file" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" android:src="@mipmap/bottombar_btn_file_pre" /> <TextView android:id="@+id/tv_bottom_file" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="文件" android:layout_gravity="center_horizontal" android:layout_marginBottom="2dp" android:textSize="12sp" android:textColor="@color/colorBtnEnable" /> </LinearLayout> <LinearLayout android:id="@+id/ll_me" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:orientation="vertical" android:foreground="?attr/selectableItemBackground" android:onClick="mePage" > <ImageView android:id="@+id/iv_bottom_me" android:layout_width="24dp" android:layout_height="24dp" android:layout_gravity="center_horizontal" android:layout_marginTop="8dp" android:src="@mipmap/swan_app_user_portrait_pressed" /> <TextView android:id="@+id/tv_bottom_me" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="我的" android:layout_gravity="center_horizontal" android:layout_marginBottom="2dp" android:textSize="12sp" android:textColor="@color/colorBtnEnable" /> </LinearLayout> </LinearLayout> </RelativeLayout>
这个帧布局就作为fragment放的地方。
接下来就要写这两个布局
先是文件碎片
FileFragment
在这个碎片中需要实现的是一个recycleview视图显示文件,在最开始我只是简单模拟一下几个文件,但后面感觉太单调了就改成了读取并显示本机的文件了。读取本机的文件需要注意的是要获取相关的权限:
private void requestPermissions() {
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.JELLY_BEAN && getActivity()!= null) {
if (ContextCompat.checkSelfPermission(getActivity(),
Manifest.permission.READ_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED){
//获取权限后的操作
}else{
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},0);
}
}
}
通过mCurrPath=Environment.getExternalStorageDirectory().getAbsolutePath();获取本机的文件的绝对路径
import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.example.baidudisk.R; import com.example.baidudisk.adapter.FileAdapter; import com.example.baidudisk.adapter.MyFileAdapter; import com.example.baidudisk.enity.FileEntity; import com.example.baidudisk.enity.FileListEntity; import com.example.baidudisk.intent.NetworkInterfaces; import com.google.gson.Gson; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Stack; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Response; public class FileFragment extends Fragment implements View.OnClickListener{ int mLevel=0; LinearLayout linearLayout; private Handler mHandler; private List<FileEntity> fileEntityList=new ArrayList<>(); private List<FileListEntity.ListBean> mFiles=new ArrayList<FileListEntity.ListBean>(); FileAdapter mAdapter; String mCurrPath; MyFileAdapter myFileAdapter; private TextView mTip; Stack<String> mPathStack=new Stack<>(); RecyclerView mFilesRV; SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.fragment_file,container,false); mHandler=new Handler(); mFilesRV =view.findViewById(R.id.rv_files); mAdapter=new FileAdapter(fileEntityList); myFileAdapter=new MyFileAdapter(mFiles); mTip=view.findViewById(R.id.mTipTv); linearLayout =view.findViewById(R.id.ll_1); linearLayout.setOnClickListener(this); LinearLayoutManager layoutManager=new LinearLayoutManager(getContext()); mFilesRV.setLayoutManager(layoutManager); mFilesRV.setAdapter(mAdapter); showExternalFiles(); requestPermissions(); mAdapter.setListener(new FileAdapter.setOnClickListener() { @Override public void onClick(int position) { if (getActivity()!=null&&fileEntityList.get(position).type!=R.mipmap.file){ mLevel++; mCurrPath=mCurrPath+"/"+fileEntityList.get(position).name; showSubFiles(); } } }); view.setFocusableInTouchMode(true); view.requestFocus(); /** * 后面需要注释 */ view.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction()== KeyEvent.ACTION_DOWN&&keyCode==KeyEvent.KEYCODE_BACK&&mLevel>0){ File file =new File(mCurrPath).getParentFile(); if(file!=null){ mCurrPath=file.getAbsolutePath(); mLevel--; showSubFiles(); return true; } } return false; } }); /** * 后面需要显示 */ /*myFileAdapter.setListener(new MyFileAdapter.setOnClickListener() { @Override public void onClick(int position) { if (mFiles.get(position).getIsdir()==1){ forward(mFiles.get(position).getPath()); } } }); view.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction()== KeyEvent.ACTION_DOWN&&keyCode==KeyEvent.KEYCODE_BACK){ if (!mPathStack.empty()){ back(); return true; } } return false; } }); getFileList("/");*/ return view; } /** * 获取权限 */ private void requestPermissions() { if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.JELLY_BEAN && getActivity()!= null) { if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE)==PackageManager.PERMISSION_GRANTED){ //获取权限后的操作 }else{ requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},0); } } } @Override public void onClick(View v) { switch (v.getId()) { case R.id.ll_1: startActivity(new Intent(getContext(),SearchActivity.class)); break; } } private void showExternalFiles(){ mCurrPath=Environment.getExternalStorageDirectory().getAbsolutePath(); showSubFiles(); } private void showSubFiles(){ File file =new File(mCurrPath); if (file.exists() && file.isDirectory()) { File[] sub =file.listFiles(); if (sub!=null){ fileEntityList.clear(); for(File f:sub){ fileEntityList.add(new FileEntity(f.isDirectory()?R.mipmap.ic_gc_main_empty_folder:R.mipmap.file,f.getName(),simpleDateFormat.format(new Date(f.lastModified())))); } }else{ fileEntityList.clear(); } mAdapter.notifyDataSetChanged(); } } //下面的代码在后面需要显示 /*private void showLoading(){ mHandler.post(new Runnable() { @Override public void run() { mFilesRV.setVisibility(View.GONE); mTip.setText("正在加载......."); mTip.setVisibility(View.VISIBLE); } }); } private void showResult(){ mHandler.post(new Runnable() { @Override public void run() { mFilesRV.setVisibility(View.VISIBLE); mTip.setVisibility(View.INVISIBLE); myFileAdapter.notifyDataSetChanged(); } }); } private void showError(){ mHandler.post(new Runnable() { @Override public void run() { mFilesRV.setVisibility(View.GONE); mTip.setVisibility(View.VISIBLE); mTip.setText("获取文件失败"); } }); } private void getFileList(String path){ showLoading(); new NetworkInterfaces().getFileListALL(path, new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { showError(); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { FileListEntity entity=new Gson().fromJson(response.body().string(),FileListEntity.class); if(entity.getErrno()==0){ showFileList(entity.getList()); } else { showError(); System.out.println(entity.getErrmsg()); } } }); } private void showFileList(List<FileListEntity.ListBean> beans) { mFiles.clear(); mFiles.addAll(beans); mHandler.post(new Runnable() { @Override public void run() { mFilesRV.setAdapter(myFileAdapter); myFileAdapter.notifyDataSetChanged(); if (mFiles.isEmpty()){ Toast.makeText(getContext(),"没有文件!",Toast.LENGTH_SHORT).show(); }else { showResult(); } } }); } private void forward(String path){ mCurrPath=path; mPathStack.push(path); getFileList(mCurrPath); } private void back(){ mPathStack.pop(); if (!mPathStack.empty()){ mCurrPath=mPathStack.peek(); mPathStack.pop(); }else { mCurrPath="/"; } getFileList(mCurrPath); }*/ }
FileAdapter
import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.example.baidudisk.enity.FileEntity; import com.example.baidudisk.R; import java.util.List; public class FileAdapter extends RecyclerView.Adapter<FileAdapter.ViewHolder> { public interface setOnClickListener { void onClick(int position); } private List<FileEntity> mFileList; public setOnClickListener myClickListener; public void setListener(setOnClickListener s) { myClickListener = s; } static class ViewHolder extends RecyclerView.ViewHolder { ImageView mFileTypePic; TextView mFileName; TextView mFileCreationDate; LinearLayout mRootLl; public ViewHolder(@NonNull View itemView) { super(itemView); mFileTypePic = itemView.findViewById(R.id.re_pic); mFileName = itemView.findViewById(R.id.re_name); mFileCreationDate = itemView.findViewById(R.id.re_creation_time); mRootLl = itemView.findViewById(R.id.ll_mRootLl); } } public FileAdapter(List<FileEntity> filelist) { mFileList = filelist; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_file, parent, false); final ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, final int position) { FileEntity fileEntity = mFileList.get(position); holder.mFileTypePic.setImageResource(fileEntity.getType()); holder.mFileName.setText(fileEntity.getName()); holder.mFileCreationDate.setText(fileEntity.getDate()); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myClickListener.onClick(position); } }); } @Override public int getItemCount() { return mFileList.size(); } }
不同的手机所显示的文件应该不同,但是实现文件通过level实现前进倒退的逻辑在代码中也很清晰,这里不再赘述。
因为代码中有些是后面运用百度api展示文件列表的代码所以就先注释掉了。
然后就是大概的一个布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:id="@+id/ll_1" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="32dp" android:layout_marginBottom="16dp" android:background="@drawable/bac_1" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" > <ImageButton android:layout_width="24dp" android:layout_height="24dp" android:layout_margin="8dp" android:src="@mipmap/search" android:layout_gravity="center_vertical" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginRight="16dp" android:text="搜索我的网盘文件" android:textSize="16sp" /> </LinearLayout> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_files" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="16sp" android:layout_marginRight="16sp" /> <TextView android:id="@+id/mTipTv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:visibility="gone" android:text="正在加载......."/> </LinearLayout>
在这个页面中还存在一个搜索框,点击搜索框会进入搜索页面。搜索页面也是又两部分组成,一是搜索框,必须具备一键删除文本的功能。
而是列表视图用来显示搜索历史,在下方也必须要有一键清除历史记录的功能。
梳理一下逻辑就是:
当文本框中有内容时,取消二字要能变成搜索二字,如果点击旁边的叉叉就一键删除文本内容。当没有文本时不会显示叉叉。
如果是点击搜索文字之后,文本框中的文本记录要能更新到下方的视图中。(还没有实现真的定位到所搜索的文件)
采用的是sqlite存储和删除记录。之所以采用数据库的原因是为了,后面再次进入应用时之前的记录依然存在,否则就没有实现的必要了。
点击碎片上方搜索框进入搜索页面
SearchActivity
import androidx.appcompat.app.AppCompatActivity; import androidx.core.content.ContextCompat; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import android.content.ContentValues; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.MotionEvent; import android.view.View; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; import com.example.baidudisk.db.DBHelper; import com.example.baidudisk.R; import com.example.baidudisk.adapter.SearchAdapter; import java.util.ArrayList; import java.util.List; public class SearchActivity extends AppCompatActivity implements View.OnTouchListener { private final static String TABLE_NAME="search_history"; private final static String COLUMN_NAME="name"; private static String DB_NAME ="history.db"; private static int DB_VERSION =1 ; TextView cancel_serach; EditText mEtSearch; SearchAdapter searchAdapter; RecyclerView recyclerView; TextView deleteAll; Drawable delete; private List<String> historyList; SQLiteDatabase mDB; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_search); recyclerView=findViewById(R.id.rv_search); cancel_serach=findViewById(R.id.cancel_search); mEtSearch=findViewById(R.id.et_search); historyList=new ArrayList<>(); deleteAll=findViewById(R.id.deleteAll); searchAdapter=new SearchAdapter(historyList); recyclerView.setLayoutManager(new LinearLayoutManager(this)); recyclerView.setAdapter(searchAdapter); queryAllHistory(); delete= ContextCompat.getDrawable(this,R.mipmap.deleteall); delete.setBounds(0,0,delete.getIntrinsicWidth()*3/4,delete.getIntrinsicHeight()*3/4); searchAdapter.setClick(new SearchAdapter.Click() { @Override public void Delete(int position) { deleteHistory(historyList.get(position)); historyList.remove(historyList.get(position)); searchAdapter.notifyDataSetChanged(); } @Override public void AddText(int position) { mEtSearch.setText(historyList.get(position)); mEtSearch.setSelection(mEtSearch.getText().length()); } }); deleteAll.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { deleteALLHistory(); historyList.clear(); //清除记录 searchAdapter.notifyDataSetChanged(); } }); mEtSearch.setImeOptions(EditorInfo.IME_ACTION_SEARCH); cancel_serach.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String str= cancel_serach.getText().toString(); if(str.equals("搜索")){ search(); // historyList.add(new SearchEnity(mEtSearch.getText().toString())); searchAdapter.notifyDataSetChanged(); }else{ finish(); } } }); mEtSearch.setOnTouchListener(this); mEtSearch.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override public void afterTextChanged(Editable s) { setDelete(); } }); } public void queryAllHistory(){ mDB=new DBHelper(this,DB_NAME,null,DB_VERSION) .getWritableDatabase(); Cursor cursor=mDB.query(TABLE_NAME,new String[]{COLUMN_NAME},null,null,null,null,null); while (cursor.moveToNext()){ historyList.add(cursor.getString(cursor.getColumnIndex(COLUMN_NAME))); } cursor.close(); searchAdapter.notifyDataSetChanged(); } /** * * @param name 需要插入的字符串 */ private void insertHistory(String name){ //通过键值对插入数据项 ContentValues values=new ContentValues(); values.put(COLUMN_NAME,name); mDB.insert(TABLE_NAME,null,values); } /** * * @param name 需要删除的字符串 */ private void deleteHistory(String name){ mDB.delete(TABLE_NAME,COLUMN_NAME+"=?",new String[]{name}); } private void deleteALLHistory(){ mDB.execSQL("delete from "+TABLE_NAME); } /** * 搜索 * 向搜索历史添加数据 */ private void search(){ String content =mEtSearch.getText().toString(); if (content.length()>0&&!historyList.contains(content)){ historyList.add(content); searchAdapter.notifyDataSetChanged(); insertHistory(content); } } private void setDelete() { if(mEtSearch.getText().length()>0) { mEtSearch.setCompoundDrawables(null, null, delete, null); cancel_serach.setText("搜索"); } else{ mEtSearch.setCompoundDrawables(null,null,null,null); cancel_serach.setText("取消"); } } @Override public boolean onTouch(View v, MotionEvent event) { if (event.getAction()==MotionEvent.ACTION_DOWN) { if(event.getRawX()>v.getWidth()-v.getPaddingRight()-delete.getIntrinsicWidth()) { mEtSearch.setText(""); } } return false; } }
activity_search
<?xml version="1.0" encoding="utf-8"?> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"> <LinearLayout android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="32dp" android:layout_marginBottom="16dp" android:background="@drawable/bac_1" android:layout_marginLeft="16dp" android:layout_marginRight="16dp" > <ImageButton android:layout_width="24dp" android:layout_height="24dp" android:layout_margin="8dp" android:src="@mipmap/search" android:layout_gravity="center_vertical" /> <EditText android:id="@+id/et_search" android:background="@null" android:layout_width="0dp" android:layout_weight="4" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginRight="16dp" android:hint="搜索我的网盘文件" android:textColor="#000" android:textSize="16sp" /> <TextView android:id="@+id/cancel_search" android:layout_width="0dp" android:clickable="true" android:layout_weight="1" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="取消" android:gravity="center" android:textStyle="bold" android:textSize="20sp" android:textColor="#0968F7" /> </LinearLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="搜索标题" android:textStyle="bold" android:textSize="25sp"/> <androidx.recyclerview.widget.RecyclerView android:id="@+id/rv_search" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="5dp" /> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <TextView android:id="@+id/deleteAll" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="清除所有历史记录" android:gravity="center" android:textSize="20sp" android:layout_gravity="bottom" android:textColor="@color/colorAccent"/> </LinearLayout> </LinearLayout>
在实现点击小叉叉消除文本时采用的是重写Ontouch,当所按下的位置处于小叉的位置时,清空文本。
搜索记录所需要的用到的适配器
import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.example.baidudisk.R; import java.util.List; public class SearchAdapter extends RecyclerView.Adapter<SearchAdapter.ViewHolder>{ public interface Click{ void Delete(int position); void AddText(int position); } Click click; public void setClick(Click click) { this.click = click; } private List<String> historyList; public SearchAdapter(List<String> historyList){ this.historyList=historyList; } static class ViewHolder extends RecyclerView.ViewHolder{ ImageButton imageDelete; ImageView imageTime; TextView textName; public ViewHolder(@NonNull View itemView) { super(itemView); imageDelete=itemView.findViewById(R.id.delete); imageTime =itemView.findViewById(R.id.re_time); textName=itemView.findViewById(R.id.search_name); } } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.search_item,parent,false); ViewHolder viewHolder =new ViewHolder(view); return viewHolder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, final int position) { String str =historyList.get(position); holder.textName.setText(str); holder.imageTime.setImageResource(R.mipmap.time); holder.imageDelete.setImageResource(R.mipmap.deleteall); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { click.AddText(position); } }); holder.imageDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { click.Delete(position); } }); } @Override public int getItemCount() { return historyList.size(); } }
创建接口,传入position,从而在实现接口时,更新搜索记录视图。在相应的位置同时做好数据库的相关操作。
再次进入app,这些记录也不会消失因为已经进入到了数据库中。另外点击这些搜索记录能够直接将文本快速显示到搜索框中。
这样搜索页面算是大功告成。
接下来就是个人页面,我写的相当简单。没什么好说的。
简单布局+简单控件+网格视图
这代码就没什么发的。如图:
直到这里,一个简单的雏形也就形成了。因为还是没有涉及到网络,所以就又加了一个功能,就是通过百度网盘的api将自己网盘中的头像,名称,存储容量显示在个人页面中。把网盘的文件显示在文件列表中,同时也实现文件的前进与后退。
要使用百度云盘的api就要先成为百度开发者,注册一个就可以了。
在百度开发者的应用管理中创建应用从而获得一个api key。
例如
然后通过浏览器进入下面这个网址
https://openapi.baidu.com/oauth/2.0/authorize?response_type=token&client_id=这里填你的api key&redirect_uri=oob&scope=netdisk
然后会有授权信息,授权之后网址栏上的网址会发生变化。
这时候需要保存好access_token=后面的那一小段,这个也就是拿到的令牌。有了这个授权令牌之后就不在需要通过账号密码访问网盘了直接就可以通过令牌访问百度网盘。
具体情况可以看官方文档百度网盘官方技术文档https://pan.baidu.com/union/document/entrance
拿到令牌之后就可以继续进行下面的操作了
创建一个接口类,定义一些请求方法。
NetworkInterfaces
import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import okhttp3.Callback; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; import okhttp3.Request; public class NetworkInterfaces { private static final int CONNECT_TIME_OUT=8; private OkHttpClient mClient; private final static String ACCESS_TOKEN="这里填入你的令牌"; //下面这些请求地址都是可以在官方文档中查到的 private final static String HOST="https://pan.baidu.com"; private final static String SYNC_INFO="/rest/2.0/xpan/nas?method=uinfo"; private final static String SPACE="/api/quota"; private final static String FILE_LIST_ALL="/rest/2.0/xpan/multimedia?method=listall"; private OkHttpClient getOkHttpClient(){ if (mClient==null){ mClient=new OkHttpClient.Builder() .connectTimeout(CONNECT_TIME_OUT, TimeUnit.SECONDS) .build(); } return mClient; } public void sendGetHttpRequest(String url, Map<String,String> params, Callback callback){ HttpUrl.Builder builder=HttpUrl.get(url).newBuilder(); for (String key :params.keySet()){ builder.addQueryParameter(key,params.get(key)); } Request request=new Request.Builder() .url(builder.build()) .get() .build(); getOkHttpClient().newCall(request).enqueue(callback); } //获取用户信息 public void syncUserInfo(Callback callback){ Map<String,String> param =new HashMap<>(); param.put("access_token",ACCESS_TOKEN); sendGetHttpRequest(HOST+SYNC_INFO,param,callback); } //获取网盘容量 public void syncUserSpace(Callback callback){ Map<String,String> param =new HashMap<>(); param.put("access_token",ACCESS_TOKEN); sendGetHttpRequest(HOST+SPACE,param,callback); } //获取文件列表 public void getFileListALL(String path,Callback callback){ Map<String,String> param =new HashMap<>(); param.put("access_token",ACCESS_TOKEN); param.put("path",path); sendGetHttpRequest(HOST+FILE_LIST_ALL,param,callback); } }
需要注意的是,需要用到的请求地址都是可以再官方文档中查到的。例如像获取用户信息,官方给出的要求是这样子的:
我们使用他的接口地址和格式请求网络,他会返回给我们一些数据,我们所需要的部分就在这些数据中。
官方文档也给出了响应实例:
根据响应实例我们需要创建相应的实体类的对象来保存返回的数据,从而显示在我们的应用上。
例如这个用户实例我们就需要创建实体类
public class UserInfoEnity { /**来自百度官方的示例 * avatar_url : https://dss0.bdstatic.com/7Ls0a8Sm1A5BphGlnYG/sys/portrait/item/netdisk.1.3d20c095.phlucxvny00WCx9W4kLifw.jpg * baidu_name : 啊呀呀 * errmsg : succ * errno : 0 * netdisk_name : abcdefff * request_id : 674030589892501935 * uk : 208281036 * vip_type : 0 */ private String avatar_url; private String baidu_name; private String errmsg; private int errno; private String netdisk_name; private String request_id; private double uk; private int vip_type; public String getAvatar_url() { return avatar_url; } public void setAvatar_url(String avatar_url) { this.avatar_url = avatar_url; } public String getBaidu_name() { return baidu_name; } public void setBaidu_name(String baidu_name) { this.baidu_name = baidu_name; } public String getErrmsg() { return errmsg; } public void setErrmsg(String errmsg) { this.errmsg = errmsg; } public int getErrno() { return errno; } public void setErrno(int errno) { this.errno = errno; } public String getNetdisk_name() { return netdisk_name; } public void setNetdisk_name(String netdisk_name) { this.netdisk_name = netdisk_name; } public String getRequest_id() { return request_id; } public void setRequest_id(String request_id) { this.request_id = request_id; } public double getUk() { return uk; } public void setUk(int uk) { this.uk = uk; } public int getVip_type() { return vip_type; } public void setVip_type(int vip_type) { this.vip_type = vip_type; } }
具体的通过json串快速创建实体类可以通过一个插件GsonFormat,他可以通过json串自动创建对应的实体类
具体实现可自行百度。
这里踩了一个坑,就是当时按照文档的要求,uk属性的数据类型是设置的是int,但是始终报错,原因是因为越界了,所以考虑到可能是因为账户比较新,用户在增加,可能是因为我的uk值太大了,int表示不了就换成了double,然后就能正确运行了。
这里面就有了基本的用户信息。用GSON把它解析出来更新在界面上就好了。
ProFileFragment
import android.os.Bundle; import android.os.Handler; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.GridView; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import com.example.baidudisk.R; import com.example.baidudisk.adapter.GridviewAdapter; import com.example.baidudisk.enity.Icon; import com.example.baidudisk.enity.SpaceEntity; import com.example.baidudisk.enity.UserInfoEnity; import com.example.baidudisk.intent.NetworkInterfaces; import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; import org.jetbrains.annotations.NotNull; import java.io.IOException; import java.util.ArrayList; import java.util.List; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Response; public class ProFileFragment extends Fragment { GridView gridView; TextView user_id; private GridviewAdapter adapter; private ArrayList<Icon> Data; private Handler mHandler; private TextView mSpace; private RecyclerView mFilesRV; private ProgressBar mProgress; ImageView mVip,touxiang; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view=inflater.inflate(R.layout.activity_me_acitivity,container,false); gridView=view.findViewById(R.id.grid_view); user_id=view.findViewById(R.id.user_id); touxiang=view.findViewById(R.id.touxiang); mFilesRV=view.findViewById(R.id.rv_files); mProgress=view.findViewById(R.id.pro); mSpace=view.findViewById(R.id.mSpace); String data=getActivity().getIntent().getStringExtra("str1"); mVip=view.findViewById(R.id.mVip); user_id.setText(data); Data=new ArrayList<>(); mHandler=new Handler(); Data.add(new Icon(R.mipmap.file_add_btn_file,"上传文档")); Data.add(new Icon(R.mipmap.file_add_btn_folder,"新建文件夹")); Data.add(new Icon(R.mipmap.file_add_btn_music,"上传音乐")); Data.add(new Icon(R.mipmap.file_add_btn_note,"新建笔记")); Data.add(new Icon(R.mipmap.file_add_btn_other,"上传其他文件")); Data.add(new Icon(R.mipmap.file_add_btn_photo,"上传照片")); Data.add(new Icon(R.mipmap.file_add_btn_scan,"扫一扫")); Data.add(new Icon(R.mipmap.file_add_btn_video,"上传视频")); adapter=new GridviewAdapter(getContext(),R.layout.item_grid_icon,Data); gridView.setAdapter(adapter); syncUserInfo(); syncSpace(); return view; } private void syncUserInfo(){ new NetworkInterfaces().syncUserInfo(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Toast.makeText(getContext(),"获取用户信息失败!",Toast.LENGTH_SHORT).show(); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { UserInfoEnity entity=new Gson().fromJson(response.body().string(),UserInfoEnity.class); if(entity.getErrno()==0){ showUserInfo(entity); } else { Toast.makeText(getContext(),"获取失败",Toast.LENGTH_SHORT).show(); } } }); } private void showUserInfo(final UserInfoEnity entity) { mHandler.post(new Runnable() { @Override public void run() { Glide.with(ProFileFragment.this) .load(entity.getAvatar_url()) .centerCrop() .placeholder(R.mipmap.swan_app_user_portrait_pressed) .apply(RequestOptions.circleCropTransform()) .into(touxiang); user_id.setText(entity.getBaidu_name()); switch (entity.getVip_type()){ case 0: mVip.setImageResource(R.mipmap.home_identity_common); break; case 1: mVip.setImageResource(R.mipmap.home_identity_member); break; case 2: mVip.setImageResource(R.mipmap.home_identity_super); break; } } }); } public void syncSpace(){ new NetworkInterfaces().syncUserSpace(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Toast.makeText(getContext(),"获取容量失败",Toast.LENGTH_SHORT).show(); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { SpaceEntity entity=new Gson().fromJson(response.body().string(),SpaceEntity.class); if(entity.getErrno()==0){ showSpace(entity); } else { Toast.makeText(getContext(),"获取失败",Toast.LENGTH_SHORT).show(); } } }); } private void showSpace(final SpaceEntity entity) { mHandler.post(new Runnable() { @Override public void run() { long total=entity.getTotal()/(1024*1024*1024); long used =entity.getUsed()/(1024*1024*1024); int process=(int)(used*100/total); mSpace.setText(used+"GB/"+total+"GB"); mProgress.setProgress(process); } }); } }
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent"> <ImageView android:id="@+id/touxiang" android:layout_width="100dp" android:layout_height="78dp" android:src="@mipmap/swan_app_user_portrait_pressed" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/user_id" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dp" android:text="移动开发" android:textColor="#000" android:textSize="20sp" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toRightOf="@+id/touxiang" /> <ImageView android:id="@+id/mVip" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toRightOf="@+id/user_id" android:src="@mipmap/home_identity_common" /> <ProgressBar android:id="@+id/pro" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginLeft="10dp" app:layout_constraintLeft_toRightOf="@+id/touxiang" app:layout_constraintTop_toBottomOf="@id/user_id" app:layout_constraintRight_toRightOf="parent" style="@style/Widget.AppCompat.ProgressBar.Horizontal" /> <TextView android:id="@+id/mSpace" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="10dp" android:text="668GB/3220GB" app:layout_constraintLeft_toRightOf="@+id/touxiang" app:layout_constraintTop_toBottomOf="@+id/pro" /> <GridView android:id="@+id/grid_view" android:layout_width="match_parent" android:layout_height="0dp" android:numColumns="4" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toBottomOf="@+id/touxiang" app:layout_constraintVertical_bias="0.0" tools:layout_editor_absoluteX="1dp" /> </androidx.constraintlayout.widget.ConstraintLayout>
如:
从上面的代码可以知道
网盘容量的获取方法与用户相同。(请求正确网址+创建正确实体类+更新ui)
先是文件列表的实体类
import java.util.List; public class FileListEntity { /** * cursor : 5 * errmsg : succ * errno : 0 * has_more : 1 * list : [{"category":4,"fs_id":30995775581277,"isdir":0,"local_ctime":1547097900,"local_mtime":1547097900,"md5":"540b49455n2f04f55c3929eb8b0c0445","path":"/测试目录/20160820.txt","server_ctime":1578310087,"server_filename":"20160820.txt","server_mtime":1596078161,"size":1120,"thumbs":{"url1":"https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c140_u90&quality=100","url2":"https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c360_u270&quality=100","url3":"https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c850_u580&quality=100"}},{"category":4,"fs_id":85001194198278,"isdir":0,"local_ctime":1547097899,"local_mtime":1547097899,"md5":"25319465flf30b76c4032ef358446f91","path":"/测试目录/20160907.txt","server_ctime":1578309734,"server_filename":"20160907.txt","server_mtime":1596078163,"size":3480,"thumbs":{"url1":"https://thumbnail0.baidupcs.com/thumbnail/25319465flf30b76c4032ef358446f91?fid=2082810368-250528-85001194198278&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-q5Zan3NIaWpCaD6Qq6kyaiuVMZ4%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316600199584700&dp-callid=0&time=1596092400&size=c140_u90&quality=100","url2":"https://thumbnail0.baidupcs.com/thumbnail/25319465flf30b76c4032ef358446f91?fid=2082810368-250528-85001194198278&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-q5Zan3NIaWpCaD6Qq6kyaiuVMZ4%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316600199584700&dp-callid=0&time=1596092400&size=c360_u270&quality=100","url3":"https://thumbnail0.baidupcs.com/thumbnail/25319465flf30b76c4032ef358446f91?fid=2082810368-250528-85001194198278&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-q5Zan3NIaWpCaD6Qq6kyaiuVMZ4%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316600199584700&dp-callid=0&time=1596092400&size=c850_u580&quality=100"}},{"category":4,"fs_id":231830464480934,"isdir":0,"local_ctime":1547097898,"local_mtime":1547097898,"md5":"08f596c15n1bd3fa383af69d4a0b677f","path":"/测试目录/20160906.txt","server_ctime":1578309734,"server_filename":"20160906.txt","server_mtime":1596078162,"size":5568,"thumbs":{"url1":"https://thumbnail0.baidupcs.com/thumbnail/08f596c15n1bd3fa383af69d4a0b677f?fid=2082810368-250528-231830464480934&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-rxqXy8qFeG7VFWj0B%2B1v8%2BeKfss%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316660217540833&dp-callid=0&time=1596092400&size=c140_u90&quality=100","url2":"https://thumbnail0.baidupcs.com/thumbnail/08f596c15n1bd3fa383af69d4a0b677f?fid=2082810368-250528-231830464480934&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-rxqXy8qFeG7VFWj0B%2B1v8%2BeKfss%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316660217540833&dp-callid=0&time=1596092400&size=c360_u270&quality=100","url3":"https://thumbnail0.baidupcs.com/thumbnail/08f596c15n1bd3fa383af69d4a0b677f?fid=2082810368-250528-231830464480934&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-rxqXy8qFeG7VFWj0B%2B1v8%2BeKfss%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316660217540833&dp-callid=0&time=1596092400&size=c850_u580&quality=100"}},{"category":1,"fs_id":372974889986706,"isdir":0,"local_ctime":1472733116,"local_mtime":1472729122,"md5":"addc44e2d26154ba44a41915d2b4270f","path":"/测试目录/test.mp4","server_ctime":1472733116,"server_filename":"test.mp4","server_mtime":1595921251,"size":47761278,"thumbs":{"icon":"https://thumbnail0.baidupcs.com/thumbnail/addc44e2d26154ba44a41915d2b4270f?fid=2082810368-250528-372974889986706&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-oQ6l5FPEK8mSVzyU21BN%2F6e0bwg%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316708114546127&dp-callid=0&time=1596092400&size=c60_u60&quality=100&vuk=2082810368&ft=video","url1":"https://thumbnail0.baidupcs.com/thumbnail/addc44e2d26154ba44a41915d2b4270f?fid=2082810368-250528-372974889986706&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-oQ6l5FPEK8mSVzyU21BN%2F6e0bwg%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316708114546127&dp-callid=0&time=1596092400&size=c140_u90&quality=100&vuk=2082810368&ft=video","url2":"https://thumbnail0.baidupcs.com/thumbnail/addc44e2d26154ba44a41915d2b4270f?fid=2082810368-250528-372974889986706&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-oQ6l5FPEK8mSVzyU21BN%2F6e0bwg%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316708114546127&dp-callid=0&time=1596092400&size=c360_u270&quality=100&vuk=2082810368&ft=video","url3":"https://thumbnail0.baidupcs.com/thumbnail/addc44e2d26154ba44a41915d2b4270f?fid=2082810368-250528-372974889986706&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-oQ6l5FPEK8mSVzyU21BN%2F6e0bwg%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316708114546127&dp-callid=0&time=1596092400&size=c850_u580&quality=100&vuk=2082810368&ft=video"}},{"category":4,"fs_id":376509117714123,"isdir":0,"local_ctime":1547097902,"local_mtime":1547097902,"md5":"b048a8fb7mfdfafbf1fba286228c4dc6","path":"/测试目录/20160825.txt","server_ctime":1578310092,"server_filename":"20160825.txt","server_mtime":1596077685,"size":3480,"thumbs":{"url1":"https://thumbnail0.baidupcs.com/thumbnail/b048a8fb7mfdfafbf1fba286228c4dc6?fid=2082810368-250528-376509117714123&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-HW3JtBFisIj8n8f8p52pJVxFCcM%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316759458944734&dp-callid=0&time=1596092400&size=c140_u90&quality=100","url2":"https://thumbnail0.baidupcs.com/thumbnail/b048a8fb7mfdfafbf1fba286228c4dc6?fid=2082810368-250528-376509117714123&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-HW3JtBFisIj8n8f8p52pJVxFCcM%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316759458944734&dp-callid=0&time=1596092400&size=c360_u270&quality=100","url3":"https://thumbnail0.baidupcs.com/thumbnail/b048a8fb7mfdfafbf1fba286228c4dc6?fid=2082810368-250528-376509117714123&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-HW3JtBFisIj8n8f8p52pJVxFCcM%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316759458944734&dp-callid=0&time=1596092400&size=c850_u580&quality=100"}}] * request_id : 4194083114842583994 */ private int cursor; private String errmsg; private int errno; private int has_more; private String request_id; private List<ListBean> list; public int getCursor() { return cursor; } public void setCursor(int cursor) { this.cursor = cursor; } public String getErrmsg() { return errmsg; } public void setErrmsg(String errmsg) { this.errmsg = errmsg; } public int getErrno() { return errno; } public void setErrno(int errno) { this.errno = errno; } public int getHas_more() { return has_more; } public void setHas_more(int has_more) { this.has_more = has_more; } public String getRequest_id() { return request_id; } public void setRequest_id(String request_id) { this.request_id = request_id; } public List<ListBean> getList() { return list; } public void setList(List<ListBean> list) { this.list = list; } public static class ListBean { /** * category : 4 * fs_id : 30995775581277 * isdir : 0 * local_ctime : 1547097900 * local_mtime : 1547097900 * md5 : 540b49455n2f04f55c3929eb8b0c0445 * path : /测试目录/20160820.txt * server_ctime : 1578310087 * server_filename : 20160820.txt * server_mtime : 1596078161 * size : 1120 * thumbs : {"url1":"https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c140_u90&quality=100","url2":"https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c360_u270&quality=100","url3":"https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c850_u580&quality=100"} */ private int category; private long fs_id; private int isdir; private int local_ctime; private int local_mtime; private String md5; private String path; private int server_ctime; private String server_filename; private int server_mtime; private int size; private ThumbsBean thumbs; public int getCategory() { return category; } public void setCategory(int category) { this.category = category; } public long getFs_id() { return fs_id; } public void setFs_id(long fs_id) { this.fs_id = fs_id; } public int getIsdir() { return isdir; } public void setIsdir(int isdir) { this.isdir = isdir; } public int getLocal_ctime() { return local_ctime; } public void setLocal_ctime(int local_ctime) { this.local_ctime = local_ctime; } public int getLocal_mtime() { return local_mtime; } public void setLocal_mtime(int local_mtime) { this.local_mtime = local_mtime; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public String getPath() { return path; } public void setPath(String path) { this.path = path; } public int getServer_ctime() { return server_ctime; } public void setServer_ctime(int server_ctime) { this.server_ctime = server_ctime; } public String getServer_filename() { return server_filename; } public void setServer_filename(String server_filename) { this.server_filename = server_filename; } public int getServer_mtime() { return server_mtime; } public void setServer_mtime(int server_mtime) { this.server_mtime = server_mtime; } public int getSize() { return size; } public void setSize(int size) { this.size = size; } public ThumbsBean getThumbs() { return thumbs; } public void setThumbs(ThumbsBean thumbs) { this.thumbs = thumbs; } public static class ThumbsBean { /** * url1 : https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c140_u90&quality=100 * url2 : https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c360_u270&quality=100 * url3 : https://thumbnail0.baidupcs.com/thumbnail/540b49455n2f04f55c3929eb8b0c0445?fid=2082810368-250528-30995775581277&rt=pr&sign=FDTAER-DCb740ccc5511e5e8fedcff06b081203-VDkr1qdq%2BXPk79PIL5SHCADhEnk%3D&expires=8h&chkbd=0&chkv=0&dp-logid=4193316540243014694&dp-callid=0&time=1596092400&size=c850_u580&quality=100 */ private String url1; private String url2; private String url3; public String getUrl1() { return url1; } public void setUrl1(String url1) { this.url1 = url1; } public String getUrl2() { return url2; } public void setUrl2(String url2) { this.url2 = url2; } public String getUrl3() { return url3; } public void setUrl3(String url3) { this.url3 = url3; } } } }
然后是修改之前文件页面的一些注释
import android.Manifest; import android.content.Intent; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.content.ContextCompat; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.example.baidudisk.R; import com.example.baidudisk.adapter.FileAdapter; import com.example.baidudisk.adapter.MyFileAdapter; import com.example.baidudisk.enity.FileEntity; import com.example.baidudisk.enity.FileListEntity; import com.example.baidudisk.intent.NetworkInterfaces; import com.google.gson.Gson; import org.jetbrains.annotations.NotNull; import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Stack; import okhttp3.Call; import okhttp3.Callback; import okhttp3.Response; public class FileFragment extends Fragment implements View.OnClickListener { int mLevel = 0; LinearLayout linearLayout; private Handler mHandler; private List<FileEntity> fileEntityList = new ArrayList<>(); private List<FileListEntity.ListBean> mFiles = new ArrayList<FileListEntity.ListBean>(); FileAdapter mAdapter; String mCurrPath; MyFileAdapter myFileAdapter; private TextView mTip; Stack<String> mPathStack = new Stack<>(); RecyclerView mFilesRV; SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss"); @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_file, container, false); //initFile(); mHandler = new Handler(); mFilesRV = view.findViewById(R.id.rv_files); mAdapter = new FileAdapter(fileEntityList); myFileAdapter = new MyFileAdapter(mFiles); mTip = view.findViewById(R.id.mTipTv); linearLayout = view.findViewById(R.id.ll_1); linearLayout.setOnClickListener(this); LinearLayoutManager layoutManager = new LinearLayoutManager(getContext()); mFilesRV.setLayoutManager(layoutManager); mFilesRV.setAdapter(mAdapter); showExternalFiles(); requestPermissions(); mAdapter.setListener(new FileAdapter.setOnClickListener() { @Override public void onClick(int position) { if (getActivity() != null && fileEntityList.get(position).type != R.mipmap.file) { mLevel++; mCurrPath = mCurrPath + "/" + fileEntityList.get(position).name; showSubFiles(); } } }); view.setFocusableInTouchMode(true); view.requestFocus(); /*view.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction()== KeyEvent.ACTION_DOWN&&keyCode==KeyEvent.KEYCODE_BACK&&mLevel>0){ File file =new File(mCurrPath).getParentFile(); if(file!=null){ mCurrPath=file.getAbsolutePath(); mLevel--; showSubFiles(); return true; } } return false; } });*/ myFileAdapter.setListener(new MyFileAdapter.setOnClickListener() { @Override public void onClick(int position) { if (mFiles.get(position).getIsdir() == 1) { forward(mFiles.get(position).getPath()); } } }); view.setOnKeyListener(new View.OnKeyListener() { @Override public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_BACK) { if (!mPathStack.empty()) { back(); return true; } } return false; } }); getFileList("/"); return view; } /** * 获取权限 */ private void requestPermissions() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && getActivity() != null) { if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { //获取权限后的操作 } else { requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, 0); } } } /*private void initFile() { for (int i=0;i<20;i++) { fileEntityList.add(new FileEntity(R.mipmap.ic_gc_main_empty_folder,"新建文件夹"+i,"2020-10-01 00:00")); } }*/ @Override public void onClick(View v) { switch (v.getId()) { case R.id.ll_1: startActivity(new Intent(getContext(), SearchActivity.class)); break; } } private void showExternalFiles() { mCurrPath = Environment.getExternalStorageDirectory().getAbsolutePath(); showSubFiles(); } private void showSubFiles() { File file = new File(mCurrPath); if (file.exists() && file.isDirectory()) { File[] sub = file.listFiles(); if (sub != null) { fileEntityList.clear(); for (File f : sub) { fileEntityList.add(new FileEntity(f.isDirectory() ? R.mipmap.ic_gc_main_empty_folder : R.mipmap.file, f.getName(), simpleDateFormat.format(new Date(f.lastModified())))); } } else { fileEntityList.clear(); } mAdapter.notifyDataSetChanged(); } } private void showLoading() { mHandler.post(new Runnable() { @Override public void run() { mFilesRV.setVisibility(View.GONE); mTip.setText("正在加载......."); mTip.setVisibility(View.VISIBLE); } }); } private void showResult() { mHandler.post(new Runnable() { @Override public void run() { mFilesRV.setVisibility(View.VISIBLE); mTip.setVisibility(View.INVISIBLE); myFileAdapter.notifyDataSetChanged(); } }); } private void showError() { mHandler.post(new Runnable() { @Override public void run() { mFilesRV.setVisibility(View.GONE); mTip.setVisibility(View.VISIBLE); mTip.setText("获取文件失败"); } }); } private void getFileList(String path) { showLoading(); new NetworkInterfaces().getFileListALL(path, new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { showError(); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { FileListEntity entity = new Gson().fromJson(response.body().string(), FileListEntity.class); if (entity.getErrno() == 0) { showFileList(entity.getList()); } else { showError(); System.out.println(entity.getErrmsg()); } } }); } private void showFileList(List<FileListEntity.ListBean> beans) { mFiles.clear(); mFiles.addAll(beans); mHandler.post(new Runnable() { @Override public void run() { mFilesRV.setAdapter(myFileAdapter); myFileAdapter.notifyDataSetChanged(); if (mFiles.isEmpty()) { Toast.makeText(getContext(), "没有文件!", Toast.LENGTH_SHORT).show(); } else { showResult(); } } }); } private void forward(String path) { mCurrPath = path; mPathStack.push(path); getFileList(mCurrPath); } private void back() { mPathStack.pop(); if (!mPathStack.empty()) { mCurrPath = mPathStack.peek(); mPathStack.pop(); } else { mCurrPath = "/"; } getFileList(mCurrPath); } }
MyFileAdapter
import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.RecyclerView; import com.example.baidudisk.R; import com.example.baidudisk.enity.FileEntity; import com.example.baidudisk.enity.FileListEntity; import java.util.List; public class MyFileAdapter extends RecyclerView.Adapter<MyFileAdapter.ViewHolder> { public interface setOnClickListener { void onClick(int position); } private List<FileListEntity.ListBean> mFileList; public setOnClickListener myClickListener; public void setListener(setOnClickListener s) { myClickListener = s; } static class ViewHolder extends RecyclerView.ViewHolder { ImageView mFileTypePic; TextView mFileName; TextView mFileCreationDate; LinearLayout mRootLl; public ViewHolder(@NonNull View itemView) { super(itemView); mFileTypePic = itemView.findViewById(R.id.re_pic); mFileName = itemView.findViewById(R.id.re_name); mFileCreationDate = itemView.findViewById(R.id.re_creation_time); mRootLl = itemView.findViewById(R.id.ll_mRootLl); } } public MyFileAdapter(List<FileListEntity.ListBean> filelist) { mFileList = filelist; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_file, parent, false); final ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull ViewHolder holder, final int position) { FileListEntity.ListBean fileEntity = mFileList.get(position); holder.mFileTypePic.setImageResource(R.mipmap.ic_gc_main_empty_folder); holder.mFileName.setText(fileEntity.getServer_filename()); holder.mFileCreationDate.setText(""+fileEntity.getLocal_ctime()); holder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { myClickListener.onClick(position); } }); } @Override public int getItemCount() { return mFileList.size(); } }
最后就是文件列表的获取,大致方法相同。只是需要注意的是,文件目录的前进与后退,这里我采用了栈来实现。
当是文件时可以进入时,将该文件的路径值进栈,同时在此显示当前文件路径下的内容,后面的也是同理。当退出时,将路径出栈之后,再返回栈顶的元素即是上一级的路径,然后显示,实现一级一级的返回。退到第一级目录时如果再按back键则会退出。
就目前为止,简单的百度api的使用也算完成了。可以继续根据官方文档继续深入研究,通过应用实现百度网盘的内容修改,管理,上传。
我的这篇文章差不多就在这里结束。 over :)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。