-
Notifications
You must be signed in to change notification settings - Fork 4
✏️ BEEP Tech 김명석
김명석 기술 블로그
✂️ 벡터 이미지를 편집해보자!
문득 새벽에 이미지를 찾지 못한 경우 이미지를 보여주기 위해 사용하는 이미지를 표현하기 위해 12dp 만큼 padding을 줘야한다는 점이 마음에 들지 않았다.
그냥 Background를 Drawable 파일로 만들어서 쓸껄...
일단 하기로 했으니 확인부터 해보면
width, height 를 48로 변경하고 모든 값을 12를 더해주면 되지 않을까 생각을 해봤다
일단 여기 까지는 괜찮았다.
M33,31V17c0,-1.1 -0.9,-2 -2,-2
M33,31V17c12,10.9 11.1,10 10,10
여기부터 먼가 이상함을 느끼게 되었고 MDN 문서를 보고 공부를 하게 되었다
M x y or m dx dy
시작 위치를 이동 하는 명령어, 이동 시 선이 그려지지 않습니다.
L x y or l dx dy
현재 위치에서 이동하면서 선을 그린다 (대각선에서 사용)
H x or h dx
현재 위치에서 수평으로 이동을 하면서 선을 그린다
V y or v dy
현재 위치에서 수직으로 이동을 하면서 선을 그린다
Z or z
현재 위치에서 첫 번째 경로 (M) 까지 직선을 그립니다
C x1 y1, x2 y2, x y or c dx1 dy1, dx2 dy2, dx dy
처음 2개의 좌표는 제어점, 마지막 좌표는 선이 끝나는 위치
대문자로 이루어져 있는 것은 절대좌표, 소문자로 이루어져 있는것은 상대좌표 라고 이해하면 될듯하다
그래서 이번에는 대문자의 값만 12를 더해줬다
직접 처음부터 그리기는 힘들지만 적당히 수정해 보는건 어렵지 않을듯하다
📏 ItemDecorator 를 이용한 아이템 Offset 설정!
특정 뷰에 Offset, 특수한 그림을 추가 할 수 있고 ViewHolder xml 의 margin과 중복 적용된다.
@Override
public void onDraw(Canvas c) {
super.onDraw(c);
final int count = mItemDecorations.size();
for (int i = 0; i < count; i++) {
mItemDecorations.get(i).onDraw(c, this, mState);
}
}
- Decoration의 등록순서에 맞춰서 onDraw에서 그려준다
- RecyclerView에서 제공 받은 canvas를 이용하여 ViewHolder가 그려진 후 위에 그린다
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view,
@NonNull RecyclerView parent, @NonNull State state)
- 각 ViewHolder 마다 margin을 추가 할 수 있다.
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
// 1. LinearLayout 인지 확인
val manager = parent.layoutManager as? LinearLayoutManager ?: return
val itemCount = parent.adapter?.itemCount ?: 0
// 2. 아이템이 처음, 끝, 중간인지 판단
val position = when (parent.getChildAdapterPosition(view)) {
0 -> Position.Start
itemCount - 1 -> Position.End
else -> Position.Mid
}
// 3. RecyclerView 가 orientation 에 따라 Offset을 다르게 설정
val isVertical = manager.orientation == LinearLayoutManager.VERTICAL
if (isVertical) {
calculateVerticalOffsets(outRect, position)
} else {
calculateHorizontalOffsets(outRect, position)
}
}
private fun calculateVerticalOffsets(outRect: Rect, pos: Position) {
outRect.left = start
outRect.top = when (pos) {
Position.Start -> top
else -> space / 2
}
outRect.right = end
outRect.bottom = when (pos) {
Position.End -> bottom
else -> space / 2
}
}
private fun calculateHorizontalOffsets(outRect: Rect, pos: Position) {
outRect.left = when (pos) {
Position.Start -> start
else -> space / 2
}
outRect.top = top
outRect.right = when (pos) {
Position.End -> end
else -> space / 2
}
outRect.bottom = bottom
}
기본적으로
SpanSize
는 1이고setSpanSizeLookup
을 이용하여 수정할 수 있다.
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
// 1. GridManager 인지 확인
val manager = parent.layoutManager as? GridLayoutManager ?: return
// 2. SpanIndex를 구하기위해 Params 를 가져옴
val params = view.layoutParams as? GridLayoutManager.LayoutParams ?: return
val position = parent.getChildAdapterPosition(view)
val itemCount = parent.adapter?.itemCount ?: 0
// 3. 위치에 따른 Offset 설정
outRect.left = if (isFirstCol(params)) start else hSpace / 2
outRect.top = if (isFirstRow(manager, position)) top else vSpace / 2
outRect.right = if (isLastCol(manager, params)) end else hSpace / 2
outRect.bottom = if (isLastRow(manager, position, itemCount)) bottom else vSpace / 2
}
// 해당 Position의 GroupIndex가 처음 아이템의 GroupIndex와 같은지 확인
private fun isFirstRow(manager: GridLayoutManager, position: Int): Boolean {
return getGroupIndex(manager, position) == getGroupIndex(manager, 0)
}
// 해당 Position의 GroupIndex가 마지막 아이템의 GroupIndex와 같은지 확인
private fun isLastRow(manager: GridLayoutManager, position: Int, itemCount: Int): Boolean {
return getGroupIndex(manager, position) == getGroupIndex(manager, itemCount - 1)
}
// 해당 포지션의 GroupIndex 를 반환
private fun getGroupIndex(manager: GridLayoutManager, position: Int): Int {
return manager.spanSizeLookup.getSpanGroupIndex(position, manager.spanCount)
}
// params 의 spanIndex 가 0 인지 확인
private fun isFirstCol(params: GridLayoutManager.LayoutParams): Boolean {
return params.spanIndex == 0
}
// params 의 spanIndex + spanSize == spanCount 이면 마지막 Colunm
private fun isLastCol(manager: GridLayoutManager, params: GridLayoutManager.LayoutParams): Boolean {
return params.spanIndex + params.spanSize == manager.spanCount
}
private fun setUpRecyclerView() {
val spanCount = 3
binding.rvList.apply {
adapter = galleryAdapter
layoutManager = GridLayoutManager(context, spanCount).apply {
spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
override fun getSpanSize(position: Int): Int {
return if (galleryAdapter.getItemViewType(position) == GalleryAdapter.TYPE_HEADER) spanCount else 1
}
}
}
addItemDecoration(GridSectionSpaceItemDecoration(20.dp, 4.dp, 4.dp, 12.dp, 4.dp, 12.dp))
}
}
- Header의 SpanSize를 변경
- addItemDecoration으로 Item의 Offset 설정
override fun getItemOffsets(
outRect: Rect,
view: View,
parent: RecyclerView,
state: RecyclerView.State
) {
// 1. GridManager 인지 확인
val manager = parent.layoutManager as? GridLayoutManager ?: return
// 2. SpanIndex를 구하기위해 Params 를 가져옴
val params = view.layoutParams as? GridLayoutManager.LayoutParams ?: return
val position = parent.getChildAdapterPosition(view)
val itemCount = parent.adapter?.itemCount ?: 0
// 3. 위치에 따른 Offset 설정
outRect.left = if (isFirstCol(params)) start else itemSpace / 2
outRect.top = if (isSection(manager, params)) {
if (isFirstRow(manager, position)) top else sectionDivider - itemSpace / 2
} else {
itemSpace / 2
}
outRect.right = if (isLastCol(manager, params)) end else itemSpace / 2
outRect.bottom = if (isLastRow(manager, position, itemCount)) bottom else itemSpace / 2
}
private fun isSection(manager: GridLayoutManager, params: GridLayoutManager.LayoutParams): Boolean {
return manager.spanCount == params.spanSize
}
- 안드로이드에서 지문 인증 하기!
- Firebase Google 로그인 세팅 중 겪은 오류
- 양탐정의 viewModelScope.launch 살인사건 수사일지
- 쉿! KeyStore과 Cipher
- WorkManager 알림과 위젯을 사용해보자!
- 애니메이션으로 삡에 숨결 불어넣기
- 리뷰어 등록을 자동으로 해보자
- Mockk을 활용한 테스트
- 검색 결과를 Room에 캐싱해보자!
- Room One to Many
- CustomException 과 Result를 적극 활용해보자!
- View의 Event를 처리하기 위한 상태 클래스를 만들어보자!
- WorkManager 알림과 위젯을 사용해보자!