공부를 하며 작성하는 포스트입니다. 완벽하진 않지만 최대한 이해하고 작성하려고 합니다. 잘못된 점은 알려주세요.

다시한번 원문을 읽으며 느낀 점은 읽는데 무리없도록 영어도 계속해서 공부해야겠습니다.


Android 사용자는 앱이 플랫폼과 일관된 방식으로 보이고 동작하기를 기대합니다. 개발자는 시각적 패턴 및 탐색 패턴을 위해 머티리얼 디자인 가이드라인을 준수해야 할 뿐만 아니라 호환성, 성능, 보안 등을 위해 품질 가이드라인을 준수해야 합니다.

다음 링크에서는 고품질의 Android 앱을 디자인하는데 필요한 모든 것을 제공합니다.
머티리얼 디자인 가이드라인
앱 품질 가이드라인

출처 : https://developer.android.com/design

1. Introduction

Material Design은 기술과 과학의 혁신으로 좋은 디자인의 고전적인 원칙을 종합한 시각적 언어입니다.

 

Goals : 목표

Create : 기술과 과학의 혁신과 가능성을 바탕으로 우수한 디자인의 고전적 원칙을 합성하는 시각적 언어를 만듭니다.

Unify : 플랫폼, 장치 및 입력 방법에서 사용자 경험을 통합하는 단일 기본 시스템을 개발하세요.

Customize : Material의 시각적 언어를 확장하고 혁신과 브랜드 표현을위한 유연한 기반을 제공하세요.

Principles: 원칙

  • Material is the metaphor
    머티리얼 디자인은 빛을 반사하고 그림자를 드리우는 방법을 포함하여 실제 세계와 텍스처에서 영감을 얻었습니다. 재료 표면은 종이와 잉크의 매체를 재구성합니다.
  • Bold, graphic, intentional
    머티리얼 디자인은 typography, grids, space, scale, color and imagery와 같은 인쇄 디자인 방법에 따라 안내되어 경험에 시청자를 몰입시키는 계층, 의미 및 초점을 만듭니다. 
  • Motion provides meaning
    모션은 미묘한 피드백과 일관된 전환을 통해 주의를 집중시키고 연속성을 유지합니다. 요소가 화면에 표시되면 새로운 변환을 생성하는 상호 작용으로 환경을 변환하고 재구성합니다.
  • Flexible foundation
    머티리얼 디자인 시스템은 브랜드 표현이 가능하도록 설계되었습니다. 구성 요소, 플러그인 및 디자인 요소를 원활하게 구현할 수 있는 사용자 지정 코드베이스와 통합되었습니다.
  • Cross-platform
    머티리얼 디자인은 Android, iOS, Flutter 및 웹에서 공유 구성 요소를 사용하여 플랫폼에서 동일한 UI를 유지 관리합니다.

Getting around : 둘러보기

구글의 포괄적인 지침을 통해 아름다운 제품을 더 빠르게 만들 수 있습니다. 사용자 정의 머티리얼 및 작업 공유를위한 새로운 도구를 사용하여 설계 및 제작하고 머티리얼 스터디에서 영감을 찾고 머티리얼 테마로 제품의 고유한 정체성을 표현하세요. 

 

다음 세 섹션을 탐색하여 필요한 것을 찾으세요.

 

  • Material Systeme
    확장 및 개선 된 설계 시스템은 재료 도구 및 구성 요소와 통합되어 설계와 개발 간의 워크 플로우를 향상시킵니다.
  • Material Foundation
    머티리얼 디자인 아키텍처를 사용하여 앱을 구축하는 방법을 디자인하고 전략을 세우면서 머티리얼 디자인을 뒷받침하는 원칙과 이론을 배우세요.
  • Material Giudelines
    디자인부터 코드까지 제품 전체에 걸쳐 고유한 재료 테마를 체계적으로 사용자 정의하고 구축하세요.

 

이전 머티리얼 가이드라인을 살펴볼 수 있습니다.

Up next

References

'Android' 카테고리의 다른 글

[Kotlin] TextWatcher  (0) 2019.10.30
[Kotlin] 안드로이드 권한 - 1  (0) 2019.09.30
[Kotlin] Room Library  (0) 2019.08.17
[Kotlin] Permission 얻기  (0) 2019.08.12
Jetpack, AndroidX  (0) 2019.07.30

Android 6.0 이상에서는 앱에 권한이 필요함을 선언하려면 해당 권한을 Manifest에 나열하고 런타임에 사용자가 권한을 승인하도록 해야합니다.  Android 지원 라이브러리를 사용하여 권한을 확인하고 요청하는 방법을 알아보겠습니다.

 

