목록FrontEnd/Android (63)
만족
css에서 box-shadow를 이용해 그림자 효과를 내듯이, android 에서는 elevation을 이용해 그림자 효과를 낸다. 다만, 위의 이미지처럼 컴포넌트를 위로 띄워 발생하는 그림자라는 컨셉트를 가지고 있어서, '얼마만큼 컴포넌트를 위로(z축) 띄울 것인가?' 를 지정하는 방식이다. 이렇게 elevation이라는 속성을 추가함으로써 손쉽게 그림자를 표시할 수 있지만, 그림자가 정상적으로 표시되지 않는 문제가 빈번히 발생한다. 몇 가지 케이스들을 통해 원인과 해결법을 살펴본다. 거두절미하고 결론만 보려면 맨 아래로 스크롤하면 된다. 그림자가 생길 공간이 없는 경우 이 코드에서 두 번째 TextView는 elevation 속성을 가지고 있다. 이 경우 그림자가 표시될까? 표시되지 않는다. 왜냐하면..
문제 웹앱 제출 시 사이트의 도메인의 소유권이 앱 제작자에게 있는지 없는지 구글에서는 알 수 없다. 가령 내가 네이버의 소유주가 아닌 데도 https://naver.com 을 웹뷰로 래핑해 앱으로 출시한다면 이는 서비스 소유권 침해이기 때문에 구글에서는 민감하게 반응할 수 밖에 없다. 내 경우엔 내가 제작한 키즈스쿨 서비스(https://kidsschool.vercel.app)을 웹뷰로 래핑해 출시했는데, 콘텐츠 도용 의심으로 제재를 받았다. 조치 웹사이트 서버 및 도메인은 내 소유가 아니기 때문에(웹사이트 호스팅 서비스로부터 대여) 도메인 소유 증명서는 제출할 수 없다. 따라서 해당 서비스에 내 소유라는 것을 증명하기 위한 짧은 텍스트 파일을 추가했다. https://kidsschool.vercel.a..
저런걸 어떻게 하는지 알아볼 것이다. 방법1: SpannableString public class ViewUtil { public static SpannableString colorString(int color, String text, String... wordsToColor) { SpannableString coloredString = new SpannableString(text); for (String word : wordsToColor) { int startColorIndex = text.indexOf(word); int endColorIndex = startColorIndex + word.length(); if (startColorIndex >= 0) coloredString.setSpan(new..
위의 Object를 read한 후, 그것을 write하고 다시 read하려고 할 때 발생한 예외이다. 신기한 것은 처음 read했을 때는 문제가 발생하지 않고, 그 데이터를 write한 다음, 다시 read했을때만 오류가 발생한다. W/System.err: java.lang.NullPointerException: Attempt to invoke virtual method 'int java.math.RoundingMode.ordinal()' on a null object reference at java.text.DecimalFormat.convertRoundingMode(DecimalFormat.java:4129) at java.text.DecimalFormat.readObject(DecimalFormat..
어떤 이미지의 색상을 변경해서 여러가지로 사용하고 싶을 때, 직접 각각의 색상들로 변경해서 여러 개의 drawable 파일로 만들 수도 있지만, 이는 매우 번거롭고, 변경사항에 유연하지 못하다. 따라서 tint라는 것을 사용해 여러 색상을 덧씌울 수 있는데, 이에 대해 알아보자. xml에서 tint 적용하기 android:tint로 적용할 수 있다. 요즘은 app:tint로 변경하라는 문구가 뜨는데, 아직은 둘 중 뭘로 해도 빌드는 잘 된다. 주의할 점은 이 방법으로는 벡터 이미지만 가능하다는 것이다. Java/Kotlin에서 tint적용하기 벡터 이미지와 비트맵 이미지에 색상을 입하는 방법이 다르다. 먼저 벡터 이미지에 색상을 덧씌워보자. imageView.setColorFilter(resources...
android app 내부에서 프라이빗하게 사용할 수 있는 간단한 SQLite에 대해 알아보자. 파일 형태로 입출력할 경우 검색을 위해서는 먼저 모든 file을 스캔해야 하고 검색, 수정, 삭제 등 모든 기능을 직접 구현해야 해서 매우 귀찮기 때문에, 용량이 비교적 큰 데이터를 다룰 때는 raw file과 직접 IO하는 것 보다 DB를 사용하는 편이 더 좋다. DBHelper 가장 먼저 Database와 통신하기 위한 클래스를 작성한다. class MyDBHelper( val context: Context, ) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) { companion object { val DB_NAME = "mydb.db" val DB_VE..
android에서 app의 스레드는 main thread와, sub thread가 있다. 기본 스레드는 main thread이고, 여기에서 ui update 가 진행된다. 하지만 file IO, network IO와 같이 오랫동안 스레드 상태를 block으로 만드는 작업들의 경우 해당 작업을 main thread에서 진행하면 ui update를 할 수 없는 상태가 되어 사용자가 바라보기에는 앱이 멈췄다고 인식하게 된다. (사실 network io의 경우에는 아주 구버전의 안드로이드가 아니라면 main thread에서 아예 실행할 수 없다. 가능하다면 발생할 상황이다.) 따라서 오래 걸리는 작업들은 별도의 스레드를 이용하여 메인 스레드가 오랫동안 블락되지 않게 해 주어야 한다. CorotineScope 기..
위와 같이 하나의 액티비티에 존재하는 어떤 sharedData에 대해 액티비티에 attached된 2개의 fragment가 그것을 사용한다고 가정해 보자. 만약 fragment1이 데이터를 변경한다면, 변경한 데이터를 activity가 fragment2에도 반영해 주어야 하고, 반대의 경우에도 마찬가지이다. 이것을 코드로 구현하게 되면 코드가 지저분해지면서 가독성을 낮추고, 이는 곧 버그가 발생하기 쉬운 환경이 된다. 사실 여기에서 우리는 '데이터가 무엇으로 바뀌는지'에 관심이 있지, '데이터가 어떻게 변경되는지(how to fetch?)'는 관심이 없다. ViewModel RxJava나 React를 경험해 본 사람들이라면, 리액티브 프로그래밍 기법을 사용했을 때 위와 같은 불필요한 곳에 신경을 덜 쓸 ..