도서리뷰 앱 만들기(2) - 도서리뷰저장하기

2022. 12. 23. 17:41Android

  • item_book.xml
  • 이미지뷰랑 textview를 하나더 추가햇습니다.
  • drawble에 backgroud.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"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="20dp">

    <ImageView
        android:background="@drawable/background_gray_stroke_radius_8"
        android:id="@+id/coverImageView"
        android:layout_width="100dp"
        android:layout_height="100dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"/>

    <TextView
        android:id="@+id/titleTextView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="12dp"
        android:ellipsize="end"
        android:maxLines="1"
        android:text="안드로이드 마스터하기"
        android:textColor="@color/black"
        android:textSize="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@id/coverImageView"
        app:layout_constraintTop_toTopOf="parent"/>

        <TextView
            android:id="@+id/descriptionTextView"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:ellipsize="end"
            android:maxLines="3"
            android:textSize="12dp"
            android:layout_marginTop="12dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="@id/titleTextView"
            app:layout_constraintTop_toBottomOf="@id/titleTextView" />

</androidx.constraintlayout.widget.ConstraintLayout>

-새로 알게된 용어

ellipsize : width를 넘어갈 경우 textview의 ellipsize로 처리. (글자가 넘어갈경우)

android:ellipsize="end"
android:maxLines="1"

max를 주거나 lines를 주면 내용이 길더라도 ......이렇게표시가된다

 

모든 Http URL에 대해서 접근 허용

AndroidManifest의 application 태그에서 android:usesCleartextTraffic를 true로 설정하시면 모든 Http 주소에 접근할 수 있습니다.

android:supportsRtl="true"

 

  • Glide 로 이미지 쉽게 불러오기

이미지의 경우 imageview에 표시 할 수 있는데 이때 소스로 자원에 있는 이미지 경로를 넣어주거나

하지만 현재 받아오는것은 이미지의 uri 이기 때문에 이를 쉽게 받아오기 위해  Gide를 사용할수있습니다,

 

 

Glide 의존성 주입

implementation 'com.github.bumptech.glide:glide:4.14.2'
annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2'

 

Glide
    .with(binding.coverImageView.context)
    .load(bookModel.coverSmallUrl)
    .into(binding.coverImageView)

위 사용 예시에서는 with구문을 사용하여 url 을 로드해 오고 이를 into로 어느 이미지 뷰에 뿌려줄지

정하는데 여기에 활용한 바인등을 사용하여 커버 이미지뷰에 뿌려주도록햇습니다.

코드를추가한걸 애뮬레이터를 실행하면

 

 

  • 도서 목록을 보여주었기때문에  검색어 히스토리 를  저장해보겟다
  • 안드로이드 Room DB 사용

  먼저 그래들 모듈에 Room을 사용하기위해 의존성을 추가한다.

dependencies {
    implementation 'androidx.room:room-runtime:2.4.3'
    kapt 'androidx.room:room-compiler:2.4.3'

 

플러그인에 kotlin -kapt를 추가한다.

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-kapt'
    id 'Kotlin-parcelize'
  • 검색 히스토리 dao 구현
package com.example.myapplication.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import com.example.myapplication.model.History

@Dao
interface HistoryDao {
    @Query("SELECT * FROM history")
    fun getAll() : List<History>

    @Insert
    fun insertHistory(history: History)

    @Query("DELETE FROM history WHERE keyword == :keyword")
    fun delete(keyword: String)
}

데이터베이스 테이블 사용할 dao입니다. 검색히스토리를 전부 getall()가져오고,

삽입 삭제하는 쿼리를 정의하고 ,삭제의경우 일치하는 keyword를 지워주도록합니다.

 

  • Review Dao 구현.
package com.example.myapplication.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.example.myapplication.model.Review

@Dao
interface ReviewDao {

    @Query("SELECT * FROM review WHERE id == :id")
    fun getOneReview (id : Int): Review

    @Insert(onConflict = OnConflictStrategy.REPLACE)
    fun saveReview(review: Review)
}
  • RoomDatabase 만들기
  • 데이터베이스를 상속한 앱데이터베이스를 만들어주고 사용하도록햇다.
package com.example.myapplication

import android.content.Context
import androidx.room.Database
import androidx.room.PrimaryKey
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
import com.example.myapplication.dao.HistoryDao
import com.example.myapplication.dao.ReviewDao
import com.example.myapplication.model.History
import com.example.myapplication.model.Review

@Database(entities = [History::class, Review::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
    abstract fun historyDao(): HistoryDao
    abstract fun ReviewDao(): ReviewDao
}

 

  • 검색리뷰  메인액티비티 만들기
package com.example.myapplication

import android.os.Bundle
import android.os.PersistableBundle
import androidx.appcompat.app.AppCompatActivity
import androidx.room.Room
import androidx.room.util.DBUtil
import com.bumptech.glide.Glide
import com.example.myapplication.databinding.ActivityDetailBinding
import com.example.myapplication.databinding.ActivityMainBinding
import com.example.myapplication.model.Book
import com.example.myapplication.model.Review

class DetailActivity : AppCompatActivity() {
    private lateinit var binding: ActivityDetailBinding
    private lateinit var db: AppDatabase

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityDetailBinding.inflate(layoutInflater)
        setContentView(binding.root)

        db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java,
            "BookSearchDB"
        ).build()
        val model = intent.getParcelableExtra<Book>("bookModel")
        binding.titleTextView.text = model?.title.orEmpty()
        binding.descriptionTextView.text = model?.description.orEmpty()

        Glide.with(binding.coverImageView.context)
            .load(model?.coverSmallUrl.orEmpty())
            .into(binding.coverImageView)
        Thread {
            val review = db.ReviewDao().getOneReview(model?.id?.toInt() ?: 0)
            runOnUiThread {
                binding.reviewEditTExt.setText(review?.review.orEmpty())
            }
        }.start()

        binding.saveButton.setOnClickListener {
            Thread {
                db.ReviewDao().saveReview(
                    Review(
                        model?.id?.toInt() ?: 0,
                        binding.reviewEditTExt.text.toString()
                    )
                )

            }.start()
        }
    }
}

 

  • 메인 액티비티 사용
  • 메인에서 전역으로 db로 앱데이터베이스를 불러올때 초기화하여 사용하는예시입니다.
  • 검색하는 경우 검색한 키워들 저장하는 함수를 실행해 데이터베이스에 히스토리를 삽입
//...
private lateinit var db: AppDatabase
    }
   //...
   db = Room.databaseBuilder(
            applicationContext,
            AppDatabase::class.java,
            "BookSearchDB"
        ).build()
        //...
  private fun showHistoryRecyclerView() {
        Thread {
            val keywords = db.historyDao().getAll().reversed()

            runOnUiThread {
                binding.historyRecyclerView.isVisible = true
                historyAdapter.submitList(keywords.orEmpty())
            }

        }.start()

        binding.historyRecyclerView.isVisible = true

    }

    private fun hideHistoryRecyclerView() {
        binding.historyRecyclerView.isVisible = false
    }

    private fun saveSearchKeyword(keyword: String) {
        Thread {
            db.historyDao().insertHistory(History(null, keyword))
        }.start()
    }

    private fun deleteSearhKeyword(keyword: String) {
        Thread {
            db.historyDao().delete(keyword)
        }.start()

    }

 

  • 애뮬레이터 실행