본문 바로가기

Programming

(11)
SavedInstanceState와 ViewModel SavedInstanceState 앱을 만들면서 늘 지나치던 아이가 있다…. onCreate에서 인자로 들어오는 savedInstanceState인데, Bundle 자료형으로 들어온다고 한다. 보통 화면을 회전할 때 사용한다고 개념적으로 알고만 있는데 이번에 직접 사용해보려고 한다. class MainActivity : AppCompatActivity() { private val binding: ActivityMainBinding by lazy { DataBindingUtil.setContentView(this, R.layout.activity_main) } var count = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(..
lifecycleOwner와 viewLifecycleOwner의 차이 Fragment에서 LiveData를 observe할 때 LifecycleOwner로 Fragment 자체를 넣게 되면 viewLifecycleOwner를 사용하라는 에러가 표시된다. 처음엔 Fragment는 lifecycleOwner가 따로 없어서 viewLifecycleOwner를 사용해야하는구나 하고 넘어갔다. 당연한 건 없었다…. Fragment는 분명 LifecycleOwner를 상속받고 있기 때문에 LiveData의 observe에서도 사용이 가능한 게 정상이다. 심지어 DataBinding의 lifecycleOwner에는 정상적으로 잘 들어가지만 observe에서 사용할 때만 빨간줄로 에러가 표시된다. 그 이유는 Fragment의 lifecycleOwner와 viewLifecycleOwner..
RoomDB에서 LiveData 사용 예전 포스팅에서 Realm이 RoomDB보다 사용이 간편해 Realm을 사용한다고 했는데, 그 중 가장 크게 작용했던 RoomDB의 단점이 Main Thread에서는 사용이 불가능하다는 점이었다. 이전 포스트 👉 Room DB 사용하기 그런데 회사 프로젝트 진행을 하던 중 LiveData를 사용하면 메인 스레드에서도 데이터를 읽어올 수 있다는 글을 보게 되었다. LiveData 자체가 비동기적으로 동작하기 때문에 Main Thread에서도 에러가 발생하지 않는다는 것이 핵심이었다. 이를 토대로 위 포스트의 코드를 리팩토링 해본다면 @Dao interface RecipeDao { ... @Query("SELECT * FROM recipe") fun getAll(): LiveData ... } 위처럼 SEL..
Android Custom View 만들기 해당 뷰 하단의 버튼들을 보면 모두 같은 레이아웃에 아이콘과 텍스트만 다르다는 것을 볼 수 있다. 이럴 때는 커스텀뷰를 사용해 뷰를 정의하면 버튼마다 레이아웃과 뷰를 만들어줄 필요 없이 커스텀 뷰 하나만 만들어주면 되기 때문에 상당히 편리하다. 커스텀뷰를 만드는 것은 세 단계를 거친다. 뷰 구현 속성 정의 뷰 클래스 구현 1. 뷰 구현 우선 커스텀뷰의 xml 파일을 만들어준다. 데이터바인딩을 사용하기 위해 layout 태그로 감싸는 것도 잊지 말자. 대략 원하는 형태의 뷰가 만들어졌다. 2. 속성 정의 이제 해당 뷰에서 사용하는 속성을 정의해야한다. 해당 뷰에서는 이모지, 타이틀, 카운트가 다르므로 세 개의 속성을 받을 것이다. values 폴더 내에 attrs.xml 파일을 만들고 속성과 자료형을 입력..
RxSwift Input/Output 구조 사용하기 RxSwift와 MVVM을 프로젝트에 적용시키기 위해 다양한 글들을 읽어봤는데 가장 보편적으로 사용되는 패턴이 Input/Output 패턴인 것 같았다. 해당 패턴은 두 가지 방법으로 구현이 가능했는데 첫 번째는 ViewModel 내에서 Input, Output 객체를 생성하고 View에서 바인딩하는 방법 두 번째는 View에서 Input 객체를 생성하고 ViewModel의 transform의 함수를 호출해 Output 객체를 반환받아 사용하는 방법이 있었다. 개인적으로 View에서는 ViewModel의 데이터에 직접 접근해 수정이 가능하도록 만드는 것이 MVVM과는 살짝 거리가 있다고 생각해서 두 번째 방식을 선택했고 코드는 아래와 같다. // ViewModel.swift protocol ViewMod..
Array의 toList()와 asList() Array를 List로 변환하는 중에 Array의 메서드로 toList()와 asList()가 있는 것을 보고 무슨 차이점이 있는 건지 궁금해 검색해봤다. 1. toList() toList()는 대상 Array를 복사해 새로운 인스턴스로 반환한다. val array = arrayOf(1, 2, 3) val list = array.toList() array[0] = 4 println(list) // 1, 2, 3 2. asList() asList()는 대상 Array와 동일한 배열 요소를 공유하는 List를 반환한다. val array = arrayOf(1, 2, 3) val list = array.asList() array[0] = 4 println(list) // 4, 2, 3 toList()와 달리 ..
RecyclerView 첫 번째, 마지막 아이템 간격 주기 구현을 하다보면 위 사진처럼 RecyclerView의 첫 번째 아이템에만 간격을 주고싶을 때가 있다. 하지만 이럴 때 RecyclerView의 왼쪽에 Margin을 주는 방식으로 구현한다면 스크롤이 잘리게 돼서 모양이 안 예쁘다. 우리가 원하는 건 이런 느낌이 아니다. 이런 저런 방법을 찾아봤는데 생각보다 간단히 구현이 가능했다. clipToPadding 속성을 false로 주고 padding을 주면 원하는 대로 구현이 된다. padding만 줄 경우 margin을 줬을 때처럼 스크롤이 잘리게 되는데, clipToPadding을 false로 설정한다면 padding을 주되 스크롤이 잘리지 않는다. RecyclerView 자체는 padding의 영향을 받지 않고 스크롤이 원하는 대로 구현되는 것을 확인할 ..
RecyclerView Adapter 연동하기 Recycler View란? 리사이클러뷰(RecyclerView)는 많은 리스트 데이터들을 효율적으로 렌더링하기 위해 제공되는 뷰로, 기존에 쓰이던 리스트뷰(ListView)보다 성능과 유연성이 뛰어나 리스트뷰를 대체하고있다. 지금와서 리스트뷰는 안 쓰인다고 보면 된다. 리사이클러뷰가 뭔지 간단하게 설명하자면 많은 수의 데이터를 스크롤 가능한 리스트 형태로 표시해주는 뷰그룹이라고 할 수 있다. 리사이클러뷰를 사용해 데이터를 표시하기 위해서는 3가지 구성 요소를 구현해야한다. Adapter: 데이터를 사용해 뷰를 생성하는 역할 LayoutManager: 아이템들이 나열되는 형태를 관리하는 역할. 이를 사용해 수평, 수직 형태로 표현하는 건 물론 그리드 등의 형태로도 표시가 가능하다. ViewHolder: ..
OS 구분하기 Flutter의 가장 큰 장점이라고 하면 아무래도 한 번의 빌드로 안드로이드와 iOS 앱 모두 제작이 가능하다는 것이라고 할 수 있다. 하지만 막상 앱을 제작해보면 생각보다 친절하게 OS를 구분해주지는 않는다. 가령 다이얼로그나 팝업 메뉴를 표시할 때도 단순히 AlertDialog를 표시하면 안드로이드에서 사용하는 Material Design의 Alert Dialog만 표시된다. 이럴 땐 OS를 구분하여 직접 넣어줘야하는데 OS를 구별하는 방법은 두 가지가 있다. 1. foundation.dart 사용 'flutter/foundation.dart' 패키지를 import한다. import 'package:flutter/foundation.dart'; 그러면 defaultTargetPlatform으로 현재 ..
Room DB 사용하기 Room DB란? 2017년 구글 I/O에서 아키텍쳐 컴포넌트에 소개된 라이브러리로, SQLite의 추상 레이어를 제공하여 SQLite의 객체를 매핑하는 역할을 한다. 다시 말해 SQLite의 모든 기능을 사용할 수 있으며, DB로의 접근을 편하게 도와주는 라이브러리라 할 수 있다. SQLite와 비교해서 컴파일 도중 SQL에 대한 유효성 검사가 가능하며, schema가 변경될 경우에도 수동으로 업데이트할 필요 없이 쉽게 해결이 가능하다. 특히 getter, setter와 같은 상용구 코드 사용 없이 매핑이 가능해 무의미한 코드 작성 시간을 줄여준다. 학교에서 모바일 프로그래밍 과목을 수강하며 처음 접하게 된 로컬DB였는데 쉽게 따라하고 응용이 간편해 내부DB가 필요한 경우 주로 사용했다. 현재는 Rea..