Petnow LogoPetnow
Android SDKUI 모듈

UI 커스터마이징

Client Custom UI를 카메라 화면에 커스터마이징하는 방법.


이 가이드를 시작하기 전에 기본 사용법을 완료하세요.

개요

PetnowCameraFragment는 카메라 UI를 클라이언트 요구사항에 맞게 커스터마이징 할 수 있습니다:

  1. 커스텀 UI 추가 - Client에서 Camera 화면에 커스터마이징 방법
  2. 트래커 위 가이드 UI 배치 - Client에서 FloatingGuide UI를 화면에 커스터마이징 방법

커스텀 UI 추가

커스텀 UI 레이아웃을 생성하고 모듈에 주입하여 카메라 화면 UI를 커스터마이징합니다.

class ClientCameraFragment : PetnowCameraFragment(), PetnowCameraDetectionListener {
    private var _binding: FragmentClientCameraBinding? = null
    private val binding get() = _binding!!

    override fun provideCustomOverlayLayout(): Int = R.layout.fragment_client_camera 

    override fun onAttach(context: Context) {
        super.onAttach(context)
        setPetnowCameraDetectionListener(this)
    }

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

    override fun onAddedCustomLayout(view: View) { 
        super.onAddedCustomLayout(view)            
        _binding = FragmentClientCameraBinding.bind(view) // 해당 함수에서 binding 객체 얻어야 합니다.

        binding.statusText.text = "Camera is ready"
    } 

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    override fun onDetectionStatus(primaryDetectionStatus: PetnowDetectionStatus) {
        when (primaryDetectionStatus) {
            PetnowDetectionStatus.Detected -> { 
                // Success status
                binding.statusText.text = "Nose print scans are coming through!"
                binding.statusText.setTextColor(Color.GREEN) 
            } 

            // Distance issues 
            PetnowDetectionStatus.TooClose -> { 
                binding.statusText.text = "It's too close! Stay away from your pet's face about a handspan."
                binding.statusText.setTextColor(Color.RED) 
            } 
            PetnowDetectionStatus.TooFarAway -> { 
                binding.statusText.text = "It's too far! Stay away from your pet's face about a handspan."
                binding.statusText.setTextColor(Color.RED) 
            } 

            // Lighting issues
            PetnowDetectionStatus.TooBright -> { 
                binding.statusText.text = "It's too bright! Try in a place with less light."
                binding.statusText.setTextColor(Color.RED) 
            } 
            PetnowDetectionStatus.TooDark -> { 
                binding.statusText.text = "It's too dark! Try in a place with more light."
                binding.statusText.setTextColor(Color.RED) 
            } 
            PetnowDetectionStatus.ShadowDetected -> { 
                binding.statusText.text = "Adjust the lighting to avoid shadows on the nose."
                binding.statusText.setTextColor(Color.RED) 
            } 
            PetnowDetectionStatus.GlareDetected -> { 
                binding.statusText.text = "Too shiny! Wipe the nose or adjust the lighting."
                binding.statusText.setTextColor(Color.RED) 
            } 

            // Clarity issues
            PetnowDetectionStatus.TooBlurred -> { 
                binding.statusText.text = "Move back and forth slowly to focus!"
                binding.statusText.setTextColor(Color.RED) 
            } 
            PetnowDetectionStatus.MotionBlurDetected -> { 
                binding.statusText.text = "Too shaky! Be aware of your moving hand and dog."
                binding.statusText.setTextColor(Color.RED) 
            } 
            PetnowDetectionStatus.DefocusedBlurDetected -> { 
                binding.statusText.text = "Move back and forth slowly to focus!"
                binding.statusText.setTextColor(Color.RED) 
            } 

            // Angle issues
            PetnowDetectionStatus.NotFrontFace -> { 
                binding.statusText.text = "Point the camera at the front of the face - only the front works."
                binding.statusText.setTextColor(Color.RED) 
            }
            PetnowDetectionStatus.NotFrontNose -> { 
                binding.statusText.text = "Point the camera at the front of the face - only the front works."
                binding.statusText.setTextColor(Color.RED) 
            }
            PetnowDetectionStatus.NotFrontCatFaceHor, 
            PetnowDetectionStatus.NotFrontCatFaceTop, 
            PetnowDetectionStatus.NotFrontCatFaceBottom -> { 
                binding.statusText.text = "Align the camera with the cat's face."
                binding.statusText.setTextColor(Color.RED) 
            } 

            // Obstruction issues
            PetnowDetectionStatus.NoseNotFound -> { 
                binding.statusText.text = "Be careful not to cover or sway the nose."
                binding.statusText.setTextColor(Color.RED) 
            } 
            PetnowDetectionStatus.FurDetected -> { 
                binding.statusText.text = "Make sure fur isn't covering the nose/face."
                binding.statusText.setTextColor(Color.RED) 
            } 
            PetnowDetectionStatus.FakeDetected -> { 
                binding.statusText.text = "Fake Detected!!"
                binding.statusText.setTextColor(Color.RED) 
            } 

            // Default case
            else -> { 
                binding.statusText.text = "Align your pet's face in the frame"
                binding.statusText.setTextColor(Color.WHITE) 
            } 
        }
    }

