中级实训Android学习记录——RecyclerView、Android存储
学习记录 2020/11/26
RecyclerView
- RecyclerView
RecyclerView在之前就学习过,但用的时候才发现没有学习记录,所以在这里自行总结。
使用RecyclerView建立列表视图的步骤:
创建一个activity(Android studio会帮你顺便声明并创建layout),在activity的xml文件中建立一个RecyclerView
// 我们在activity_linear_recycler.xml中声明一个RecyclerView <androidx.recyclerview.widget.RecyclerView android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" android:id="@+id/rv_main" android:background="@color/purple_700"/>
建立一个列表的一个小项所用的layout
// 这里我们建立的是layout_linear_item.xml,里面只有一个简单的TextView <TextView android:id="@+id/tv_title" android:layout_width="match_parent" android:layout_height="50dp" android:textColor="#000" android:textSize="20sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" />
建立使用小项layout的RecyclerViewAdapter
public class LinearAdapter extends RecyclerView.Adapter<LinearAdapter.LinearViewHolder> { // 自己创建一个ViewHolder并将它传入Adapter的模板参数中 private Context mContext; public LinearAdapter(Context context) { this.mContext = context; } // 注意这里的返回值也要改成自己的ViewHolder @NonNull @Override public LinearAdapter.LinearViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new LinearViewHolder(LayoutInflater.from(mContext).inflate(R.layout.layout_linear_item, parent, false)); } // 注意这里传入的参数也要改成自己的ViewHolder @Override public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, int position) { holder.textView.setText("Hello World!"); } // 自定义一个列表的小项的个数,一般我们可以使用一个列表来声明 @Override public int getItemCount() { return 30; } // 建立一个ViewHolder来自定义小项 class LinearViewHolder extends RecyclerView.ViewHolder { private TextView textView; public LinearViewHolder(@NonNull View itemView) { super(itemView); textView = itemView.findViewById(R.id.tv_title); } } }
最后就可以在Activity中直接使用RecyclerView
// 先声明一个变量 private RecyclerView mRvMain; mRvMain = findViewById(R.id.rv_main); mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerActivity.this)); mRvMain.setAdapter(new LinearAdapter(LinearRecyclerActivity.this));
加每个小项之间的分隔线的方法
在Activity.java中自定义一个类并继承ItemDecoration
class myDecoration extends RecyclerView.ItemDecoration { @Override public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) { super.getItemOffsets(outRect, view, parent, state); outRect.set(0, 0, 0, getResources().getDimensionPixelOffset(R.dimen.dividerHeight)); } } // 这里的set函数的四个参数代表的方向依次是左、上、右、下 // 所以我们的效果其实是在每个item的下方加一个R.dimen.dividerHeight大小的横线
调用RecyclerView的addItemDecoration方法即可
mRvMain.addItemDecoration(new myDecoration());
Android 存储
-
SharedPreferences 轻量级数据存储
- 一般存储形式表现为以Key-Value的形式,存储在XML文件中
- 用SharedPreferences来读取xml文件
- 用SharedPreferences.Editor来更改xml文件
-
SharedPreferences 用法
-
获取数据
-
private SharedPreferences mSharedPreferences; mSharedPreferences = getSharedPreferences("data", MODE_PRIVATE); // 这里第一个参数表示文件名,第二个参数表示文件打开的模式,一般使用MODE_PRIVATE // 假设已经使用Editor输入数据,key="name" String data = mSharedPreferences.getString("name", "");
-
输入数据
-
private SharedPreferences.Editor mEditor; mEditor = mSharedPreferences.edit(); //调用put类型的方法来输入key-value数据 mEditor.putString("name", "haha") // 调用apply或者commit进行数据更新 // apply是异步 // commit是同步 mEditor.apply();
-
SharedPreferences的存储目录
- /data/data/
/shared_prefs
- /data/data/
-
SharedPreferences的存储目录的查看方式
-
命令行下
-
// 使用android的shell adb shell // 调用run-as 后面加上自己的完整applicaiton的名称 run-as com.skypan.helloworld // 进入目录shared_prefs cd shared_prefs // 在命令行中列出data.xml的内容 cat data.xml
-
-
存储概念
- Android存储分为
- 内部存储(Internal Storage)
- 内部存储会随应用的卸载而删除,如
- /data/data/
/shared_prefs - /data/data/
/databases - /data/data/
/files - /data/data/
/cache
- /data/data/
- 利用的方法可以是
- context.getFilesDir()获得目录/data/data/
/files - context.getCacheDir()获得目录/data/data/
/cache
- context.getFilesDir()获得目录/data/data/
- 内部存储会随应用的卸载而删除,如
- 外部存储(External Storage)
- 公有目录
- 公有目录的获取
- Environment.getExternalStoragePublicDirectory(int type)
- type可以是Environment下的已经定义好的变量
- 公有目录的获取
- 私有目录,随应用的卸载而删除,如
- /mnt/sdcard/Anroid/data/data/
/cache - /mnt/sdcard/Anroid/data/data/
/files
- /mnt/sdcard/Anroid/data/data/
- 公有目录
- 内部存储(Internal Storage)
- Android存储分为
-
File内部存储
-
使用的类
-
FileOutputStream,FileInputStream
-
存储数据
private void save(String content) { try { FileOutputStream fileOutputStream = openFileOutput("test.txt", MODE_PRIVATE); // 第一个参数代表文件名,第二个参数是文件读写方式 fileOutputStream.write(content.getBytes()); // 这里调用的write函数接受的是byte[],所以需要将String对象转换成byte } catch (IOException e) { // 注意,此时其实丢出的不止是IOException,还有FileNotFoundException,不过这个错误继承了IOException,所以都可以被IOException catch到 e.printStackTrace(); } finally { // 无论如何,我们都需要关闭一开始打开的文件 if (fileOutputStream != null) { // 如果已经打开了,就关闭 try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
读取数据
private String read() { FileInputStream fileInputStream = null; try { FileInputStream fileInputStream = openFileInput("test.txt"); byte[] buff = new byte[1024]; StringBuilder sb = new StringBuilder(""); int len = 0; while((len = fileInputStream.read(buff)) > 0) { sb.append(new String(buff, 0, len)); // 从buff的0开始,将len长度的byte做成String加到sb的后面 } return sb.toString(); } catch (IOException e) { e.printStackTrace(); // 默认可能发生错误,直接返回空 return null; }
-
-
File外部存储
-
使用的类
-
FileOutputStream,FileInputStream,Environment
-
存储数据
private void save(String content) { try { File dir = new File(Environment.getExternalStorageDirectory(), "skypan"); // 第一个是找到外部存储目录,第二个参数是文件夹的名字 if (!dir.exists()) { dir.mkdirs(); // 使用mkdirs而非mkdir是因为他会帮我们创建一系列的文件夹(当我们的文件夹路径很复杂的时候) } File file = new File(dir, "test.txt"); if (!file.exists()) { file.createNewFile(); } FileOutputStream fileOutputStream = new FileOutputStream(file); fileOutputStream.write(content.getBytes()); // 这里调用的write函数接受的是byte[],所以需要将String对象转换成byte } catch (IOException e) { // 注意,此时其实丢出的不止是IOException,还有FileNotFoundException,不过这个错误继承了IOException,所以都可以被IOException catch到 e.printStackTrace(); } finally { // 无论如何,我们都需要关闭一开始打开的文件 if (fileOutputStream != null) { // 如果已经打开了,就关闭 try { fileOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } }
读取数据
private String read() { FileInputStream fileInputStream = null; try { File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"skypan", "test.txt"); // 第一个参数是我们的文件夹的绝对路径,其中File.separator的值="/",第二个参数是我们的文件名 fileInputStream = new FileInputStream(file); byte[] buff = new byte[1024]; StringBuilder sb = new StringBuilder(""); int len = 0; while((len = fileInputStream.read(buff)) > 0) { sb.append(new String(buff, 0, len)); // 从buff的0开始,将len长度的byte做成String加到sb的后面 } return sb.toString(); } catch (IOException e) { e.printStackTrace(); // 默认可能发生错误,直接返回空 return null; }
注意,并不能直接使用以上代码,在使用外部存储之前,我们需要先获取外部存储的读写权限:
-
在AndroidManifest.xml中声明以下语句
<user-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
-
如果在build.gradle中,我们的sdk版本超过了23,我们还需要动态申请权限
在build.gradle中查看sdk版本
compileSdkVersion n
如果$n\geq 23$,就需要动态申请权限,在使用外部存储之前:
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WIRTE_EXTERNAL_STORAGE}, 1); // 第一个参数是调用这个函数的activity(context),第二个参数是申请的是什么权限,第三个是权限返回结果的识别码,如果我们需要得到用户是否确认给予权限,我们需要额外调用方法进行判断
在申请了权限之后,就可以正常的运行之前说的使用外部存储的代码了。
-
-