今天要來提提File Stream這個功能,稍後會進行讀、存txt檔至手機sd卡內,首先先加入讀、存檔所需要的權限至Manifest中,順便加入我今天還會進行設計的一個頁面(RecordActivity):
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:requestLegacyExternalStorage="true"
...
<activity
android:name=".RecordActivity"
android:exported="true">
</activity>
</application>
接著附上需要的UI及樣式:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@color/white" />
<stroke android:width="1dp"
android:color="@color/black"/>
<corners android:radius="10dp"/>
</shape>
<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=".MainActivity">
<Button
android:id="@+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="新增檔案"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.925" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="500dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
<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">
<EditText
android:id="@+id/et_title"
android:layout_width="304dp"
android:layout_height="47dp"
android:ems="10"
android:inputType="textPersonName"
android:hint="標題"
android:background="@drawable/et_drawable"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.495"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.001" />
<EditText
android:id="@+id/et_context"
android:layout_width="match_parent"
android:layout_height="542dp"
android:background="@drawable/et_drawable"
android:ems="10"
android:gravity="top"
android:hint="請輸入內容"
android:inputType="textPersonName"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.497" />
<Button
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="返回"
android:textSize="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.167"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
<Button
android:id="@+id/btn_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="儲存"
android:textSize="20dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.829"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.976" />
</androidx.constraintlayout.widget.ConstraintLayout>
public class DataAdapter extends RecyclerView.Adapter<DataAdapter.MyHolder> {
File[] noteList;//宣告資料
dataInterface Interface;
private BasicFileAttributes attrs;
private String context="";
public DataAdapter(File[] noteList,dataInterface Interface){
this.noteList=noteList;
this.Interface=Interface;
}
//將原本RecyclerView.ViewHolder的部分皆改為MyHolder
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.item_data,parent,false);
return new MyHolder(view);//連接布局,新增一個view給viewholder綁定元件
}
@Override
public void onBindViewHolder(MyHolder holder, int position) {
holder.txv_title.setText(getFileName(position));//position為索引值,用get來取得arraylist資料
holder.txv_date.setText(getTime(noteList[position].getPath()));
//當點選刪除按紐
holder.btn_delete.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Interface.onItemDelete(position,noteList[position]);
// noteList.remove(position);
notifyDataSetChanged();
}
});
}
@Override
public int getItemCount() {
return noteList.length; //回傳List大小
}
public class MyHolder extends RecyclerView.ViewHolder{
private TextView txv_title,txv_date;
private ImageButton btn_delete;
public MyHolder(View Holder){
super(Holder);
//當點選項目時(getAdapterPosition()為點擊的position)
Holder.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Log.d("AdapterPosition",""+getAdapterPosition());
getContext(noteList[getAdapterPosition()]);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Interface.onItemClick(getFileName(getAdapterPosition()),context);
// notifyItemRemoved(getAdapterPosition());
}
});
//取得從onCreateViewHolder的view,此ViewHolder綁定主布局元件
txv_title=Holder.findViewById(R.id.txv_title);
txv_date=Holder.findViewById(R.id.txv_date);
btn_delete=Holder.findViewById(R.id.btn_delete);
}
}
//回傳時間
public String getTime(String path){
Calendar c = Calendar.getInstance();
try {
attrs = Files.readAttributes(Paths.get(path), BasicFileAttributes.class);
} catch (IOException e) {
e.printStackTrace();
}
c.setTimeInMillis(attrs.lastModifiedTime().toMillis());
String dateformat = "yyyy年MM月dd日";
SimpleDateFormat df = new SimpleDateFormat(dateformat);
return df.format(c.getTime());
}
//回傳fileName(title)
public String getFileName(int position){
return noteList[position].getName().substring(0, noteList[position].getName().length()-4);
}
//回傳文字內容
public void getContext(File file){
try {
Log.d("Path:",""+file.getPath());
FileReader mFileReader = new FileReader(file.getPath());
BufferedReader reader = new BufferedReader(mFileReader);
while(reader.ready()){
//讀取字串
String str = reader.readLine();
context = context + str+"\n";
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public interface dataInterface {
//點擊List項目
void onItemClick(String title, String context);
//點擊List項目的刪除按紐
void onItemDelete(int position,File file);
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="50dp">
<TextView
android:id="@+id/txv_title"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="4"
android:layout_gravity="center"
android:gravity="center"
android:textSize="20dp"
android:text="TextView" />
<TextView
android:id="@+id/txv_date"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="5"
android:layout_gravity="center"
android:gravity="center"
android:textSize="20dp"
android:text="TextView" />
<ImageButton
android:id="@+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:background="#00FF0000"
app:srcCompat="@android:drawable/ic_menu_delete" />
</LinearLayout>
等需要的布局和RecyclerView都設計好後,便開始設計需要的class,首先因為你會進行存、讀檔,所以要先要求存儲空間的權限,如果沒同意權限的話可能會報錯:
//要求存儲空間權限
this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},0);
接著便是創建需要的資料夾和txt檔,假如原本就有檔案或資料夾,他file的exists()方法就會回傳true,但假如沒有的話,而你要建立資料夾(mFile),下的是mkdirs()的語法,他會幫你建立需要的資料夾,而如果是建立檔案(file),下的則是createNewFile的指令。接著看到程式:
private mFile,file;
//Environment.getExternalStorageDirectory().getAbsolutePath()為取得sd卡的路徑
mFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/IT/");
file = new File(mFile,"abc.txt");
//假使sd底下/IT資料夾不存在
if(!mFile.exists()){
mFile.mkdirs();//建立資料夾
}
//如果檔案不存在
if(!file.exists()){
try {
file.createNewFile();//建立檔案
} catch (IOException e) {
e.printStackTrace();
}
}
在上面已經建立,或者說已經存在檔案後,同時一定也會有讀取檔案的需求,那麼首先一樣要先去抓你資料夾的位置,然後抓資料夾內的全部檔案的話是listFiles()這個方法,當然你也可以用filter來過濾要抓的檔案,接著要讀取裡面的資料,假設我想要讀這個資料夾的第一筆資料大約會像:
File[] fileList = mFile.listFiles();
FileReader mFileReader = new FileReader(fileList[0].getPath());
BufferedReader reader = new BufferedReader(mFileReader);
String context="";
//迴圈讀取資料
while(reader.ready()){
//讀取字串
String str = reader.readLine();
context = context + str+"\n";
}
假設我想刪除這個資料夾的第一筆資料:
fileList[0].delete();
接著便開始設計需要的主程式:
public class MainActivity extends AppCompatActivity implements dataInterface{
private File mFile;
private File[] fileList;
private Button btn_send;
private DataAdapter dataAdapter;
private RecyclerView recyclerView;
private String content="";
// private BasicFileAttributes attrs;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//要求存儲空間權限
this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},0);
recyclerView = findViewById(R.id.recyclerView);
btn_send=findViewById(R.id.btn_send);
mFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/IT/");
//假使資料夾存在(取得資料夾底下的file設定清單)
if(mFile.exists()){
fileList = mFile.listFiles();
setRecyclerView(fileList);
}
btn_send.setOnClickListener(view->{
//要求存儲空間權限
this.requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},100);
if(!mFile.exists()){
Log.d("CreatePath","Success"+mFile.getAbsolutePath());
mFile.mkdirs();
}
Intent intent = new Intent(MainActivity.this,RecordActivity.class);
startActivity(intent);
finish();
});
}
public void setRecyclerView(File[] fileList){
dataAdapter = new DataAdapter(fileList,this);
recyclerView.setLayoutManager(new LinearLayoutManager(this));//使用LinearLayout布局
//分割線套件
recyclerView.addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.VERTICAL));
recyclerView.setAdapter(dataAdapter);//將資料給recyclerView顯示
}
@Override
public void onItemClick(String title, String context) {
Log.d("ItemClick!",""+context);
Intent intent = new Intent(MainActivity.this,RecordActivity.class);
Bundle bundle = new Bundle();
bundle.putString("title",title);
bundle.putString("context",context);
intent.putExtras(bundle);
startActivity(intent);
finish();
}
@Override
public void onItemDelete(int position,File file) {
Log.d("ItemDelete!",""+file.getName());
file.delete();
setRecyclerView(mFile.listFiles());
}
}
public class RecordActivity extends AppCompatActivity {
private File file,mFile;
private EditText et_title,et_context;
private Button btn_save,btn_back;
//用於暫存我點擊item進來的fileName
private String format_title;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_record);
Bundle bundle =getIntent().getExtras();
mFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/IT/");
et_title = findViewById(R.id.et_title);
et_context = findViewById(R.id.et_context);
btn_save=findViewById(R.id.btn_save);
btn_back=findViewById(R.id.btn_back);
if(bundle!=null){
//取得點擊item的資料
et_title.setText(bundle.getString("title"));
et_context.setText(bundle.getString("context"));
format_title=bundle.getString("title");
}
btn_save.setOnClickListener(view->{
//當標題及內容不為空
if(!et_title.getText().toString().equals("") && !et_context.getText().toString().equals("")) {
if(format_title!=null){
//修改的txt File檔進行新增
File changeTitle = new File(mFile,et_title.getText().toString()+".txt");
//原txt File檔進行刪除
file = new File(mFile,format_title+".txt");
file.delete();
if(!changeTitle.exists()) {
try {
Log.d("taggg", "更改資料!" + format_title);
FileOutputStream output = new FileOutputStream(changeTitle);
output.write(et_context.getText().toString().getBytes(StandardCharsets.UTF_8));
output.flush();
output.close();
} catch (Exception e) {
e.printStackTrace();
}
format_title=null;
Intent intent = new Intent(RecordActivity.this,MainActivity.class);
startActivity(intent);
finish();
}
else{
Toast.makeText(this,"此檔名已存在",Toast.LENGTH_SHORT).show();
}
}
else {
//title等於檔名
file = new File(mFile, et_title.getText().toString() + ".txt");
//如果檔案不存在
if (!file.exists()) {
try {
Log.d("CreateFile", "Success!");
file.createNewFile();
FileOutputStream output = new FileOutputStream(file);
output.write(et_context.getText().toString().getBytes(StandardCharsets.UTF_8));
output.flush();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
format_title=null;
Intent intent = new Intent(RecordActivity.this,MainActivity.class);
startActivity(intent);
finish();
}
else{
Toast.makeText(this,"此檔名已存在",Toast.LENGTH_SHORT).show();
}
}
}
});
//返回按鈕
btn_back.setOnClickListener(view->{
format_title=null;
Intent intent = new Intent(RecordActivity.this,MainActivity.class);
startActivity(intent);
finish();
});
}
}