9. 파일 입출력 – 1. 내부 저장 장치에 파일 입출력(일기장)

이번에는 내부 저장 장치에 파일 입출력하는 간단한 앱을 만들어 봅시다. 여기에서는 일기장을 내부 저장 장치에 저장하는 예를 들기로 할게요.

[그림] 내부 저장 장치에 일기장 파일 입출력 실행 화면

먼저 activity_main.xml 파일에 컨트롤을 배치합시다. 최상위 요소는 LinearLayout을 배치하세요. 그리고 DatePicker와 EditText, Button을 배치합니다. 간략하게 날짜를 선택할 수 있게 DatePicker의 canlendarViewShown을 false로 지정하고 datePickerMode를 spinner로 지정합시다.

<?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="match_parent"
    tools:context="com.example.ehclub.ex_fileio.MainActivity"
    android:orientation="vertical">
    <DatePicker
        android:id="@+id/date_picker"
        android:calendarViewShown="false"
        android:datePickerMode="spinner"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lines="8"/>
    <Button
        android:id="@+id/btn_write"
        android:text = "쓰기"
        android:onClick="writeBtnOnClick"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
</LinearLayout>

MainActivity.java 파일을 편집합시다. 먼저 배치한 DatePicker와 EditText, Button을 참조할 멤버 필드를 선언하고 파일 명을 기억할 멤버 필드를 선언하세요.

    DatePicker dp;
    EditText et;
    Button btn_write;
    String fname;

onCreate 메서드에서는 배치한 컨트롤을 멤버필드가 참조할 수 있게 findViewById 메서드를 호출하고 DatePicker를 현재 날짜로 설정합니다. 현재 날짜를 설정하는 부분은 setDatePicker 이름의 메서드를 만들기로 합시다.

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("작은 일기장");
        dp = (DatePicker)findViewById(R.id.date_picker);
        et = (EditText)findViewById(R.id.et);
        btn_write = (Button)findViewById(R.id.btn_write);
        setDatePicker();
    }

setDatePicker 메서드에서는 현재 날짜 정보를 얻어와서 Date에 따라 파일명을 조합할 DateChange 메서드를 호출합시다. 참고로 DateChange 메서드는 별도로 정의할 것입니다. 그리고 DatePicker의 날짜가 바뀔 때 처리할 리스너를 등록하고 리스너에서도 DateChange 메소드를 호출합니다.

    private void setDatePicker(){
        Calendar calendar = Calendar.getInstance();
        int ny = calendar.get(Calendar.YEAR);
        int nm = calendar.get(Calendar.MONTH);
        int nd = calendar.get(Calendar.DAY_OF_MONTH);
        DateChange(ny,nm,nd);

        dp.init(ny,nm,nd, new DatePicker.OnDateChangedListener(){
           public void onDateChanged(DatePicker view, int year, int month,int day){
               DateChange(year, month,day);
           }
        });
    }

DateChange 메서드에서는 날짜에 따라 파일명을 결정하고 해당 이름의 파일을 읽어옵니다. 이 부분은 readDiary 이름의 메서드로 별도로 작성합시다.

    public void DateChange(int year, int month,int day){
        fname = Integer.toString(year)+"_"
                +Integer.toString(month+1)+"_"
                +Integer.toString(day)+".txt";
        readDiary(fname);
    }

먼저 openFileInput 메서드를 통해 FileInputStream 개체를 참조합니다. 그리고 문자열 버퍼를 생성하고 FileInputStream 개체를 입력 인자로 InputStreamReader 개체를 생성하여 BufferedReader 개체를 생성합니다. 그리고 BufferedReader 개체의 readLine 메서드를 호출합니다. 만약 읽어온 결과가 null이면 해당 날짜의 일기 파일이 없는 것이므로 이를 명시한 후에 메서드를 빠져 나갑니다.

