본문 바로가기

Project

09_mvvm : Localization, Coroutine

728x90

검색어 기록을 담당하는 layout의 height을 wrap_content로 변경해 photoGridRecyclerView 위에서 보이도록 변경

//activity_collection.xml

<LinearLayout
        android:id="@+id/linear_search_history"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/white"
        android:orientation="vertical"
        android:visibility="invisible"
        app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
        tools:visibility="visible">

 

onOptionsItemSelected()를 이용해 선택된 menu의 item 별로 작업하도록 코드를 수정

//CollectionActivity.kt

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        // 매뉴를 생성

        Log.d(Constants.TAG, "CollectionActivity - onCreateOptionMenu() called")
        val inflater = menuInflater
        inflater.inflate(R.menu.top_app_bar_menu, menu)
        // 매뉴인플레이터로 매뉴xml과 매개변수로 들어오는 매뉴와 연결

        val searchManager = getSystemService(Context.SEARCH_SERVICE) as SearchManager
        // System으로 부터 SearchManager를 가져옴

        this.mSearchView =
            menu?.findItem(R.id.search_menu_item)?.actionView as androidx.appcompat.widget.SearchView

        this.mSearchView.apply {
            this.queryHint = "검색어를 입력해주세요"

            this.setOnQueryTextListener(this@CollectionActivity)
            // 위에서 정의한 구현할 setOnQueryTextListener와 현재 setOnQueryTextListener를 연결

            this.setOnQueryTextFocusChangeListener { _, hasFocus ->
                //hasFocus : SearchView의 활성화, 비활성화 상태를 가져오는 리스너
                when (hasFocus) {
                    // Linear_Search_History 활성화 유무를 변경
                    true -> {
                        Log.d(Constants.TAG, "CollectionActivity - 서치뷰 활성화")
                        binding.linearSearchHistory.visibility = View.VISIBLE
                    }
                    false -> {
                        Log.d(Constants.TAG, "CollectionActivity - 서치뷰 비활성화")
                        binding.linearSearchHistory.visibility = View.INVISIBLE
                    }
                }
            }

            // SearchView의 EditText를 가져온다.
            mSearchViewEditText = this.findViewById(androidx.appcompat.R.id.search_src_text)
        }

        return true
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {

        when (item.itemId) {
            R.id.more_menu_item -> {

            }
            R.id.search_menu_item -> {

                //textChanges() : editText의 내용이 있는지 없는지 확인
                //RxBinding을 통해서 text가 변경되면 Observable을 만듬
                val editTextChangeObservable = mSearchViewEditText.textChanges()

                // debounce 오퍼레이터 추가
                //Observable에서 발행된 item들의 원천인 Observable과 최종의 Subscriber 사이에서 조작
                val searchEditTextSubscription: Disposable =
                //글자가 입력되고 0.8초 후 onNext() 이벤트로 데이터 보내기
                    //debounce() : 연속적인 이벤트를 처리하는 흐름 제어 함수
                    editTextChangeObservable.debounce(800, TimeUnit.MILLISECONDS)
                        // subscribeOn() : 작업 스레드를 설정
                        // Schedulers.io() : 동기 I/O를 별도로 처리해 비동기 효율을 얻는 스케줄러
                        .subscribeOn(Schedulers.io())
                        //구독하여 이벤트에 대한 응답을 받게된다.
                        .subscribeBy(
                            onNext = {
                                Log.d(Constants.TAG, "onNext : $it")
                                if (it.isNotEmpty()) {
                                    searchPhotoApiCall(it.toString())
                                }
                            },
                            onComplete = {
                                //실행되면 흐름이 끊김
                                Log.d(Constants.TAG, "onComplete")
                            },
                            onError = {
                                //실행되면 흐름이 끊김
                                Log.d(Constants.TAG, "onError")
                            }
                        )
                compositeDisposable.add(searchEditTextSubscription)
                //Observable 객체에서 발행된 후 반환된 객체의 관리를 위해 compositeDisposable에 추가

                // SearchView의 EditText에 길이 제한, 컬러를 수정
                mSearchViewEditText.apply {
                    this.filters = arrayOf(InputFilter.LengthFilter(15))
                    this.setTextColor(Color.WHITE)
                    this.setHintTextColor(Color.WHITE)
                }
            }
        }

        return super.onOptionsItemSelected(item)
    }

enqueue()를

enqueue()를 이용한 CallBack 메소드로 구현되어 있는 코드를 coroutine로 변경

