안녕하세요.
오늘은 SharedPreferences에 객체 데이터를 저장하는 방법에 대해 공부할 수 있도록, 간단한 메모를 SharedPreferences에 저장하고 저장된 메모를 다시 불러와서 리사이클러뷰에 띄워주는 예제를 만들어보겠습니다.
아래 작성된 예제에는 메모를 저장하고 다시 불러와서 조회하는 기능 밖에 없지만, 몇 가지 기능을 추가한다면 수정하고 삭제하는 기능도 만들 수 있습니다.
안드로이드에는 간단한 데이터를 키-값 쌍의 형태로 이루어진 파일에 저장할 수 있는 SharedPreferenses API가 있습니다.
해당 API가 제공하는 메서드를 사용하면 boolean, int, long, float, String 타입의 데이터를 저장하고 가져올 수 있습니다.
그러나 데이터를 객체로 관리하는 경우 SharedPreferenses에 바로 저장할 수 없기 다른 방법을 찾아야 합니다.
여러가지 방법이 있지만, 이 글에서는 Gson 라이브러리를 활용하여 객체 데이터를 JSON 형태의 String 데이터으로 변환하여 SharedPreferences에 저장하고자 합니다.
먼저 시연 영상 입니다.
동영상 서비스가 종료되어 해당 콘텐츠를 재생할 수 없습니다.
1. Gson 라이브러리 의존성 추가
Gson 이란 구글에서 개발한 오프소스로 JAVA에서 JSON을 파싱하고 생성하기 위해 사용되는 라이브러리입니다.
Gson 라이브러리를 사용하기 위해 build.gradle 에 의존성을 추가합니다.
implementation 'com.google.code.gson:gson:2.10.1'
2. Memo 클래스
다음으로 메모 데이터를 관리하기 위한 Memo 클래스입니다.
import java.text.SimpleDateFormat;
import java.util.Date;
// 메모를 최근에 작성한 순서로 정렬하기 위해 Comparable을 implements 합니다.
public class Memo implements Comparable<Memo> {
private String key; // 키값
private String title; // 제목
private String contents; // 내용
private long editTime; // 작성일시
public Memo(String title, String contents) {
this.title = title;
this.contents = contents;
this.editTime = System.currentTimeMillis();
// 쉐어드에 저장하고 불러올 때 사용할 키값은 최초 생성시간을 String으로 변환하여 사용했습니다.
this.key = String.valueOf(this.editTime);
}
// compareTo 메서드를 override 하여 각 객체의 작성일시를 비교하도록 하였습니다.
@Override
public int compareTo(Memo memo) {
if (this.editTime < memo.editTime) {
return -1;
} else if (this.editTime > memo.editTime) {
return 1;
}
return 0;
}
// long 타입으로 저장된 작성일시를 화면에 띄워줄 때 String 타입으로 변환하기 위한 메서드입니다.
public String getTimeStr() { // ex) 2024.04.01 16:24
Date date = new Date(this.editTime);
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MM.dd HH:mm");
return formatter.format(date);
}
// 아래는 일반적인 getter 와 setter 입니다.
public String getKey() {
return key;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContents() {
return contents;
}
public void setContents(String contents) {
this.contents = contents;
}
public long getEditTime() {
return editTime;
}
public void setEditTime(long editTime) {
this.editTime = editTime;
}
}
2. SharedPreferencesHelper 클래스
SharedPreferences 사용을 돕기 휘안 Helper 클래스를 만들었습니다.
import android.content.Context;
import android.content.SharedPreferences;
import com.google.gson.Gson;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
public class SharedPreferencesHelper {
private final SharedPreferences memoPref;
private final SharedPreferences.Editor memoEditor;
private final Gson gson;
public SharedPreferencesHelper(Context mContext) {
this.memoPref = mContext.getSharedPreferences("memo", 0);
this.memoEditor = memoPref.edit();
//Gson 객체를 생성합니다.
this.gson = new Gson();
}
//키값을 사용하여 쉐어드에 저장된 특정 메모를 가져오기 위한 메서드 입니다.
public Memo getMemo(String key) {
String json = memoPref.getString(key, "");
if (json.isEmpty()) return null;
return gson.fromJson(json, Memo.class);
}
//메모를 쉐어드에 저장하기 위한 메서드 입니다.
public void putMemo(Memo memo) {
String json = gson.toJson(memo);
memoEditor.putString(memo.getKey(), json);
memoEditor.apply();
}
//memo.xml 파일에 저장된 모든 메모를 리스트로 반환하기 위한 메서드입니다.
public List<Memo> getMemoList() {
List<Memo> list = new ArrayList<>();
Collection<String> colKey = memoPref.getAll().keySet();
for (String key : colKey) {
list.add(getMemo(key));
}
// 메모를 최신순으로 정렬합니다.
// Memo 클래스에서 오버라이드 했던 compareTo 메서드가 여기서 활용됩니다.
list.sort(Collections.reverseOrder());
return list;
}
}
3. MainActivity 작성
MainActicity에는 작성되어 있는 메모를 보여줄 RecyclerView가 있습니다.
activity_main.xml
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/btn_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:clickable="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
android:src="@drawable/round_add_24"
android:contentDescription="추가 버튼" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private SharedPreferencesHelper pref;
MemoAdapter adapter;
List<Memo> dataList;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pref = new SharedPreferencesHelper(this);
FloatingActionButton btnWrite = findViewById(R.id.btn_add);
RecyclerView recyclerView = findViewById(R.id.recyclerView);
adapter = new MemoAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(
new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
);
btnWrite.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, WriteActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onStart() {
super.onStart();
dataList = pref.getMemoList();
adapter.setDataList(dataList);
}
}
4. 리사이클러뷰에서 사용할 아이템 레이아웃 작성
item_memo.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardUseCompatPadding="true"
app:contentPadding="16dp"
app:cardBackgroundColor="@color/white"
app:strokeColor="@color/design_default_color_primary">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="제목"
android:textStyle="bold"/>
<TextView
android:id="@+id/tv_contents"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="내용" />
<TextView
android:id="@+id/tv_time"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="2024.03.01"
android:textAlignment="textEnd"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
5. MainActivity의 리사이클러뷰에서 사용할 MemoAdapter & MemoViewHolder 작성
import android.annotation.SuppressLint;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;
public class MemoAdapter extends RecyclerView.Adapter<MemoAdapter.MemoViewHolder> {
private List<Memo> dataList;
@SuppressLint("NotifyDataSetChanged")
public void setDataList(List<Memo> dataList) {
this.dataList = dataList;
notifyDataSetChanged();
}
@NonNull
@Override
public MemoViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View view = inflater.inflate(R.layout.item_memo, parent, false);
return new MemoViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull MemoViewHolder holder, int position) {
Memo memo = dataList.get(position);
holder.setMemo(memo);
}
@Override
public int getItemCount() {
return dataList == null ? 0 : dataList.size();
}
class MemoViewHolder extends RecyclerView.ViewHolder {
TextView tvTitle;
TextView tvContents;
TextView tvTime;
public MemoViewHolder(@NonNull View itemView) {
super(itemView);
this.tvTitle = itemView.findViewById(R.id.tv_title);
this.tvContents = itemView.findViewById(R.id.tv_contents);
this.tvTime = itemView.findViewById(R.id.tv_time);
}
public void setMemo(Memo memo) {
tvTitle.setText(memo.getTitle());
tvContents.setText(memo.getContents());
tvTime.setText(memo.getTimeStr());
}
}
}
6. 메모 작성을 위한 WriteActivity 생성
activity_write.xml
<?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:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".WriteActivity">
<EditText
android:id="@+id/et_title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:background="@color/material_dynamic_tertiary95"
android:hint="제목을 입력해주세요"
android:inputType="text"
android:padding="8dp"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/et_contents"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="16dp"
android:background="@color/material_dynamic_tertiary95"
android:ems="10"
android:gravity="start|top"
android:hint="내용을 입력해주세요"
android:inputType="textMultiLine"
android:padding="8dp"
app:layout_constraintBottom_toTopOf="@+id/btn_save"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/et_title" />
<Button
android:id="@+id/btn_save"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="저장"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
WriteActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import androidx.appcompat.app.AppCompatActivity;
public class WriteActivity extends AppCompatActivity {
private SharedPreferencesHelper pref;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write);
pref = new SharedPreferencesHelper(this);
EditText etTitle = findViewById(R.id.et_title);
EditText etContents = findViewById(R.id.et_contents);
Button btnSave = findViewById(R.id.btn_save);
btnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String title = etTitle.getText().toString();
String contents = etContents.getText().toString();
Memo memo = new Memo(title, contents);
pref.putMemo(memo);
Intent intent = new Intent(WriteActivity.this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
}
});
}
}
'안드로이드 자바' 카테고리의 다른 글
| [JAVA][Android] ChatGPT API로 챗봇 만들기 - (2) 리사이클러뷰 만들기 (0) | 2024.05.09 |
|---|---|
| [JAVA][Android] ChatGPT API로 챗봇 만들기 - (1) ChatGPT API 사용하기 (0) | 2024.05.03 |
| [JAVA][Android] 카카오 지도에 현재 위치 표시하기 (12) | 2024.04.22 |
| [JAVA][Android] 카카오 지도 API Android v2 사용하기 (0) | 2024.04.13 |
| [Android][Java] 플래시 라이트 (2) | 2024.02.28 |