Android 프레임워크에서는 Android 6.0(API level 23)과 유사한 메서드를 제공하지만 지원라이브러리르 사용하여 더욱 쉽게 이전 Android 버전과 호환성을 지원할 수 있습니다.

 

manifest에 권한 추가

Android에서는 버전에 상관없이 앱에 권한이 필요함을 선언하려면 manifest에 <uses-permission> 요소를 최상위 <manifest>의 하위요소로 지정합니다.

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

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

    <application
    	...
    </application>

</manifest>

권한 선언 후 해당 권한의 민감한 정도에 따라 시스템 동작은 달라집니다. 어떤 권한은 '정상'권한으로 간주되어 시스템이 설치 시에 곧바로 부여하지만, 어떤 권한은 '위험'권한으로 간주되어 사용자가 명시적으로 엑세스 권한을 부여해야 합니다. 권한에 관한 자세한 정보는 보호수준에서 확인할 수 있습니다.

 

권한 확인

앱에 위험 권한이 필요한 경우 해당 권한이 요구되는 작업을 할 때마다 권한이 있는지 확인해야합니다.

Android 6.0(API level 23)부터는 앱이 더 낮은 API 레벨을 타겟팅하더라도 사용자가 언제든 앱의 권한을 취소할 수 있습니다. 따라서 어제 카메라를 사용했다고 해서 오늘도 권한이 있다고 가정할 수는 없습니다.

 

권한이 있는지 확인하려면 ContextCompat.checkSelfPermission()메서드를 호출합니다.

// 권한 체크하기
if(ContextCompat.checkSelfPermission(this@WelcomeActivity, Manifest.permission.CAMERA) ==
   PackageManager.PERMISSION_GRANTED) {
	// 권한이 있을 때 카메라 실행하기
	Toast.makeText(this, "카메라 허가 가능, 프리뷰 시작", Toast.LENGTH_SHORT).show()
	startCamera()
} else {
	// 권한이 없을 때 권한 요청하기
	requestCameraPermission()
}

앱에 권한이 있는 경우 이 메서드는 PERMISSION_GRANTED를 반환하고, 앱은 작업을 계속 진행할 수 있습니다. 앱에 권한이 없는 경우 이 메서드는 PERMISSION_DENIED를 반환하고, 앱은 사용자에게 명시적으로 권한을 요청해야합니다.

 

권한 요청

앱이 checkSelfPermission()에서 PERMISSION_DENIED를 수신하면 사용자에게 해당 권한을 요청하는 메시지를 표시해야합니다. Android는 requestPermissions()과 같이 권한을 요청하는데 사용할 수 있는 여러 메서드를 제공합니다. 이러한 메서드를 호출하면 표준 Android 대화상자가 나타나며, 이 상자는 맞춤설정할 수 없습니다.

 

사용자에게 표시되는 방식은 기기 Android버전과 애플리케이션의 대상 버전에 따라 다릅니다. 자세한 내용은 권한 개요에 설명됩니다.

 

앱에 권한이 필요한 이유 설명

사용자가 사진앱을 실행하는 경우 사용자에게 카메라 사용 권한을 요청해도 놀라지 않을 것입니다. 그러나 사용자 위치나 연락처에 접근하려고 하면 사용자가 이유를 이해하지 못할 수도 있습니다. 앱에서 권한을 요청하기 전에 먼저 사용자에게 이유를 설명하는 것을 고려해야합니다. 명심할 점은, 설명이 사용자에게 부담이 되어서는 안됩니다. 너무 많은 설명을 제공할 경우 사용자가 짜증을 느끼고 앱을 제거할 수도 있습니다.

 

권한 설명에서 사용할 수 있는 한 가지 방법은 사용자가 해당 권한 요청을 이미 거절한 경우에만 설명을 제공하는 것입니다. Android에서는 이를 위해 유틸리티 메서드 shouldShowRequestPermissionRationable()를 제공합니다. 이 메서드는 사용자가 전에 해당 요청을 거부한 경우에는 true를 반환하고, 사용자가 해당 권한을 거부했으며 '다시 묻지 않음' 옵션을 선택했거나 기기 정책에서 해당 권한을 금지하는 경우에는 false를 반환합니다.

 

사용자가 권한이 요구되는 기능을 계속 사용하려고 시도하면서도 권한 요청을 계속 거절한다면 아마도 이 사용자는 해당 기능을 제공하기 위해 앱에 권한이 필요한 이유를 모를 수도 있습니다. 이런 상황에서는 설명을 하는 것이 좋을 수 있습니다. 권한을 요청할 때 좋은 사용자 환경을 만드는 방법에 관한 권장사항을 확인할 수 있습니다.

 

