[kotlin] RecyclerView를 이용하여 지하철 노선도 만들기

지하철 관련 앱을 제작하려고 했다.

지하철 혼잡도를 지하철 노선도 상에 표현을 하고자 지하철 노선도를 화면에 띄우는 것 부터 완성하기로 했다.

 

여러 방식을 찾아보던중

이미지를 그대로 가져와서, 사용하기에는 혼잡도를 지하철 노선 상에 표현하는 것이 어렵다고 생각했다.

 

그러던중 네이버 버스 정보가 생각이 났다.

etc-image-0

 

위의 이미지를 참고하여, 리사이클러뷰를 이용해 만들어보기로 했다.

 

1. 지하철 노선 이미지 생성

가장 먼저 이미지 아이콘을 만들어야 했다. 아래의 역과 위의 역이 이어져야 하므로 아래와 같은 이미지를 만들기로 했다.

etc-image-1

Drawble Resourse를 이용하여 제작했다. (drawable - new - Drawable Resource File)

etc-image-2

<item_o.xml>

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
    <item>
        <shape android:shape="rectangle">
            <size
                android:width="60dp"
                android:height="30sp" />
        </shape>
    </item>
    <item
        android:right="60sp" android:left="-5sp">
        <shape android:shape="line">
            <stroke
                android:width="5sp"
                android:color="@color/black"/>
        </shape>
    </item>
    <item android:left="25sp" android:right="25sp" android:top="5sp" android:bottom="5sp">
        <shape android:shape="ring" android:innerRadiusRatio="2.5" android:thickness="5sp" android:useLevel="false">
            <solid android:color="@color/black"/>
        </shape>
    </item>
    <item android:left="60sp" android:right="-5sp">
        <shape android:shape="line">
            <stroke
                android:width="5sp"
                android:color="@color/black"/>
        </shape>
    </item>

</layer-list>

위를 통해 만들어진 이미지는 아래와 같다. 어차피 rotate를 이용해 90도를 회전시켜줘서 사용할거라 그냥 가로로 만들었다.

blob

2. 아이템 뷰 만들기

리사이클러 뷰에 적용될 아이템 뷰를 만들었다.

 

크게 3개의 뷰로 이루어져 있다.

가장 왼쪽은 지하철 혼잡도를 나타내기 위한 이미지 사용,

중앙은 노선 

마지막은 지하철 역이름이다. 

 

<subway_item.xml>

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <ImageView
            android:id="@+id/subway_status"
            android:layout_marginRight="-10sp"
            android:layout_gravity="center"
            android:layout_width="40sp"
            android:layout_height="40sp"
            />
        <androidx.appcompat.widget.AppCompatButton
            android:rotation="90"
            android:background="@drawable/item_o"
            android:layout_width="wrap_content"
            android:layout_height="60sp">
        </androidx.appcompat.widget.AppCompatButton>
        <TextView
            android:layout_marginLeft="5sp"
            android:text="길음역"
            android:layout_gravity="center"
            android:layout_marginStart="-10sp"
            android:textSize="20sp"
            android:textColor="@color/black"
            android:textStyle="bold"
            android:id="@+id/subway_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content">
        </TextView>
    </LinearLayout>
</LinearLayout>

etc-image-4
미리보기 이미지

3. 데이터 생성

리사이클러 뷰에서 바뀌는 데이터는 역이름, 지하철 혼잡도 두개이다.

두 개를 데이터 클래스로 생성해주었다.

data class SubwayData(
    val name : String,
    val status : String?
)

4.  어뎁터 생성

어뎁터를 생성해준다.

import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView

class SubwayAdapter(private val context: Context) : RecyclerView.Adapter<SubwayAdapter.ViewHolder>() {

    var datas = mutableListOf<SubwayData>()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val view = LayoutInflater.from(context).inflate(R.layout.subway_item,parent,false)
        return ViewHolder(view)
    }

    override fun getItemCount(): Int = datas.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.bind(datas[position])
    }

    inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {

        private val subWayName: TextView = itemView.findViewById(R.id.subway_name)
        private val subWayStatus : ImageView = itemView.findViewById(R.id.subway_status)
        fun bind(item: SubwayData) {
            subWayName.text = item.name
            if(item.status != null) subWayStatus.setImageResource(R.drawable.icon_subway_red)
        }
    }
}

5. activity_main에 리사이클러 뷰 추가

적용할 리사이클러뷰를 메인 레이아웃에 추가해준다.

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/RV_subway"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</androidx.constraintlayout.widget.ConstraintLayout>

6. 어뎁터와 레이아웃 매니저 지정

간단하게 테스트를 해보기위해 데이터는 임의로 넣었다.

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.recyclerview.widget.RecyclerView

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

        val subwayData = mutableListOf(SubwayData("길음역",null),SubwayData("성신여대역",null),SubwayData("창동역","confusion"),SubwayData("노원역",null))
        val subwayAdapter = SubwayAdapter(this)

        findViewById<RecyclerView>(R.id.RV_subway).adapter = subwayAdapter
        subwayAdapter.datas = subwayData
        subwayAdapter.notifyDataSetChanged()
    }
}

7. 완성

etc-image-5