728x90
Api 연결
dependencies
implementation ("com.google.code.gson:gson:2.10.1")
implementation ("com.squareup.retrofit2:retrofit:2.9.0")
implementation ("com.squareup.retrofit2:converter-gson:2.9.0")
implementation ("com.squareup.okhttp3:okhttp:4.10.0")
implementation ("com.squareup.okhttp3:logging-interceptor:4.10.0")
ImageSearchApi
import com.example.imagesearch.data.ImageSearch
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
private const val AUTH_HEADER = //API//
private const val SORT_DEFAULT = "accuracy"
private const val PAGE_NUMBER = 1
private const val API_MAX_RESULT = 80
interface ImageSearchApi {
@GET("/v2/search/image")
suspend fun getImage(
@Header("Authorization") authorization: String = AUTH_HEADER,
@Query("query") query: String,
@Query("sort") sort: String = SORT_DEFAULT,
@Query("page") page: Int = PAGE_NUMBER,
@Query("size") size: Int = API_MAX_RESULT
): ImageSearch
}
RetrofitInstance
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitInstance {
private const val BASE_URL = "https://dapi.kakao.com"
private val retrofit by lazy {
Retrofit
.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
val imageSearchApi: ImageSearchApi by lazy {
retrofit.create(ImageSearchApi::class.java)
}
}
DataDTO
data class ImageSearch(
val documents: MutableList<Document>,
val meta: Meta
)
data class Document(
val collection: String,
@SerializedName("datetime")
val datetime: String,
@SerializedName("display_sitename")
val displaySiteName: String,
@SerializedName("doc_url")
val docUrl: String,
val height: Int,
@SerializedName("image_url")
val imageUrl: String,
@SerializedName("thumbnail_url")
val thumbnailUrl: String,
val width: Int
)
data class Meta(
@SerializedName("is_end")
val isEnd: Boolean,
@SerializedName("pageable_count")
val pageableCount: Int,
@SerializedName("total_count")
val totalCount: Int
)
SearchImageFragment에서 사용
package com.example.imagesearch.fragment
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.InputMethodManager
import android.widget.EditText
import androidx.core.content.edit
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import com.example.imagesearch.adapter.ImageAdapter
import com.example.imagesearch.data.Document
import com.example.imagesearch.databinding.FragmentSearchImageBinding
import com.example.imagesearch.listener.ImageClickListener
import com.example.imagesearch.manager.DocumentsManager
import com.example.imagesearch.retrofit.RetrofitInstance
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
class SearchImageFragment : Fragment(), ImageClickListener {
private var _binding: FragmentSearchImageBinding? = null
private val binding get() = _binding!!
private lateinit var imageAdapter: ImageAdapter
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSearchImageBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
with(binding) {
btnSearch.setOnClickListener {
val query = etSearch.text.toString()
if (query.isEmpty()) return@setOnClickListener
saveData(query)//검색어 저장
//Api 연결 시 IO 으로 연결 하고, UI갱신은 withContext로 Main에서 처리
CoroutineScope(Dispatchers.IO).launch {
val responseData = RetrofitInstance.imageSearchApi.getImage(query = query)
DocumentsManager.addDocument(responseData.documents)
withContext(Dispatchers.Main) {
initRecyclerView()
}
}
downKeyBoard(requireContext(), etSearch)
}
}
}
private fun initRecyclerView() {
binding.recyclerView.apply {
imageAdapter = ImageAdapter(this@SearchImageFragment)
adapter = imageAdapter
layoutManager = GridLayoutManager(context, 2)
//검색 결과 80개 표시
val searchList = DocumentsManager.getDocument()
val sublist = if (searchList.size > 80) searchList.subList(0, 80) else searchList
imageAdapter.submitList(sublist)
}
}
override fun onDestroy() {
super.onDestroy()
_binding = null
}
//이미지 클릭시 좋아요 표시
override fun onClickImage(document: Document, position: Int) {
DocumentsManager.toggleLike(document)
imageAdapter.notifyItemChanged(position)
}
//키보드 내리기
private fun downKeyBoard(context: Context, editText: EditText) {
val inputMethodManager =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
editText.clearFocus()
inputMethodManager.hideSoftInputFromWindow(editText.windowToken, 0)
}
//검색어 저장
private fun saveData(keyWord: String) {
activity?.getSharedPreferences(SEARCH_WORD, Context.MODE_PRIVATE)?.edit {
putString(KEYWORD, keyWord)
apply()
}
}
//저장된 검색어 가져오기
private fun getData() {
val saveKeyWord = activity?.getSharedPreferences(SEARCH_WORD, Context.MODE_PRIVATE)
binding.etSearch.setText(saveKeyWord?.getString(KEYWORD, ""))
}
override fun onResume() {
getData()
initRecyclerView()
super.onResume()
}
companion object {
private const val SEARCH_WORD = "searchWord"
private const val KEYWORD = "keyWord"
}
}