Android Fragment에 지도(map)가 그려지지 않는 현상 해결 방법 문의

  • SDK 버전 : Android 길찾기 UI SDK 버전 1.5.6 / 샘플 버전 1.6.8
  • 디벨로퍼스 앱 ID : 977154

안녕하세요. Android SDK를 이용해서 Android Fragment에 내비게이션을 개발하고자 합니다.
샘플 소스 코드를 보고 개발 중으로

  1. KNSDK.apply … 코드 적용했고, 에러 없이 동작하고 있습니다.
  2. initializeWithAppKey 코드 적용했고, 에러 없이 이상 없이 동작하고 있습니다.
  3. initializeWithAppKey 코드에 KNSDK.bindingMapView 코드 추가하여 에러 없이 동작하는 것을 확인하였습니다.

위 3개는 디버그로도 확인했고, Logcat에도 별다를 에러가 발생하지 않습니다.
그런데, 위 초기화 3개를 모두 적용하여도 Fragment에 지도(map)가 그려지지 않습니다.

샘플앱을 확인해보니 지도(map)가 비동기 방식으로 그려지고 있는 것 같아 보이는데, 해당 코드를 찾을 수 없어서 문의 드립니다. (어떤 코드를 추가해야 하는 것인지? 아니면 gradle에 추가해야 하는 내용이 있는지 문의 드립니다.)

감사합니다.

안녕하세요. 카카오모빌리티의 기술 제휴 담당자입니다.

SDK가 샘플 버전보다 하위이기 때문에 정상 작동하지 않을 수 있습니다. 최신 버전으로 사용 부탁드립니다.

문의주신 내용으로 보아, NavigationView가 아닌 MapView를 Fragment에 생성을 하시려는 것으로 보입니다.
지도가 그려지는 방식은 KNSDK의 내부에서 동작되며 난독화되어 있기 떄문에 해당 코드는 확인하실 수 없습니다.

현재 질문자님께서 구현하신 코드를 확인할 수가 없기에, KNMapView 추가하는 샘플코드를 추가하오니 참고 부탁드립니다.

test_map_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android" >

  <com.kakaomobility.knsdk.map.knmapview.KNMapView
    android:id="@+id/mapView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

</layout>

testFragment class

class testFragment : Fragment() {
  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    val binding = TestMapLayoutBinding.inflate(inflater)
    initMapView(binding.mapView)
    return binding.root
  }

  private fun initMapView(mapView: KNMapView) {
    KNSDK.bindingMapView(mapView, mapView.mapTheme) { error ->
      if (error == null) {
        //현 위치가 나타나도록 설정
        val pos = FloatPoint([현위치 좌표(Katech)])
        mapView.moveCamera(KNMapCameraUpdate.targetTo(pos).zoomTo(2.5f), false)
      }
    }
  }
}

MainActivity.kt

class MainActivity : AppCompatActivity() {

  lateinit var knsdk: KNSDK

  @SuppressLint("MissingInflatedId")
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    sdkInit()
  }

  private fun sdkInit() {
    knsdk = KNSDK.apply {
      install(application, "$filesDir/KNSample")
      delegate = object: KNSDKDelegate {
        override fun knsdkFoundUnfinishedTrip(aTrip: KNTrip, aPriority: KNRoutePriority, aAvoidOptions: Int) {}
        override fun knsdkNeedsLocationAuthorization() {}
      }
    }

    knsdk.initializeWithAppKey("APP_KEY", "VERSION", "USER_KEY", KNLanguageType.KNLanguageType_KOREAN, aCompletion = {
      if (it == null) {
        //mapView add
        supportFragmentManager.beginTransaction().apply {
          add(R.id.test_frame_layout, testFragment())
          commit()
        }
      }
    })
  }
}

activity_main.xml

<?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">

  <FrameLayout
    android:id="@+id/test_frame_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

  </FrameLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

이 밖에 추가 문의사항이 있으시면 언제든지 문의 주세요.
감사합니다.

1개의 좋아요

안녕하세요. 우선 답변 감사드립니다.

현재 구현하고자 하는 앱에서 한 화면(MainActivity)에 3개의 Fragment를 이용하여 각각의 Fragment에서 정보를 처리할 수 있도록 구현할 예정입니다.

답변 주신 내용으로 적용해 본 결과 지도가 그려지는 것은 확인하였습니다.

3개의 Fragment를 사용하려고 하고 있기 때문에 상기 답변의 MainActivity에 있는 sdkInit 함수의 코드를 카카오맵 Fragment 내부에서 처리하고자 하였습니다.

sdkInit 함수를 Fragment에 적용하는 경우, 아래와 같은 에러가 발생합니다.

E Exception configuring surface
java.lang.NullPointerException: Attempt to invoke virtual method ‘void android.opengl.GLSurfaceView$GLThread.surfaceCreated()’ on a null object reference
at android.opengl.GLSurfaceView.surfaceCreated(GLSurfaceView.java:533)
at android.view.SurfaceView.updateSurface(SurfaceView.java:1174)
at android.view.SurfaceView.lambda$new$0$SurfaceView(SurfaceView.java:181)
at android.view.-$$Lambda$SurfaceView$w68OV7dB_zKVNsA-r0IrAUtyWas.onPreDraw(Unknown Source:2)
at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:1093)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:3197)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:2020)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:8410)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:974)
at android.view.Choreographer.doCallbacks(Choreographer.java:797)
at android.view.Choreographer.doFrame(Choreographer.java:732)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:959)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7717)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:612)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:997)

