안녕하세요.

구글맵 도움없이 그냥 위도와 경도만 얻어오는 방법을 알아볼게요

그냥 간단히 30분정도면 알아낼 수 있을 것 같아서 구글링 했는데 몇 시간을 소비했어요

왜 이렇게 다들 무언가 붙혀놨거나 다닥다닥 때어놔서 설명 해놨는지 참... 


일단 위치정보를 사용해야 하기 때문에 매니페스트에 권한을 추가해줘요

AndroidManifest.xml

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />


activitiy_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">

<Button
android:id="@+id/button"
android:text="현재위치 가져오기"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

<TextView
android:id="@+id/TV_Result"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="30sp"
/>

</LinearLayout>


MainActivity.kt

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.location.Location
import android.location.LocationListener
import android.location.LocationManager
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.activity_main.*


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val lm = getSystemService(Context.LOCATION_SERVICE) as LocationManager
        button?.setOnClickListener {
            val isGPSEnabled: Boolean = lm.isProviderEnabled(LocationManager.GPS_PROVIDER)
            val isNetworkEnabled: Boolean = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
            //매니페스트에 권한이 추가되어 있다해도 여기서 다시 한번 확인해야함
            if (Build.VERSION.SDK_INT >= 23 &&
                    ContextCompat.checkSelfPermission(applicationContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this@MainActivity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), 0)
            } else {
                when { //프로바이더 제공자 활성화 여부 체크
                    isNetworkEnabled -> {
                        val location =
                            lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER) //인터넷기반으로 위치를 찾음
                        getLongitude = location?.longitude!!
                        getLatitude = location.latitude
                        toast("현재위치를 불러옵니다.")
                    }
                    isGPSEnabled -> {
                        val location =
                            lm.getLastKnownLocation(LocationManager.GPS_PROVIDER) //GPS 기반으로 위치를 찾음
                        getLongitude = location?.longitude!!
                        getLatitude = location.latitude
                        toast("현재위치를 불러옵니다.")
                    }
                    else -> {

                    }
                }
                //몇초 간격과 몇미터를 이동했을시에 호출되는 부분 - 주기적으로 위치 업데이트를 하고 싶다면 사용
                // ****주기적 업데이트를 사용하다가 사용안할시에는 반드시 해제 필요****
                /*lm.requestLocationUpdates(LocationManager.GPS_PROVIDER,
                        1000, //몇초
                        1F,   //몇미터
                        gpsLocationListener)
                lm.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,
                        1000,
                        1F,
                        gpsLocationListener)
                //해제부분. 상황에 맞게 잘 구현하자
                lm.removeUpdates(gpsLocationListener)*/
            }
        }
        lm.removeUpdates(gpsLocationListener)
    }

    //위에 *몇초 간격과 몇미터를 이동했을시에 호출되는 부분* 에 필요한 정보
    //주기적으로 위치 업데이트 안할거면 사용하지 않음
    val gpsLocationListener = object : LocationListener {
        override fun onLocationChanged(location: Location) {
            val provider: String = location.provider
            val longitude: Double = location.longitude
            val latitude: Double = location.latitude
            val altitude: Double = location.altitude
        }

        //아래 3개함수는 형식상 필수부분
        override fun onStatusChanged(provider: String, status: Int, extras: Bundle) {}
        override fun onProviderEnabled(provider: String) {}
        override fun onProviderDisabled(provider: String) {}
    }
}

자세한 설명은 주석에 충분히 설명해놨어요

이렇게 간단한 것을 왜 이리 해맸는지...

반응형

안녕하세요.

이번에는 앱을 개발하면서 많이 쓰이는 리사이클러뷰에 대해 알아보려고 해요

이제는 ListView 보다는 유연하고 쉬운 RecyclerView를 권장해요

Anko 라이브러리에 있는 RecylcerView를 사용하면 쉽게 할 수 있지만

이제는 deprecated 되어서 더 이상 지원하지 않는다고 해요

