본문 바로가기

Android

Kotlin 이슈 5 include 대신 정적 fragment, Unresolved reference.

반응형

지난 이야기

저번 시간에는 크롤링 코드를 작성했습니다. 모든 코드를 MainActivity.kt에 집어넣으려니 상당히 난잡해보이더라고요.

2020/01/22 - [Android + Kotlin + Figma] - doAsync, AsyncTask, Coroutines 안드로이드 비동기 정답은?!

 

doAsync, AsyncTask, Coroutines 안드로이드 비동기 정답은?!

네트워크 연결은 비동기로 처리하는 게 보편적이죠! 과연 안드로이드 + 코틀린에서는 어떤 방식을 많이 쓸까 알아봅시다. 종류 doAsync AsyncTask Coroutines Anko를 이용한 doAsync { ... } 제일 처음 발견한 방..

roomedia.tistory.com

class MainActivity : AppCompatActivity() {

        /* var/val for crawling */
    private lateinit var queue: RequestQueue
    private val url = "https://m.dhlottery.co.kr/gameResult.do?method=byWin&drwNo="
    private val pattern = """동행복권 (\d{3})회 당첨번호 ((\d{1,2},?){6})\+\d.+ (당첨금액 (\d,?)+원)""".toRegex()
        /* var/val for crawling */

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        fab.setOnClickListener { view ->
            Snackbar.make(view, "text", Snackbar.LENGTH_LONG)
                .setAction("Action", null).show()
        }

                /* crawling start */
        initCrawl()
        getRound()
                /* crawling end */

        /* wishlist start */
        val mAdapter = WishlistRVAdapter(this, wishList)
        listRecyclerView.adapter = mAdapter

        val lm = LinearLayoutManager(this)
        listRecyclerView.layoutManager = lm
        listRecyclerView.setHasFixedSize(true)
        /* wishlist end */
    }

    private fun getRound() {
        ...
    }

    private fun initCrawl() {
        ...
    }

    private fun getCrawl(round: Int = 0) {
                ...
    }

    private fun setBall(view: TextView, num: String) {
        ...
    }

    private fun getResourceId(num: String): Int {
                ...
    }
}

TL; DR

  1. fragment를 상속하자!!
  2. 모든 view 요소 앞에 view!!.을 붙이자!
  3. this 들어가는 자리에는 context!!

따로 클래스로 만들자!

위시리스트 관련 코드도, 로또 구매 관련 코드도 필요한데 이를 하나의 파일에서 처리하면 점점 더 복잡해지겠죠? 그래서 크롤링 코드만 따로 떼어 클래스로 만들어봤습니다.

class CrawlLotto {
    private lateinit var queue: RequestQueue
    private val url = "https://m.dhlottery.co.kr/gameResult.do?method=byWin&drwNo="
    private val pattern = """동행복권 (\d{3})회 당첨번호 ((\d{1,2},?){6})\+\d.+ (당첨금액 (\d,?)+원)""".toRegex()

    constructor() {
        initCrawl()
        getRound()
    }

    private fun initCrawl() {
        queue = Volley.newRequestQueue(this)
    }

    private fun getRound() {
        val startDate = SimpleDateFormat("yyyy-MM-dd").parse("2002-12-07")!!
        val now = Date()
        val currentRound = ((now.time - startDate.time) / 604800000).toInt()
        val dataAdapter = ArrayAdapter<String>(
            this,
            android.R.layout.simple_spinner_dropdown_item,
            (currentRound downTo 0).map { "${it + 1}회" }
        )
        round.adapter = dataAdapter
        round.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                getCrawl(parent!!.adapter.count - position)
            }

            override fun onNothingSelected(parent: AdapterView<*>?) {
            }
        }
    }
        ...
}

아마 여기까지만 작성해도 round가 빨간색으로 에러를 뿜을 것입니다. 마우스를 가져가보면 아래와 같이 에러를 표시합니다.

Unresolved reference.
None of the following candidates is applicable because of receiver type mismatch:
...

 

fragment 상속

Activity에서 사용했던 것 마냥 view를 변수로 사용하거나 findViewById를 사용하기 위해선 fragment를 상속해야 한다고 합니다. 도전! Fragment를 상속하고 모든 this를 context!!로 바꾸고, view 내부 요소들은 앞에 view!!.를 붙여주었습니다.

class CrawlLottoFragment : Fragment() {
    private lateinit var queue: RequestQueue
    private val url = "https://m.dhlottery.co.kr/gameResult.do?method=byWin&drwNo="
    private val pattern = """동행복권 (\d{3})회 당첨번호 ((\d{1,2},?){6})\+\d.+ (당첨금액 (\d,?)+원)""".toRegex()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.content_lotto_round, container, true)
    }

        override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        initCrawl()
        getRound()
    }

    private fun initCrawl() {
        queue = Volley.newRequestQueue(context)
    }

    private fun getRound() {
        val startDate = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.KOREA).parse("2002-12-07 20:00")!!
        val now = Date()
        val currentRound = ((now.time - startDate.time) / 604800000).toInt()
        val dataAdapter = ArrayAdapter<String>(
            context!!,
            R.layout.support_simple_spinner_dropdown_item,
            (currentRound downTo 0).map { "${it + 1}회" }
        )

        view!!.round.adapter = dataAdapter
        view!!.round.onItemSelectedListener = object: AdapterView.OnItemSelectedListener {
            override fun onItemSelected(
                parent: AdapterView<*>?,
                view: View?,
                position: Int,
                id: Long
            ) {
                getCrawl(parent!!.adapter.count - position)
            }

            override fun onNothingSelected(parent: AdapterView<*>?) {}
        }
    }

    private fun getCrawl(round: Int = 0) {
        val stringRequest = StringRequest(
            Request.Method.GET,
            url + round,
            Response.Listener<String> {
                    response ->
                println(pattern)
                println(response)
                println(pattern.find(response)!!)
                val result = pattern.find(response)!!.groups
                val nums = result[2]!!.value.split(",")

                val balls = view!!.lottoConstraint.children.toList().filterIsInstance<TextView>()
                for ((ball: TextView, num: String) in balls zip nums) {
                    setBall(ball, num)
                }
                view!!.prize.text = result[4]!!.value
            },
            Response.ErrorListener { Log.e("error:", "parsing error") }
        )
        queue.add(stringRequest)
    }

    private fun setBall(view: TextView, num: String) {
        view.text = num
        view.setBackgroundResource(
            getResourceId(num)
        )
    }

    private fun getResourceId(num: String): Int {
        val packageName = context!!.packageName
        val resContext = context!!.createPackageContext(packageName, 0)
        val res = resContext.resources
        return res.getIdentifier("clr" + (num.toInt() / 10 + 1), "drawable", packageName)
    }
}

