76일차(3)/Android App(34) : SQLite 활용(2)
- TodoDao에 할일 수정, 삭제 메소드 추가하기
- SQLite 날짜, 요일 설정방법
TodoDao
package com.example.step14sqlite
import android.database.Cursor
/*
TodoDao 클래스의 대표생성자(primary constructor) 의 인자로 DBHelper 객체를 전달받아서
필드에 넣어두고 메소드에서 활용하는 구조이다.
insert, update, delete 작업을 할때는
val db:SQLiteDataBase = helper.getWritableDataBase()
val db:SQLiteDataBase = helper.writableDataBase()
select 작업을 할때는
val db:SQLiteDataBase = helper.readableDataBase()
java의 JDBC에서 PreparedStatement 객체와 비슷한 기능을 하는 객체가 SQLiterDataBase 객체이다.
*/
class TodoDao(var helper:DBHelper) {
//할일 정보를 삭제하는 메소드
fun delete(num:Int){
val db=helper.writableDatabase
val args = arrayOf<Any>(num)
val sql = "DELETE FROM todo WHERE num=?"
db.execSQL(sql, args)
db.close()
}
//할일 정보 하나를 수정하는 메소드
fun update(data: Todo){
/*
val db=helper.writableDatabase
val args= arrayOf<Any>(data.content, data.num)
val sql="UPDATE todo SET content=? WHERE num=?"
db.execSQL(sql, args)
db.close()
*/
with(helper.writableDatabase){
execSQL("UPDATE todo SET content=? WHERE num=?", arrayOf<Any>(data.content, data.num))
close()
}
}
//할일 정보를 저장하는 메소드
fun insert(data:Todo){
//java => SQLiteDataBase db=helper.getWritableDataBase()
val db=helper.writableDatabase
// ? 에 바인딩할 인자를 Any 배열로 얻어내기
//java => Object[] args = { data.getContent() }
var args= arrayOf<Any>(data.content)
//실행할 sql문
// SQLiteDB => datetime('now', 'localtime'), oracle => SYSDATE
val sql = "INSERT INTO todo (content, regdate)" +
" VALUES(?, datetime('now', 'localtime'))"
//sql문 실행하기
db.execSQL(sql, args)
db.close() //close() 를 호출해야 실제로 반영된다.
}
//할일 목록을 리턴하는 함수
fun getList():List<Todo>{
//수정가능한 todo type을 담을 수 있는 리스트
val list= mutableListOf<Todo>()
val db=helper.readableDatabase
/*
SQLite DB 에서 날짜 format 만들기
- strftime() 함수를 활용한다.
year : %Y
month : %m
date : %d
week of day 0 1 2 3 4 5 6 : %w
hour : %H
minute : %M
substr('일월화수목금토', strftime('%w', regdate)+1, 1)
substr('일요일월요일화요일수요일목요일금요일토요일', strftime('%w', regdate)*3+1, 3)
*/
//실행할 select 문 구성 (binding 할것은 없음)
val sql="SELECT num, content, " +
" strftime('%Y년 %m월 %d일 ', regdate) " +
"|| substr('일월화수목금토', strftime('%w', regdate)+1, 1)" +
"|| strftime(' %H:%M', regdate)" +
" FROM todo ORDER BY num ASC"
//query 문을 수행하고 결과를 Cursor 객체로부터 얻어내기 (selection 인자는 없다.)
val result:Cursor = db.rawQuery(sql, null)
//Cursor 객체의 메소드를 이용해서 담긴 내용을 반복문 돌면서 호출한다.
while (result.moveToNext()){
//0번째는 num, 1번째는 content, 2번째는 regdate이다.
val data=Todo(result.getInt(0), result.getString(1), result.getString(2))
//할일 정보가 담긴 Todo 객체를 List에 추가한다.
list.add(data)
}
//할일 목록을 리턴해주기
return list
}
}

- 마지막에 close() 까지 호출해야만 반영된다.

- sql문, array 정보를 받아서 데이터를 삭제한다.
- args 라는 상수를 굳이 만들 필요는 없다. 바로 execSQL 에 인자로 넣어주어도 된다.

