안녕하세요.

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

그냥 간단히 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
    }
}

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

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


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

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

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

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

반응형

안드로이드 스튜디오를 설치하고 공부하려는 도중에

예전에는 못보던 오류가 생겼어요.

무언가 다운로드해서 동기화해야 하는데 그러지 못하는 것이에요.

실패한 이유를 보니

New Gradle Sync is not supported due to containing Kotlin modules

라고 되있었어요!

해결방법은 JVM 버전을 올려주는 것으로 해결!

File->Settings-> KotlinCompiler

여기서 Targer JVM version 을 1.8로 설정해주시면 해결이 돼요.

다시 재밌게 안드로이드를 하러~!

반응형

+ Recent posts