https://github.com/Kotlin/anko

지원중단!

그래서 앞으로 새로 개발할거면 다른 라이브러리를 사용하거나(Splitties 등) 직접 구현하는게 좋아요

리사이클러뷰를 사용하려면 최소한 필요한 5가지가 있어요

Java 폴더에는 MainActivity, RecyclerViewAdapter.kt(파일명 마음대로), Item.kt(파일명 마음대로) 이 3가지가 필요하구요

layout폴더에는 activity_main.xml, showitem.xml(파일명 마음대로) 이 2가지가 필요해요

적어도 5가지는 만들어야 리사이클러뷰를 사용할 수 있어요

근데 저 파일들이 무슨 역할을 하느냐!! 일단 감잡을 수 있게 간단히만 설명할게요


MainActivity = 리사이클러뷰를 앱에 올려주는 역할을 해요

Item.kt = 리사이클러뷰에 표현할 데이터를 필요로 하는 곳에 알려주는 역할을 해요 

RecyclerViewAdapter.kt = 가장 중요한 부분. 리사이클러뷰를 전체적인 부분을 구현하는 역할을해요

activity_main.xml = 레이아웃에 리사이클러뷰 라는 '틀'만 만드는 역할이에요

showitem.xml = 아파트가 리사이클러뷰라고 한다면 아파트에 있는 집들은 showitem.xml 이걸로 표현할 수 있어요.

(완성돤 화면에서는
list의 첫번째 데이터
list의 두번째 데이터
라고 표현된 곳)

----------------------

activity_main.xml 안에  showitem.xml가 자식으로 들어간다고 생각하면 쉬워요.

----------------------


이제 본격적으로 알아보도록 해요 (변수를 형식적이게 안하고 구별해낼 수 있도록 특별하게 만들었습니다)

activitiy_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/idOfRecyclerView_from_acitivity_main"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

<androidx.recyclerview.widget.RecyclerView
android:id="@+id/idOfRecyclerView_from_acitivity_main" ★ID를 잘 기억하세요. Mainactivity에 쓰입니다★
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

activitiy_main에서는 위 부분만 추가해주시면 되요

 


showitem.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    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:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_gravity="center"
        android:text="textView1"
        android:textSize="30sp" />

    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="textView2"
        android:textSize="30sp"
     />
</LinearLayout>

 

★텍스트뷰들의 ID를 잘 기억하세요. RecyclerViewAdapter.kt의 holder1 클래스에서 쓰입니다★


MainActivity.kt

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import java.util.*

class MainActivity : AppCompatActivity() {
    //리사이클뷰를 레이아웃에 올리기 위해 참조용 변수
    var recyclerView: RecyclerView? = null
    //Item.kt에 있는 변수들 참조용. Item.kt안에 변수가 여러개니 ArrayList.
    var list = arrayListOf<Item>(
        Item("list의 첫번째 데이터","list의 두번째 데이터"),
        Item("2list의 첫번째 데이터","2list의 두번째 데이터"),
        Item("3list의 첫번째 데이터","3list의 두번째 데이터"),
        Item("4list의 첫번째 데이터","4list의 두번째 데이터")
    )
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //이 변수에 layout폴더에 있는 activity_main에 있는 리사이클러뷰를 연결시켜준다.
        recyclerView = findViewById<View>(R.id.idOfRecyclerView_from_acitivity_main) as RecyclerView
        //리사이클러뷰 크기가 변하지 않게 함
        recyclerView!!.setHasFixedSize(true)
        //리사이클러뷰를 앱에 그려주기 위해 필요하다.
        val layoutManager = LinearLayoutManager(this)
        layoutManager.orientation = LinearLayoutManager.VERTICAL
        recyclerView!!.layoutManager = layoutManager

        //RecyclerViewAdapter.kt에 있는 클래스 a1를 연결한다.(어뎁터를 연결한다는 의미)
        val adapter = a1(applicationContext, list)
        recyclerView!!.adapter = adapter
    }
}