- sql문에서 바인딩할 ? 의 순서에 유의해서 작성하기
** 조금 더 코틀린스러운 코드로 바꾸어본다면?

- 불러다 놓고, 객체를 여러번 사용할 것이라면 with { } 안에서 쓰기
- 이 객체의 메소드를 연속으로 2번 호출하므로, 이렇게 작성하면 코드가 간단해진다.
- 날짜 문자열 출력방식 수정

- datetime('now','localtime') 으로 작성했을 때의 기본 형식
- TO_CHAR() 와 같은 방식으로 날짜형식을 바꿀수 있을까?
[ SQLite DB 에서 날짜 format 만들기 ]
- strftime() 함수를 활용한다.
- year : %Y
- month : %m
- date : %d
- week of day 0 1 2 3 4 5 6 : %w
- hour : %H
- minute : %M
- strftime() 사용
- 일요일 0, 월요일 1, ... , 토요일 6 순서로 숫자가 부여되어있다.
- 요일은 숫자로 지정되어 있다. 숫자에서 필요한 문자를 빼낸다고 생각하기
ex)
substr('일월화수목금토', strftime('%w', regdate)+1, 1)
substr('일요일월요일화요일수요일목요일금요일토요일', strftime('%w', regdate)*3+1, 3)
- substr() 함수는 이런 형태로 쓴다.
- 몇번째 인덱스에서 몇개의 문자를 가지고 올 것인지 표기!

- 이 숫자 중 하나가 이 위치에 들어온다. 이 문자열의 index 번호 +1번째로부터 1개만 가져온다는 뜻!
- 일요일이라고 하면 일월화수목금토에서 1번째 숫자를 1개 빼오는 것으로 작성하면 된다.

- 이렇게 날짜형식이 바뀐것을 볼 수 있다.

- 요일까지 넣으려면 파이프문자 두개 | | 로 연결하기
- 연월일, 요일, 시분초를 따로 작성한다.


