반응형
지난 이야기
저번 시간에는 크롤링 코드를 작성했습니다. 모든 코드를 MainActivity.kt에 집어넣으려니 상당히 난잡해보이더라고요.
2020/01/22 - [Android + Kotlin + Figma] - doAsync, AsyncTask, Coroutines 안드로이드 비동기 정답은?!
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
fragment
를 상속하자!!- 모든 view 요소 앞에
view!!.
을 붙이자! 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
상의 include
도 fragment
태그로 바꿔주었습니다. 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' 카테고리의 다른 글
Kotlin 이슈 7 Room 사용하여 안드로이드 로컬 db 시작하기 (0) | 2020.02.02 |
---|---|
Kotlin 이슈 6 fragment lifecycle (0) | 2020.02.01 |
Kotlin 이슈 4 기능 별로 레이아웃 나누기, include (0) | 2020.02.01 |
Object Expressions 코틀린 기초 문법 (3) - Koans 풀이 (0) | 2020.01.28 |
Extension fun, 코틀린 기초 문법 (2) - Koans 풀이 (0) | 2020.01.27 |