sdkint에 해당하는 코드를 Fragment 내에서 사용할 수 있는 방법은 없는 것인지 문의 드립니다. (상기 에러가 나면 지도가 그려지지 않는 것 같습니다.)

감사합니다.

안녕하세요
이전에 답변드린 코드를 기준으로 다시 한번 답변드립니다

KNMapView를 binding 하기전에 (testFragment 생성을 하기전에)
KNSDK의 install 및 initializeWithAppKey 함수를 통하여 결과가 먼저 들어오는지 확인 부탁드립니다

해당 함수를 통하여 인증이 모두 완료(KNError == null)되고,
testFragment를 생성(KNMapView binding)하신 후,
FragmentManager를 통하여 add를 해주시면 될 것으로 보입니다

1개의 좋아요

안녕하세요. 답변 주셔서 감사합니다.

KNSDK의 install 및 initializeWithAppKey 함수 결과를 확인 후 Fragment를 add 해야 한다는 말씀은

Fragment를 add하고 나서 KNSDK의 install 및 initializeWithAppKey 함수를 호출하면 지도가 그려지지 않는 것으로 이해해도 될까요?

Fragment 내에서 KNSDK의 install 및 initializeWithAppKey 함수를 호출하고 나서 initializeWithAppKey 가 성공일 때 KNMapView를 binding 및 requestRender를 호출 하도록 처리해 보았으나, 상기 문의 드렸던 Exception이 발생합니다.

Fragment의 onAttach, onCreate, onCreateViewe, onViewCreate 등에서 KNSDK의 install 및 initializeWithAppKey를 호출해도 동일한 Exception이 발생하고 있습니다. (디버그 결과 함수 호출과는 별개로 비동기로 처리되고 있는 것 같아 보입니다.)

추가적으로, opengl surface가 null인 것으로 보여, surface를 추가로 만들어 setRender를 호출해 보았으나, bining 할 때 setRender가 두 번 호출되어 올바르게 생성되지 않습니다.

원래 의도는 상기 문의사항에서 말씀 드린 바와 같이 3개의 Fragment에서 서로 다른 정보를 표현하기 위해 각각의 Fragment 내에서 관련 로직을 수행하는 것이 었으나, Kakao Map을 위한 Fragment 내부에서 처리하기 어렵다면

답변 주신 내용 처럼 Activity에서 KNSDK의 install 및 initializeWithAppKey 함수를 호출하고, Fragment를 add하는 방향으로 진행해야 할 것 같습니다.

번거러우시겠지만 만약, Fragment를 add 한 후에 Fragment에서 KNSDK의 intall 및 initializeWithAppKey 함수를 호출하여도 지도가 표시 될 수 있는 가능성이 있는지 다시 한 번 확인 요청 드려봅니다.

감사합니다.

안녕하세요,

문의하신 내용으로 진행하려면 이전에 답변드린 코드를 기준으로
test_map_layout.xml에서 KNMapView가 제거되고, 동적으로 추가가 되어야 합니다.

또, Fragment의 add 이후에 인증 및 초기화, KNMapView의 노출이 될 수 있는 샘플 코드를 추가하오니, 참고 부탁드립니다.
(기존에 답변드렸던 코드 기준입니다)

test_map_layout.xml - 변경

<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

</androidx.constraintlayout.widget.ConstraintLayout>

testFragment class - 변경

class testFragment : Fragment() {
  private var binding: TestMapLayoutBinding? = null

  override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
    binding = TestMapLayoutBinding.inflate(inflater)
    
	createMapView()

    return binding.root
  }

  private fun createMapView() {
    KNSDK.install 호출
    KNSDK.initializeWithAppKey 호출 {
      if (KNError == null) { //KNSDK 초기화 완료 이후
        Handler(Looper.getMainLooper()).post { 
          //KNMapView를 동적으로 생성합니다
          var mapView = KNMapView(requireContext())
          (binding?.root as ViewGroup).addView(mapView)

          initMapView(mapView)
        }
      } 
    }
  }

  private fun initMapView(mapView: KNMapView) {
    KNSDK.bindingMapView(mapView, mapView.mapTheme) { error ->
      if (error == null) {
        //현 위치가 나타나도록 설정
        val pos = FloatPoint([현위치 좌표(Katech)])
        mapView.moveCamera(KNMapCameraUpdate.targetTo(pos).zoomTo(2.5f), false)
      }
    }
  }
}

MainActivity.kt - 변경

class MainActivity : AppCompatActivity() {

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main) 

    supportFragmentManager.beginTransaction().apply {
      add(R.id.test_frame_layout, testFragment())
      commit()
    }
  }
}

감사합니다.

1개의 좋아요

안녕하세요.

상기 답변으로 적용해 본 결과, Frament 내부에서 지도가 올바르게 표시 되는 것을 확인하였습니다.

비록 KNMapView가 동적으로 생성되어 Fragment 전체를 표시하여 다른 컨트롤들이 가려지지만

이건 제가 해결해야 할 이슈로 보입니다.

장시간 해결 못한 문제였고, 아직 안드로이드 개발에 역량이 부족했던 부분이 있어 문의 드렸지만,

친절한 답변 진심으로 감사드립니다.

행복하세요~

1개의 좋아요