    override fun onDetectionProgress(progress: Int) {
        binding.detectionProgress.progress = progress 
        if (progress == 100) { 
            binding.statusText.text = "Detection Finished"
        } 
    }

    override fun onDetectionFinished(result: DetectionCaptureResult) {
        when (result) {
            is DetectionCaptureResult.Success -> {
                val (noseImages, faceImages) = result
            }
            is DetectionCaptureResult.Fail -> {
            
            }
        }
}

provideCustomOverlayLayout() :

XML 커스텀 한 Layout을 만들어서 UI 모듈에 해당 함수로 주입


onAddedCustomLayout(view: View) :

클라이언트에서 생성한 커스텀 뷰가 Inflate가 완료된 후 해당 함수 호출

클라이언트에서 생성한 UI 조작 시 해당 함수에서 inflated 된 view를 받은 이후에 조작하면 됩니다.

커스텀 UI 실행 화면

위 코드처럼 프래그먼트를 구성해 실행하면 카메라 화면에는 기본 Tracking UI와
클라이언트에서 생성한 UI가 렌더링됩니다.

UI Module Setup2

트래커 위 가이드 UI 배치

가이드 UI를 생성하고 모듈에 주입하여
TrackingUI 위에 떠다니는(같이 움직이는) 가이드 UI를 커스터마이징합니다.

  • TrackingUI에 맞춰 UI 모듈에서 좌표 설정 및 Rendering을 자동
  • 앱에서는 어떤 UI를 그릴 것인지 명시
  • 해당 메소드들은 Optional

class ClientCameraFragment : PetnowCameraFragment(), ... {
  private var _floatingBinding: LayoutFloatingGuideBinding? = null
  
  override fun provideFloatingGuideLayout(): Int = R.layout.layout_floating_guide 

  override fun onFloatingGuideAdded(view: View) { 
    _floatingBinding = LayoutFloatingGuideBinding.bind(view) 
    floatingBinding.layoutCameraGuideFloating.isVisible = false // infalted 즉시 화면에 노출됨으로 false 설정
    _floatingBinding.textCameraGuideFloatingMessage.text = "플로팅 가이드 텍스트!"
  } 
  ...
 
  override fun onDetectionProgress(progress: Int) {
        ...
        if (progress > 0) {
            ...
            floatingBinding.layoutCameraGuideFloating.isVisible = true
        }
    }

  override fun onDetectionFinished(result: DetectionCaptureResult) {
        when (result) {
            is DetectionCaptureResult.Success -> {
                val (noseImages, faceImages) = result
                ...
            }
            is DetectionCaptureResult.Fail -> {
                ...
            }
        }
        floatingBinding.layoutCameraGuideFloating.isVisible = false
    }
}

provideFloatingGuideLayout() :

XML 커스텀 한 FloatingGuideLayout을 만들어서 UI 모듈에 해당 함수로 주입


FloatingGuideAdded(view: View) :

클라이언트에서 생성한 FloatingGuide 커스텀 뷰가 Inflate가 완료된 후 해당 메소드 호출

클라이언트에서 생성한 UI 조작 시 해당 함수에서 inflated 된 view를 받은 이후에 조작하면 됩니다.

커스텀 Floating Guide UI 실행 화면

위 코드처럼 프래그먼트를 구성해 실행하면 카메라 화면에는 기본 Tracking UI위에
클라이언트에서 생성한 FloatingGuide UI가 렌더링됩니다.

UI Module Setup2 - overlayUI Module Setup2 - tracker

다음 단계

UI 커스터마이징 방법을 익혔다면 다음 문서를 참고하세요:

On this page