- 권한
- 바인딩
2.1. dataBinding
2.2. viewBinding - 뷰모델
3.1 ViewModel에서 Context 필요할 때
3.2 LiveData
3.3 Observer
*https://jslee-tech.tistory.com/45 - 앱 재시작 코드
- 스피너
5.1 정적 스피너
5.2 동적 스피너 - 웹뷰
private val permissionRequestCode = 999
//필요 권한 리스트
private val permissionsArray: Array<String> = arrayOf(
Manifest.permission.READ_CONTACTS
)
//권한 요청이 필요한 Activity/Fragment 에서 호출
fun checkPermissions() {
//API 23 이상
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
val isAllPermissionGranted: Boolean = permissionsArray.all { permission ->
context.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED
}
if (isAllPermissionGranted) {
permissionGranted()
} else {
ActivityCompat.requestPermissions(
context as Activity,
permissionsArray,
permissionRequestCode
)
}
} else {
//API 23 미만
}
}
//override
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (grantResults.all { it == PackageManager.PERMISSION_GRANTED}) {
//권한을 모두 승인 받았을 때
permission.permissionGranted()
}
else {
//권한 승인이 하나라도 거절되었을 때
permission.permissionDenied()
}
}
//메서드 내부에서 권한 체크 시 사용
if (PermissionChecker.checkSelfPermission(context, Manifest.permission.CALL_PHONE)
== PermissionChecker.PERMISSION_GRANTED
) {
//권한 승인 시
} else {
//권한 미승인 시
}
2.1.dataBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
}
}
//XML
//editText.text XML에서 바로 String 파라미터로 사용
android:onClick="@{()->callViewModel.makeCall(editText.getText().toString())}"
class EcdhFragment : Fragment() {
private var _binding: FragmentEcdhBinding? = null
private val binding get() = _binding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
_binding = DataBindingUtil.inflate(inflater, R.layout.fragment_ecdh, container, false)
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
2.2.viewBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
3.1 ViewModel에서 Context 필요할 때
class MyViewModel(application: Application) : AndroidViewModel(application) {
private val context = getApplication<Application>().applicationContext
}
3.2 LiveData
private var _isUserInfoFetching = MutableLiveData<Boolean>()
val isUserInfoFetching: LiveData<Boolean>
get() = _isUserInfoFetching
3.3 observer
userInfoViewModel.isUserInfoFetching.observe(this){ isFetching ->
}
//재시작
val packageManager: PackageManager = packageManager
val intent: Intent = packageManager.getLaunchIntentForPackage(packageName)!!
val componentName: ComponentName? = intent.component
val mainIntent = Intent.makeRestartActivityTask(componentName)
startActivity(mainIntent)
exitProcess(0)
//종료
requireActivity().finishAffinity()
exitProcess(0)
5.1 정적 스피너
val accountTypeList = resources.getStringArray(R.array.account_purpose)
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, accountTypeList)
binding.fragMainToolbarSpinner.adapter = adapter
5.2 동적 스피너
val useType = arrayListOf("a", "b", "c")
val adapter = ArrayAdapter(requireContext(), android.R.layout.simple_list_item_1, useType)
adapter.setDropDownViewResource(androidx.transition.R.layout.support_simple_spinner_dropdown_item)
binding.fragMainToolbarSpinner.adapter = adapter
<WebView
android:id="@+id/webview"
...
//cf.[Android] WebView 설정 모아보기 https://kyome.tistory.com/149
val data = remoteData.jsData //ex. <p>공지!!!!테스트</p><p><br></p><p><img src=\ .....<br></p><p><br></p>
binding.webview.settings.settings.apply {
useWideViewPort = true //페이지에 뷰포트 메타가 있으면 태그에 지정된 너비 값이 사용된다.
loadWithOverviewMode = true //컨텐츠가 웹뷰보다 클때 스크린 크기에 맞춘다
javaScriptEnabled = true //자바스크립트 사용 여부
}
binding.webview.loadDataWithBaseURL(null, data!!, "text/html; charset=utf-8", "UTF-8", null)
BaseFragment
https://github.com/HYUNJUNEPARK/-Ref-AndroidUI/blob/main/3_ViewPager2_BottomNavigation/app/src/main/java/com/example/viewpager2_bottomnavigation/util/BaseFragment.kt
-> 원하는 프래그먼트에 상속시킨 후 initView() 를 오버라이딩해 사용 (dataBinding 사용)
//상속 예시
class AFragment : BaseFragment<FragmentABinding>(R.layout.fragment_a) {
override fun initView() {
super.initView()
binding.apply {
}
}
}
NetworkConnection
https://github.com/HYUNJUNEPARK/-Ref-AndoridProgramming/blob/main/8_NetworkConnection/app/src/main/java/com/example/networkstate/NetworkConnectionCheckModule.kt
-> Activity onCreate/onDestroy 에 register()/unregister() 해 사용 (생명주기에 맞게 호출해 사용)
class MainActivity : AppCompatActivity() {
private val networkCheck: NetworkConnectionCallback by lazy { NetworkConnectionCallback(this) }
override fun onCreate(savedInstanceState: Bundle?) {
//...
networkCheck.register()
}
override fun onDestroy() {
//...
networkCheck.unregister()
}
}
AlertDialog
-경고창, 팝업창 구현 시 사용
AlertDialog.Builder(context)
.setTitle("권한 설정")
.setMessage("권한 거절로 인해 일부기능이 제한됩니다.")
.setPositiveButton("취소") { _, _ -> }
.setNegativeButton("권한 설정하러 가기") { _, _ ->
applicationInfo()
}
.create()
.show()
Thread
fun showThreadName() {
val threadName = Thread.currentThread().name
println("Running on thread: $threadName")
}
DataCass -> JSON -> String
Gson().toJson(
ErrorResponse(
ExecStatus(
code = response.code().toString(),
message = message
)
)
)
JSONObject에서 원하는 데이터 추출
fun extractResponseCode(preferencesResponse: String): String {
return try {
JSONObject(preferencesResponse).getJSONObject(EXECSTATUS).getString(CODE)
} catch (e: Exception) {
e.printStackTrace()
handleExceptionResponse(e.toString())
}
}
build.gradle(.app) buildType 세팅 -> Sync Project with Gradle Files
buildTypes {
debug {
buildConfigField "String", "BASE_URL", "\"http://1-.2-.3-.7-:5---/\""
buildConfigField "Boolean", "DEBUG_MODE", "true"
}
release {
//...
buildConfigField "String", "BASE_URL", "\"http://2--.7-.2--.7-:5---/\""
buildConfigField "Boolean", "DEBUG_MODE", "false"
}
}
invoke()
-'opertor'와 함께 invoke() 함수를 정의하여 클래스 인스턴스를 함수처럼 호출할 수 있다.
//invoke 가 사용된 use case 예시
class FormatDateUseCase(userRepository: UserRepository) {
private val formatter = SimpleDateFormat(
userRepository.getPreferredDateFormat(),
userRepository.getPreferredLocale()
)
operator fun invoke(date: Date): Stirng {
return formatter.format(date)
}
}
//use case 호출 예시
class MyViewModel(formatDataUseCase: FormatDateUseCase): ViewModel() {
init {
val today = Calendar.getInstance()
val todayDate = formateDateUserCase(today)
}
//...
}
AAR
Library AAR 파일 생성
https://cording-cossk3.tistory.com/156
프로젝트 구조 대화상자를 사용하여 종속 항목 추가
https://developer.android.com/studio/projects/android-library?hl=ko#psd-add-library-dependency
cf).aar 파일을 Project/app/libs 에 복사
-> File > Project Structure > Dependencies > Add Jar/Aar Dependency > Step 1 Provide a path to the library file or directory to add 'libs/aar파일명'
BuildConfig.DEBUG가 릴리즈 모드에서도 true가 되는 현상
https://satisfactoryplace.tistory.com/147
파이어베이스 gradle 세팅 방법(gradle 7.x 이상)
https://gift123.tistory.com/68
https://developer.android.com/studio/releases/gradle-plugin?hl=ko#updating-plugin
프래그먼트 화면 전환 시 상태 유지하기 (FragmentManager)
https://hanyeop.tistory.com/425
Android) Fragment 에서 View Binding 문제점, 제대로 사용하기
https://yoon-dailylife.tistory.com/57
AOS 외부 앱 실행
https://www.fun25.co.kr/blog/android-execute-3rdparty-app/?category=003
Android - PackageManager로 Package 정보 가져오기
https://codechacha.com/ko/android-query-package-info/
파이어베이스 그레들리 셋팅 방법(buildscript 설명 참고)
https://gift123.tistory.com/68
Kotlin Enum Class Ex
https://blog.logrocket.com/kotlin-enum-classes-complete-guide/
앱 재시작 코드
https://stackoverflow.com/questions/6609414/how-do-i-programmatically-restart-an-android-app
Cannot resolve symbol 'v7' 오류 해결(오래된 프로젝트 빌드 에러)
https://velog.io/@wonggamggik/Cannot-resolve-symbol-v7-%EC%98%A4%EB%A5%98-%ED%95%B4%EA%B2%B0