여러분에게 필요한 권한 요청

앱에 필요한 권한이 아직 없는 경우 앱은 requestPermissions() 메서드 중 하나를 호출하여 적절한 권한을 요청해야 합니다. 앱은 원하는 권한 및 이 권한 요청을 식별하기 위해 지정된 정수 요청 코드를 전달합니다. 이 메서드는 비동기식으로 작동합니다. 즉각적으로 반환되며, 사용자가 메시지에 응답하면 시스템은 그 결과를 가지고 앱의 콜백 메서드를 호출하여 앱이 requestPermissions()에 전달한 것과 동일한 요청 코드를 전달합니다.

 

아래의 예는 카메라 권한을 요청하는 코드입니다.

시나리오

  • 카메라 접근 권한이 있는지 확인

  • 권한이 없으면 권한이 필요한 이유를 표시해야하는지 확인

  • 권한 요청

private fun requestCameraPermission() {

	if (ActivityCompat.shouldShowRequestPermissionRationale(this@WelcomeActivity, Manifest.permission.CAMERA)) {
        Toast.makeText(this@WelcomeActivity, "카메라 권한이 요구됩니다", Toast.LENGTH_SHORT).show()
        ActivityCompat.requestPermissions(this@WelcomeActivity, arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CAMERA)
    } else {
        // 다시 묻지 않음을 누르고 거부하고 요청했을 때
        Toast.makeText(this@WelcomeActivity, "카메라 허가를 받을 수 없습니다.", Toast.LENGTH_SHORT).show()
        ActivityCompat.requestPermissions(this@WelcomeActivity, arrayOf(Manifest.permission.CAMERA), PERMISSION_REQUEST_CAMERA)
    }
}

참고 : requestPermissions()을 호출하면 시스템은 표준 대화상자를 사용자에게 표시합니다. 이 대화상자를 구성하거나 변경할 수 없습니다. 앱에 권한이 필요한 이유에 나오는 것처럼, 사용자에게 정보나 설명을 제공해야하는 겨우 requestPermissions()를 호출하기 전에 제공하는 것이 좋습니다.

권한 요청 응답 처리

사용자가 앱 권한 요청에 응답하면 시스템은 앱의 onRequestPermissionResult() 메서드를 호출하여 사용자 응답을 전달합니다. 권한이 부여되었는지 확인하려면 앱은 해당 메서드를 재정의해야 합니다. 이 콜백에는 requestPermissions()에 전달한 것과 동일한 요청 코드가 전달됩니다.

override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray
) {

    if(requestCode == PERMISSION_REQUEST_CAMERA) {
        if(grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, "카메라의 허가를 받았다", Toast.LENGTH_SHORT).show()
            startCamera()
        }
    } else {
        Toast.makeText(this, "카메라 요청이 거부되었다", Toast.LENGTH_SHORT).show()
    }
}

시스템이 표시하는 대화상자에서는 앱이 액세스해야 하는 권한 그룹을 설명합니다. 특정 권한은 나열하지 않습니다. 

 

예를 들어 READ_CONTACTS 권한을 요청하는 경우 기기의 연락처에 액세스해야 한다는 메시지만 시스템 대화상자에 나타납니다. 사용자는 각 권한 그룹에 관해 한번만 권한을 부여해야 합니다.

 

앱이 해당 그룹에 있는 다른 권한(앱 manifest에 나열된 다른 권한)을 요청하는 경우 시스템이 자동으로 권한을 부여합니다. 여러분이 권한을 요청하면 시스템은 사용자가 시스템 대화상자를 통해 명시적으로 요청을 승인했을 때와 동일한 방식으로 onRequestPermissionsResult() 콜백 메서드를 호출하고 PERMISSION_GRANTED를 전달합니다.

 

참고 : 사용자가 이미 동일한 그룹에 있는 다른 권한을 부여한 경우라도 앱이 필요한 모든 권한을 명시적으로 요청해야 합니다. 향후 Android 리리스에서는 권한 그룹화도 변경될 수 있습니다. 코드에서는 특정 권한이 동일한 그룹에 있다고 가정하거나 없다고 가정해서는 안됩니다.

 

예를 들어 manifest에 READ_CONTACTS 및 WRITE_CONTACTS를 둘 다 나열한다고 가정합니다. READ_CONTACTS를 요청하여 사용자가 권한을 부여한 후 WRITE_CONTACTS를 요청하면 시스템이 사용자와 상호작용 없이 곧바로 해당 권한을 부여합니다.

 