//RetrofitManager.kt

 private val mjob = Job()
    private val mScope = CoroutineScope(Dispatchers.Main + mjob)
    // http call 생성
    // 레트로핏 인터페이스 가져오기
    private val iretrofitService: IRetrofit_Service? =
        RetrofitClient.getClient(API.BASE_URL)?.create(IRetrofit_Service::class.java)

    //사진검색 api 호출
    fun searchPhotos(searchTerm: String?, completion: (RESPONSE_STATE, ArrayList<Photo>?) -> Unit) {
        val term = searchTerm.let { it } ?: ""

        mScope.launch {
            var parsedPhotoDataArray = ArrayList<Photo>()

            val job = launch(Dispatchers.IO) {
                val call =
                    iretrofitService?.serachPhotos(searchTerm = term).let { it } ?: return@launch

                val body = call.asJsonObject
                val results = body.getAsJsonArray("results")
                val total = body.get("total").asInt

                Log.d(Constants.TAG, "${HomeViewModel::class.java.simpleName} " +
                        "/ message : ${total}")

                if (total != 0) {
                    // JsonArray result[] 내부의 JsonObject를 하나씩 받아 반복 수행
                    results.forEach { resultItem ->
                        val resultItemObject = resultItem.asJsonObject // get object
                        val user =
                            resultItemObject.get("user").asJsonObject    // object -> user
                        val username = user.get("username").asString    // user -> username
                        val likeCount =
                            resultItemObject.get("likes").asInt     // object -> likes
                        val thumbnail =
                            resultItemObject.get("urls").asJsonObject.get("thumb").asString      // urls -> thumb
                        val createdAt =
                            resultItemObject.get("created_at").asString  // object -> create_at

                        Log.d(Constants.TAG, "${HomeViewModel::class.java.simpleName} " +
                                "/ message : ${user}")
                        Log.d(Constants.TAG, "${HomeViewModel::class.java.simpleName} " +
                                "/ message : ${username}")
                        Log.d(Constants.TAG, "${HomeViewModel::class.java.simpleName} " +
                                "/ message : ${likeCount}")
                        Log.d(Constants.TAG, "${HomeViewModel::class.java.simpleName} " +
                                "/ message : ${thumbnail}")
                        Log.d(Constants.TAG, "${HomeViewModel::class.java.simpleName} " +
                                "/ message : ${createdAt}")


                        val parser = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss")
                        // 실제 Date 형태
                        val formatter = SimpleDateFormat("yyyy년\nMM월 dd일")
                        // 변경하려는 Date 형태

                        val outputDateString = formatter.format(parser.parse(createdAt))
                        // Date를 원하는 형태로 치환

                        //Log.d(Constants.TAG, "RetrofitManager - onResponse() called / outputDateString : ${outputDateString}")

                        val photoItem = Photo(
                            author = username,
                            likesCount = likeCount,
                            thumbnail = thumbnail,
                            createdAt = outputDateString
                        )

                        parsedPhotoDataArray.add(photoItem)
                        //ArrayList에 Photo 타입의 데이터를 추가가
                    }
                }
            }

            job.join()
            if(job.isCompleted){
                completion(RESPONSE_STATE.OK, parsedPhotoDataArray)
            }

        }
    }

Localization : en이 기본, 한국어 추가

// strings.xml (b+ko)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">사진검색 API</string>

    <!-- activity_home.xml / HomeAcitivty.kt -->
    <string name="Photo_search">사진검색</string>
    <string name="User_search">사용자검색</string>
    <string name="Enter_search_word">검색어를 입력하시오.</string>
    <string name="String_length_limit">검색어 제한 : 15자</string>
    <string name="No_search_results">검색 결과가 없습니다. </string>

    <!-- customlayout_button -->
    <string name="search">SEARCH</string>

    <!-- activity_collection -->
    <string name="page_title">사진검색</string>
    <string name="more">더보기</string>
    <string name="content_description_search">사진을 검색합니다.</string>
    <string name="content_description_more">더보기 메뉴입니다.</string>
    <string name="search_history_save">검색어 저장</string>
    <string name="search_history_clear">전체 삭제</string>
</resources>
//strings.xml

<resources>
<string name="app_name">Photo search API</string>

<!-- activity_home.xml / HomeAcitivty.kt -->
<string name="Photo_search">Photo search</string>
<string name="User_search">User Search</string>
<string name="Enter_search_word">Please enter a search term.</string>
<string name="String_length_limit">Search term limit: 15 characters</string>
<string name="No_search_results">No results were found for your search.</string>

<!-- customlayout_button -->
<string name="search">SEARCH</string>

<!-- activity_collection -->
<string name="page_title">Photo search</string>
<string name="more">View more</string>
<string name="content_description_search">Search for photos.</string>
<string name="content_description_more">This is the more menu.</string>
<string name="search_history_save">Save search term</string>
<string name="search_history_clear">Delete all</string>
</resources>

'Project' 카테고리의 다른 글

09_mvvm : Network communication using retrofit - Search User Test  (0) 2021.02.23
Goni95_App  (0) 2021.02.02