그리고 읽어온 문자열을 StringBuffer에 추가하고 다시 하나의 라인을 읽는 것어 StringBuffer에 추가하는 것을 반복합니다. 더 이상 읽어올 것이 없을 때 해당 내용으로 EditText의 text 속성을 설정하고 BufferedReader를 닫습니다.

 void readDiary(String rfname){
        String str;
        FileInputStream fs_in;

        try{
            fs_in = openFileInput(rfname);
            StringBuffer sbuf = new StringBuffer();
            BufferedReader buf = new BufferedReader(new InputStreamReader(fs_in));
            str = buf.readLine();
            if(str == null){
                et.setText("");
                et.setHint("일기 없음");
                btn_write.setText("새로 저장");
                buf.close();
                return;
            }
            while(str != null){
                sbuf.append(str +"\n");
                str = buf.readLine();
            }
            et.setText(sbuf);
            buf.close();

            btn_write.setText("수정하기");
        }
        catch (IOException e){
            et.setText("");
            et.setHint("일기 없음");
            btn_write.setText("새로 저장");
        }

    }

쓰기 버튼을 클릭하였을 때는 openFileOutput 메서드를 호출하여 FileOutputStream 개체를 참조합니다. 그리고 EditText 컨트롤의 text 속성에 쓰여진 문자열을 얻어옵니다. PrintWriter 개체를 생성한 후에 기록을 하세요.

 public void writeBtnOnClick(View view){
        try{
            FileOutputStream fs_out;
            fs_out = openFileOutput(fname, Context.MODE_APPEND);
            String str = et.getText().toString();
            PrintWriter  pw = new PrintWriter(fs_out);
            pw.println(str);
            pw.close();
        }
        catch (IOException e){

        }
    }

다음은 MainActivity.java 파일의 소스 코드입니다.

package com.example.ehclub.ex_fileio;

import android.content.Context;
import android.icu.util.Calendar;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.Toast;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;

public class MainActivity extends AppCompatActivity {

    DatePicker dp;
    EditText et;
    Button btn_write;
    String fname;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("작은 일기장");
        dp = (DatePicker)findViewById(R.id.date_picker);
        et = (EditText)findViewById(R.id.et);
        btn_write = (Button)findViewById(R.id.btn_write);
        setDatePicker();
    }
    private void setDatePicker(){
        Calendar calendar = Calendar.getInstance();
        int ny = calendar.get(Calendar.YEAR);
        int nm = calendar.get(Calendar.MONTH);
        int nd = calendar.get(Calendar.DAY_OF_MONTH);
        DateChange(ny,nm,nd);

        dp.init(ny,nm,nd, new DatePicker.OnDateChangedListener(){
           public void onDateChanged(DatePicker view, int year, int month,int day){
               DateChange(year, month,day);
           }
        });
    }
    public void DateChange(int year, int month,int day){
        fname = Integer.toString(year)+"_"
                +Integer.toString(month+1)+"_"
                +Integer.toString(day)+".txt";
        readDiary(fname);
    }
    void readDiary(String rfname){
        String str;
        FileInputStream fs_in;

        try{
            fs_in = openFileInput(rfname);
            StringBuffer sbuf = new StringBuffer();
            BufferedReader buf = new BufferedReader(new InputStreamReader(fs_in));
            str = buf.readLine();
            if(str == null){
                et.setText("");
                et.setHint("일기 없음");
                btn_write.setText("새로 저장");
                buf.close();
                return;
            }
            while(str != null){
                sbuf.append(str +"\n");
                str = buf.readLine();
            }
            et.setText(sbuf);
            buf.close();

            btn_write.setText("수정하기");
        }
        catch (IOException e){
            et.setText("");
            et.setHint("일기 없음");
            btn_write.setText("새로 저장");
        }

    }
    public void writeBtnOnClick(View view){
        try{
            FileOutputStream fs_out;
            fs_out = openFileOutput(fname, Context.MODE_APPEND);
            String str = et.getText().toString();
            PrintWriter  pw = new PrintWriter(fs_out);
            pw.println(str);
            pw.close();
        }
        catch (IOException e){

        }
    }
}