사용자가 권한 요청을 거부하는 경우 앱은 적절한 작업을 수행해야 합니다. 예를 들어 해당 권한이 필요한 작업을 사용자가 요청하면, 수행할 수 없는 이유를 설명하는 대화상자를 표시할 수 있습니다.

 

시스템에서 사용자에게 권한을 부여하도록 요청하면 사용자는 해당 권한을 다시 요청하지 말도록 시스템에 지시할 수 있습니다. 이 경우 앱이 해당 권한을 다시 요청하기 위해 requestPermissions()를 사용할 때 마다 시스템은 즉시 해당 요청을 거부합니다. 시스템은 사용자가 명시적으로 요청을 다시 거부했을 때와 동일한 방식으로 onRequestPermissionResult() 콜백 메서드를 호출하고 PERMISSION_DENIED를 전달합니다.

 

앱이 해당 권한을 가지지 못하도록 기기 정책에서 금지하는 경우에도 이 메서드는 false를 반환합니다. 즉, requestPermissions()를 호출하는 경우 사용자와의 직접적 상호작용이 발생했다고 가정할 수 없습니다.

 

Reference

 

Android Runtime PermissionBasicSample을 참고하여 만들었습니다. 

package com.hyun.sqlite

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.util.Log
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import kotlinx.android.synthetic.main.activity_welcome.*

const val PERMISSION_REQUEST_CAMERA = 0

class WelcomeActivity : AppCompatActivity() {

    val TAG: String = ".WelcomeActivityAKDJKA"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_welcome)

        btn_camera.setOnClickListener {
            showCameraPreview()
        }
    }

    override fun onRequestPermissionsResult(
        requestCode: Int, permissions: Array<out String>, grantResults: IntArray
    ) {
        Log.d(TAG, "onRequestPermissionsResult")

        if (requestCode == PERMISSION_REQUEST_CAMERA) {
            if (grantResults.size == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "카메라의 허가를 받았다. 프리뷰 시작", Toast.LENGTH_SHORT).show()
                startCamera()
            }
        } else {
            Toast.makeText(this, "카메라 요청이 거부되었다", Toast.LENGTH_SHORT).show()
        }
    }

    // 1
    fun showCameraPreview() {
        Log.d(TAG, "showCameraPreview")

        // 권한 체크하기
        if (ContextCompat.checkSelfPermission(this@WelcomeActivity, Manifest.permission.CAMERA) ==
            PackageManager.PERMISSION_GRANTED
        ) {
            // 권한이 있을 때 카메라 실행하기
            Toast.makeText(this, "카메라 허가 가능, 프리뷰 시작", Toast.LENGTH_SHORT).show()
            startCamera()
        } else {
            // 권한이 없을 때 권한 요청하기
            requestCameraPermission()
        }
    }


    private fun requestCameraPermission() {
        Log.d(TAG, "requestCameraPermission()")

        if (ActivityCompat.shouldShowRequestPermissionRationale(
                this@WelcomeActivity,
                Manifest.permission.CAMERA
            )
        ) {
            Toast.makeText(this@WelcomeActivity, "카메라 권한이 요구됩니다", Toast.LENGTH_SHORT).show()
            ActivityCompat.requestPermissions(
                this@WelcomeActivity,
                arrayOf(Manifest.permission.CAMERA),
                PERMISSION_REQUEST_CAMERA
            )
        } else {
            // 다시 묻지 않음을 누르고 거부하고 요청했을 때
            Toast.makeText(this@WelcomeActivity, "카메라 허가를 받을 수 없습니다.", Toast.LENGTH_SHORT).show()
            ActivityCompat.requestPermissions(
                this@WelcomeActivity,
                arrayOf(Manifest.permission.CAMERA),
                PERMISSION_REQUEST_CAMERA
            )
        }
    }

    private fun startCamera() {
        Log.d(TAG, "startCamera()")
        Intent(this@WelcomeActivity, CameraPreviewActivity::class.java).let {
            startActivity(it)
        }
    }
}

'Android' 카테고리의 다른 글

[Kotlin] 안드로이드 권한 - 1  (0) 2019.09.30
[Design] Material Design  (0) 2019.08.18
[Kotlin] Room Library  (0) 2019.08.17
Jetpack, AndroidX  (0) 2019.07.30
Singleton, MVC, MVP, MVVM 한 눈에 보기  (0) 2019.07.18

+ Recent posts