file.delete()를 사용했을 때 내가 의도한대로 동작하지 않는 경우가 있었다. file.delete()가 실패해서 false를 반환하는 원인은 여러가지가 있는데 몰랐던 사실을 알게 되어 원인 및 해결 방법을 간략하게 정리해보려고 합니다. 1. ...

file.delete()를 사용했을 때 내가 의도한대로 동작하지 않는 경우가 있었다.

file.delete()가 실패해서 false를 반환하는 원인은 여러가지가 있는데

몰랐던 사실을 알게 되어 원인 및 해결 방법을 간략하게 정리해보려고 합니다.








1. File.delete() Return False 원인

File.delete 메서드가 실패한(Return False) 원인에 대한 정보는 제공해 주지 않습니다.

삭제가 실패한 이유는 사용자가 직접 찾아야 합니다.

일반적으로 크게 아래 4가지가 원인이 될 수 있다고 생각하면 좋을 것 같습니다.


 - 권한 문제 (WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE)

 - 비어 있지 않은 디렉터리인 경우

 - 해당 파일이 실행되어 있는 경우

 - 해당 파일이 존재하지 않는 경우



저 같은 경우 비어 있지 않은 디렉터리를 삭제 시도했을 때 실패하는 상황이였어요.

(이번에 알게 된 사실...ㅠㅠ)

끝으로 비어있지 않은 디렉터리인 경우 삭제하는 방법을 알아보고 마무리 하겠습니다.








2. 디렉터리 삭제

비어있지 않은 디렉터리를 삭제하는 예시 입니다.

파일이 디렉터리인 경우 하위 파일들을 모두 비워주고 삭제하는 deleteFile() 메서드를 

작성해서 테스트해봤는데 잘 동작하네요 ㅎㅎ

val path = "/sdcard/Download/TEST" // 비어있지 않은 디렉터리
val file = File(path)
if (file.exists()) {
//file.delete() // return false
deleteFile(file) // return true
}


private fun deleteFile(file: File): Boolean {
if (file.isDirectory) {
val files = file.listFiles()
if (files != null) {
for (f in files) {
deleteFile(f)
}
}
}
return file.delete()
}







[Reference]

StackOverFlow





안드로이드 스튜디오 설치 페이지를 보면 최신 버전으로 다운로드가 제공되는데요. 이전 버전을 설치는 어떻게 해야하는지 간략하게 알아보도록 합시다! 1. 안드로이드 스튜디오 이전 버전 설치 안드로이드 스튜디오 설치 파일은 출시된 버전마다 각각의 다운로드 ...

안드로이드 스튜디오 설치 페이지를 보면 최신 버전으로 다운로드가 제공되는데요.

이전 버전을 설치는 어떻게 해야하는지 간략하게 알아보도록 합시다!








1. 안드로이드 스튜디오 이전 버전 설치

안드로이드 스튜디오 설치 파일은 출시된 버전마다 각각의 다운로드 링크 주소를 가집니다.

설치하고자 하는 버전을 확인하고 링크 주소를 웹 주소 창에 입력해주면

설치 파일을 쉽게 다운 받을 수 있습니다.



버전에 따른 다운로드 링크 주소는 아래와 같습니다.

https://redirector.gvt1.com/edgedl/android/studio/install/버전/android-studio-버전-windows.exe



설치하고자 하는 버전을 확인하려면 아래 링크를 참조해주세요!

Android Studio Releases List






정리는 끝났고....

직접 한번 Giraffe - Patch 4 버전을 설치해보겠습니다.

먼저 설치하고자 하는 버전 확인 : 2023.3.1.22  OK...



그리고 아래 링크 주소를 웹 주소 창에 입력해서 설치 파일 다운로드 받았습니다.

https://redirector.gvt1.com/edgedl/android/studio/install/2022.3.1.22/android-studio-2022.3.1.22-windows.exe



아래 설치 파일이 다운이 되고 실행해서 설치까지 해줬더니 해당 버전으로 잘 실행 됐습니다.






(음.. 다시 최신 버전으로 되돌려야겠네요 ㅎㅎ...)




"UnknownFormatConversionException : Conversion = End of String" 위와 같은 오류를 보셨다면 저와 비슷한 경우가 아닐까 생각이 듭니다.. 사실 한번 당했던 오류인데 시간이 지나 똑같은 ...



"UnknownFormatConversionException : Conversion = End of String"

위와 같은 오류를 보셨다면 저와 비슷한 경우가 아닐까 생각이 듭니다..

사실 한번 당했던 오류인데 시간이 지나 똑같은 실수를 해서 이참에 글로 남깁니다.








1. 원인

String.format() 메서드를 사용할 때 정수형(%d), 문자열(%s), 부동 소수점(%f)과 같이

약속된 형식 변환 문자가 사용되어야 합니다. 

이를 벗어나서 알 수 없는 형식 변환 문자가 보이면 오류가 발생합니다.


아래와 같이 String.format()을 사용했다고 예를 들어봅니다.

val percentage = 80
Log.d("TEST", String.format("%d %", percentage))


"80 %"가 출력될 것이라고 기대했지만 마지막 "%"를 알 수 없는 형식 변환 문자로 판단하고

UnknownFormatConversionException 오류가 발생합니다.








2. 해결 방법