- 이렇게 나타나게 할 수 있다.
- 문자열3개를 데이터베이스 내에서 단순 연결한 것이다.
- 아이템을 추가하면 자동으로 아래쪽으로 스크롤되어 가장 최근에 추가한 것을 보고 싶다면?
MainActivity
package com.example.step14sqlite
import android.content.DialogInterface
import android.os.Bundle
import android.view.View
import android.widget.*
import android.widget.AdapterView.OnItemLongClickListener
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() , View.OnClickListener, OnItemLongClickListener {
//필요한 필드 정의하기
//lateinit 예약어를 사용하면 null을 넣을 필요없이 값을 나중에 넣을 수 있다.
//null을 대입했다가 나중에 값을 바꾸려면 번거롭다.
lateinit var inputText:EditText
lateinit var listView: ListView
lateinit var adapter: TodoAdapter
lateinit var helper: DBHelper
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//필요한 UI의 참조값 얻어와서 필드에 저장하기
inputText=findViewById(R.id.inputText)
listView=findViewById(R.id.listView)
//버튼 리스너 등록하기
findViewById<Button>(R.id.addBtn).setOnClickListener(this)
//DBhelper 객체의 참조값을 필드에 저장하기
//version값이 증가되면 onUpgrade() 메소드가 자동 호출되면서 db가 초기화된다.
helper = DBHelper(this, "MyDB.sqlite", null, 2)
//할일 목록 얻어오기
var list:List<Todo> = TodoDao(helper).getList()
//listView 동작 준비하고, 할일 목록 출력하기
adapter=TodoAdapter(this, R.layout.listview_cell, list)
//listView에 아답타 연결하기
listView.adapter=adapter
//listView에 LongClickListener 등록하기
listView.setOnItemLongClickListener(this)
}
override fun onClick(v: View?) {
//1. 입력한 문자열을 읽어와서
val msg=inputText.text.toString()
//2. Todo 객체에 담아서
val data=Todo(0, msg, "")
//3. TodoDao객체를 이용해서 DB에 저장한다.
TodoDao(helper).insert(data)
//4. 목록을 새로 얻어와서
val list=TodoDao(helper).getList()
//5. 아답타에 넣어주고
adapter.list=list
//6. 모델의 내용이 바뀌었다고 아답타에 알려서 ListView가 업데이트되도록 한다.
adapter.notifyDataSetChanged()
//7. Toast 띄우기
Toast.makeText(this, "저장했습니다.", Toast.LENGTH_SHORT).show()
inputText.setText("")
//8. listView의 가장 아래쪽이 화면에 보일 수 있도록 부드럽게 스크롤시키기
listView.smoothScrollToPosition(adapter.count)
}
//listView의 cell을 오랫동안 클릭하고 있으면 호출되는 메소드
override fun onItemLongClick(parent: AdapterView<*>?, view: View?, position: Int, id: Long): Boolean {
//여기서 position은 클릭한 cell의 인덱스
//여기서 id는 클릭한 cell의 아이디( Todo의 Primary key )
//id에 전달되는 값은 TodoAdapter의 getItemId() 메소드에서 리턴한 값
AlertDialog.Builder(this)
.setTitle("알림")
.setMessage("삭제하시겠습니까?")
.setPositiveButton("네", DialogInterface.OnClickListener {
dialog, which ->
val dao=TodoDao(helper)
dao.delete(id.toInt())
//목록을 새로 얻어와서
val list=TodoDao(helper).getList()
//아답타에 넣어주고
adapter.list=list
//모델의 내용이 바뀌었다고 아답타에 올려서 listView가 업데이트되도록 한다.
adapter.notifyDataSetChanged()
})
.setNegativeButton("아니오", null)
.create()
.show()
return false
}
}
listView.smoothScrollToPosition(adapter.count)
- smoothScrollToPosition() 이라는 기능이 있다. 아래쪽으로 스크롤해주는 기능!
- adapter.count 는? 모델의 개수(list.size)를 넣어주면 된다.
- 오래 클릭했을 때(LongClickListener), 알림창을 띄워서 삭제의사를 물어보는 기능 추가
- OnItemLongClickListener 인터페이스를 구현하고 메소드 오버라이드
- AlertDialog.Builder 로 알림창 상세 설정하기

- setPositiveButton 에 리스너 등록하기
- 메소드 하나짜리면 이렇게 { } 으로 전달할 수 있다!

- 이렇게 ctrl+space 로 작성할 onClickListener의 형태를 자동완성시킬 수 있다.

- 여기서 which는 어떤 인덱스를 클릭했는지 나오는것이다.
AlertDialog.Builder(this)
.setTitle("알림")
.setMessage("삭제하시겠습니까?")
.setPositiveButton("네", DialogInterface.OnClickListener {
dialog, which ->
val dao=TodoDao(helper)
dao.delete(id.toInt())
//목록을 새로 얻어와서
val list=TodoDao(helper).getList()
//아답타에 넣어주고
adapter.list=list
//모델의 내용이 바뀌었다고 아답타에 올려서 listView가 업데이트되도록 한다.
adapter.notifyDataSetChanged()
})
.setNegativeButton("아니오", null)
.create()
.show()
return false
- helper를 사용해서 TodoDao객체를 생성하고, delete() 메소드 실행
- 메소드의 id는 아답타에서 리턴한 값
- num을 받아와서 삭제해주고, getlist() 로 새로 리스트를 가져와서 아답타에 넣어 출력해준다.

- 이렇게 창이 뜨고 네를 선택하면 삭제되면서 리스트가 새로고침된다.
- spring boot-android 연동 예정
- 웹서버 연결 예정
'국비교육(22-23)' 카테고리의 다른 글
76일차(4)/Android App(36) : AsyncTask(1) (0) | 2023.01.27 |
---|---|
76일차(3)/Android App(35) : WebView (1) | 2023.01.26 |
76일차(1)/Android App(33) : SQLite 활용(1) (0) | 2023.01.26 |
75일차(3)/Android App(32) : Shared Preferences(2) / menu (1) | 2023.01.26 |
75일차(2)/Android App(31) : Shared Preferences(1) (0) | 2023.01.25 |