가능한 쉽게 이해되도록 주석도 써놨어요 하나하나 다 필요하고 서로서로 연결돼있는 부분이 많아요


RecyclerViewAdapter.kt

class a1(val context: Context, val b1: ArrayList<Item>): RecyclerView.Adapter<a1.holder1>(){

    //이 부분은 showitem.xml을 activity_main에 있는 리사이클러뷰에 실어주는 역할은 한다.
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): holder1 {
        val aa1 = LayoutInflater.from(context).inflate(R.layout.showitem, parent, false)
        //맨아래 holder1클래스에 aa1 정보를 넘겨준다
        return holder1(aa1)
    }

    //b1 사이즈를 알아내오는 역할을 합니다. 현재로써는 중요하게 알지 안아도 됩니다
    override fun getItemCount(): Int {
        return b1.size
    }

    override fun onBindViewHolder(holder: holder1, position: Int) {
        //b1 ArrayList에 담겨져있는 첫번째 데이터를 cc1에 담는 역할을 합니다
        holder.cc1.text = b1[position].firstItem
        //b1 ArrayList에 담겨져있는 두번째 데이터를 cc2에 담는 역할을 합니다
        //position은 순서를 알려주는 역할을 합니다.
        holder.cc2.text = b1[position].secondItem
    }

    //showitem.xml에 있는 텍스트뷰를 불러와서 cc1,cc2 변수에 담는 역할을 합니다
    class holder1(c1: View): RecyclerView.ViewHolder(c1) {
        val cc1 = c1.findViewById<TextView>(R.id.textView1)
        val cc2 = c1.findViewById<TextView>(R.id.textView2)
    }
}

이 부분도 마찬가지로 주석으로 잘 설명해놨어요

* 직접 설정해야할 변수옆에는 숫자까지 써놨어요 (Ex. 클래스명인 a1)

이게 작동하는 순서를 보여드리자면


첫번째 onCreateViewHolder 함수에서 activity_main의 리사이클뷰에 들어갈 공간을 만든다.

-> 이렇게 만들어진 공간을 holder1에서 받는다
-> holder1에서는 이 공간에 들어갈 showitem.xml의 정보를 불러와서 저장한다(골격을 잡는다)
-
> onBindViewHolder 함수에서 이 공간에  b1의 데이터를 담는다(진짜 데이터를 담는 구간)

 * b1이란??

MainActivity.kt 맨 아래에 adapter 연결을 하면 MainActivity.kt의 list에 있는 데이터가 a1로 넘어올때 저장되는 곳


위의 함수들을 입력하는게 어렵다 하시면

아래와 같이 하세요

여기는 개발자가 직접 설정해줘야하는 부분이에요 일단은 저렇게 '틀'만 잡아주면 되구요

저 상태에서 a1 { } 안에서 컨트롤+O 버튼을 눌러서 

위와 같이 필수 함수들을 추가시켜주시면 돼요.

*a1의 괄호안에 변수 설정할때 val context: Context는 항상 이렇게 쓴다고 기억하시면 돼요


Item.kt

class Item(var firstItem: String? = null,var secondItem: String? = null ) {

    fun firstItem(firstItem: String?){
        this.firstItem = firstItem
    }

    fun secondItem(secondItem: String?){
        this.secondItem = secondItem
    }
}

이 부분은 딱히 설명할 것이 없네요

그냥 리사이클뷰에 표현할 데이터를 세팅하는 부분 이라고 생각하시면 편해요


가능하면 진짜 쉽게 표현하려고 노력해봤는데 어떤가요?

저는 리사이클러뷰를 이해하기 위해 며칠을 만져보고 익히도록 계속 공부했어요

처음에는 잘 이해 안되겠지만 꾸준히 보면서 연습해보는게 가장 좋은 것 같아요

이해안되는 부분은 질문해주세요

반응형

+ Recent posts