"%" 같은 경우는 "%%"를 사용하거나 또는 형식 변환 문자(%s)를 사용하는 것도 방법이 될 것 같습니다.

val percentage = 80
Log.d("TEST", String.format("%d %%", percentage)) // 80 %
Log.d("TEST", String.format("%s", "$percentage %")) // 80 %



음 사실 이게 제일 깔끔할 것 같기도 하네요 ㅎㅎ

Log.d("TEST", "$percentage %") // 80 %



개인적으로 그냥 한마디로 정리하면 

String.format()을 사용해야 한다면 "%" 사용을 조심합시다...!





  ScrollView 또는 RecyclerView는 스크롤이 제공되는 View 입니다. 각각 스크롤 위치를 제어하는데 비슷하면서 조금 차이도 가지고 있어 함께 정리하였습니다.           1. ScrollView 스크롤 위치 제어 ScrollV...

 

ScrollView 또는 RecyclerView는 스크롤이 제공되는 View 입니다.

각각 스크롤 위치를 제어하는데 비슷하면서 조금 차이도 가지고 있어 함께 정리하였습니다.


 

 

 

 

 

1. ScrollView 스크롤 위치 제어

ScrollView 클래스의 "scrollTo", "smoothScrollTo" 메서드를 사용합니다.

해당 메서드를 사용하면 원하는 스크롤 위치로 제어가 가능합니다.

scrollTo는 즉시 이동시키고 smoothScrollTo는 부드럽게 이동시키는 차이를 가지고 있습니다.

사용 예시는 아래와 같습니다.

 

 

[스크롤 맨 위로 올리기]

//binding.scrollView.scrollTo(0, binding.scrollView.top) // 즉시 이동
binding.scrollView.smoothScrollTo(0, binding.scrollView.top) // 부드럽게 이동



[스크롤 맨 아래로 내리기]

//binding.scrollView.scrollTo(0, binding.scrollView.bottom) // 즉시 이동
binding.scrollView.smoothScrollTo(0, binding.scrollView.bottom) // 부드럽게 이동


 

[스크롤 특정 위치로 이동]

//binding.scrollView.scrollTo(0, position) // 즉시 이동
binding.scrollView.smoothScrollTo(0, position) // 부드럽게 이동

 

 

 

 

2. RecyclerView 스크롤 위치 제어

RecyclerView 클래스의 "scrollToPosition", "smoothScrollToPostion" 메서드를 사용합니다.

ScrollView와 다르게 RecyclerView 아이템 인덱스(Position) 위치를 인자로 사용됩니다.

마찬가지로 scrollToPosition는 즉시 이동, smoothScrollToPostion는 부드럽게 이동합니다.

사용 예시는 아래와 같습니다.

 

 

[스크롤 맨 위로 올리기]

//binding.rvItems.scrollToPosition(0) // 즉시 이동
binding.rvItems.smoothScrollToPosition(0) // 부드럽게 이동



[스크롤 맨 아래로 내리기]

val itemCount = recyclerViewAdapter.itemCount
if (itemCount > 0) {
//binding.rvItems.scrollToPosition(itemCount - 1) // 즉시 이동
binding.rvItems.smoothScrollToPosition(itemCount - 1) // 부드럽게 이동
}



[스크롤 특정 위치로 이동]

//binding.rvItems.scrollToPosition(position) // 즉시 이동
binding.rvItems.smoothScrollToPosition(position) // 부드럽게 이동

 

 

 

EditText를 사용하다보면 키보드를 내리고 싶은 경우가 있습니다. 예를 들면 위 그림과 같이 EditText 입력하고 검색 버튼을 누르면 텍스트 입력은 끝났으니 키보드가 내려갔으면 좋겠는데 계속해서 유지된 상태로 있습니다. 이와 같이 키보드를 내려...

EditText를 사용하다보면 키보드를 내리고 싶은 경우가 있습니다.

예를 들면 위 그림과 같이 EditText 입력하고 검색 버튼을 누르면

텍스트 입력은 끝났으니 키보드가 내려갔으면 좋겠는데 계속해서 유지된 상태로 있습니다.

이와 같이 키보드를 내려야하는 상황이 있을 수 있는데요,

간단한 예제로 남겨보려고 합니다.








1. EditText 키보드 내리기/올리기

아래와 같이 EditText 키보드를 내리는 동작은 아래와 같이 간단하게 구현 가능합니다.

private fun hideKeyboard(view: EditText) {
try {
val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(view.windowToken, 0)
} catch (e: Exception) {
e.printStackTrace()
}
}


추가로 원하는 EditText에 포커싱을 주고 키보드를 올려주는 동작도 아래와 같이 작성할 수 있습니다.

private fun showKeyboard(view: EditText) {
try {
view.requestFocus() // Focus to target editText

val inputMethodManager = getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.showSoftInput(view, 0)
} catch (e: Exception) {
e.printStackTrace()
}
}








2. 테스트 결과

MainActivity에서 몇 가지 버튼을 생성해서 간단하게 테스트해볼 수 있었습니다.

class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)

binding.btnSearch.setOnClickListener {
hideKeyboard(binding.etKeyword)
}

binding.btnShowKeyboard.setOnClickListener {
showKeyboard(binding.etKeyword)
}

binding.btnHideKeyboard.setOnClickListener {
hideKeyboard(binding.etKeyword)
}
}

...
}