activity_main.xml 상의 includefragment 태그로 바꿔주었습니다. android:name 속성에 패키지 명 + 클래스 명으로 fragment를 지정해주시면 됩니다.

  ...
    <!-- activity_main.xml -->
    <fragment
      android:id="@+id/include_lotto_round"
      android:name="com.rhsslug.lottowishlist.CrawlLottoFragment"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_margin="16dp"
      app:layout_constraintTop_toTopOf="@+id/main"/>
  ...

혹시 몰라 fragment에 사용된 xml도 보여드립니다.

<!-- content_lotto_round.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView 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/pastLottoCardView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/lottoConstraint"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp">

        <Spinner
            android:id="@+id/round"
            android:layout_width="0dp"
            android:layout_height="40dp"
            android:layout_marginTop="24dp"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/num1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="28dp"
            android:width="40dp"
            android:height="40dp"
            android:gravity="center|center_horizontal|center_vertical"
            android:textColor="@android:color/background_light"
            android:textStyle="bold"
            app:layout_constraintEnd_toStartOf="@+id/num2"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/round" />

        <TextView
            android:id="@+id/num2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:width="40dp"
            android:height="40dp"
            android:gravity="center|center_horizontal|center_vertical"
            android:textColor="@android:color/background_light"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="@+id/num1"
            app:layout_constraintEnd_toStartOf="@+id/num3"
            app:layout_constraintStart_toEndOf="@+id/num1" />

        <TextView
            android:id="@+id/num3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:width="40dp"
            android:height="40dp"
            android:gravity="center|center_horizontal|center_vertical"
            android:textColor="@android:color/background_light"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="@+id/num2"
            app:layout_constraintEnd_toStartOf="@+id/num4"
            app:layout_constraintStart_toEndOf="@+id/num2" />

        <TextView
            android:id="@+id/num4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:width="40dp"
            android:height="40dp"
            android:gravity="center|center_horizontal|center_vertical"
            android:textColor="@android:color/background_light"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="@+id/num3"
            app:layout_constraintEnd_toStartOf="@+id/num5"
            app:layout_constraintStart_toEndOf="@+id/num3" />

        <TextView
            android:id="@+id/num5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:width="40dp"
            android:height="40dp"
            android:gravity="center|center_horizontal|center_vertical"
            android:textColor="@android:color/background_light"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="@+id/num4"
            app:layout_constraintEnd_toStartOf="@+id/num6"
            app:layout_constraintStart_toEndOf="@+id/num4" />

        <TextView
            android:id="@+id/num6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:width="40dp"
            android:height="40dp"
            android:gravity="center|center_horizontal|center_vertical"
            android:textColor="@android:color/background_light"
            android:textStyle="bold"
            app:layout_constraintBottom_toBottomOf="@+id/num5"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/num5" />

        <TextView
            android:id="@+id/prize"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:gravity="right"
            android:text="원"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="@+id/round"
            app:layout_constraintTop_toBottomOf="@+id/num1" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.cardview.widget.CardView>

후기

kotlin으로 작성하긴 했는데.. 그냥 자바 코드와 별로 차이가 없는 형태입니다.

함수형으로 작성하려면 어떻게 해야할지 감이 잘 안 오네요..

참고

 

프래그먼트  |  Android 개발자  |  Android Developers

A Fragment represents a behavior or a portion of user interface in an Activity. You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a fragment as a modular section…

developer.android.com

 

 

코틀린/안드로이드 프래그먼트 사용하기 (Android Using Fragment)

Embedded System

esocwiki.blogspot.com

 

 

[안드로이드] Binary XML file line #13: Error inflating class fragment 해결 방법

이 에러는 해결할 첫번째 방법 xml Layout에 커스텀 뷰를 넣을 경우는 대부분은 아시겠지만.. Packgage : com.test Class : CustomComponent 위를 main.xml에 넣을려구 할 경우 gakari.tistory.com

 

 

Fragment에서 findViewById하는법

제가 Fragment에서 ImageView를 만들어 사용하려고하는데요 이미지뷰는 xml에서 만들어진거구요. 근데 이제 Fragment에서 이미지뷰에 접근할때 findViewById메소드를 사용못하잖아요.... 액티비티에서만 된다는데 어떻게하죠?? 소스코드 public class TestClass extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container,

hashcode.co.kr

 

반응형