Skip to content

✏️ BEEP Tech 김명석

ezhoon edited this page Dec 2, 2022 · 12 revisions

김명석 기술 블로그

✂️ 벡터 이미지를 편집해보자!

벡터 이미지를 편집해보자!

image

문득 새벽에 이미지를 찾지 못한 경우 이미지를 보여주기 위해 사용하는 이미지를 표현하기 위해 12dp 만큼 padding을 줘야한다는 점이 마음에 들지 않았다.
그냥 Background를 Drawable 파일로 만들어서 쓸껄...

image
일단 하기로 했으니 확인부터 해보면

width, height 를 48로 변경하고 모든 값을 12를 더해주면 되지 않을까 생각을 해봤다

image
일단 여기 까지는 괜찮았다.

image

M33,31V17c0,-1.1 -0.9,-2 -2,-2

image

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
image
처음 2개의 좌표는 제어점, 마지막 좌표는 선이 끝나는 위치

대문자로 이루어져 있는 것은 절대좌표, 소문자로 이루어져 있는것은 상대좌표 라고 이해하면 될듯하다 그래서 이번에는 대문자의 값만 12를 더해줬다
image

직접 처음부터 그리기는 힘들지만 적당히 수정해 보는건 어렵지 않을듯하다

📏 ItemDecorator 를 이용한 아이템 Offset 설정!

ItemDecoration

특정 뷰에 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을 추가 할 수 있다.

ListSpaceItemDecoration

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
}

GridSpaceItemDecoration

GridLayoutManager

기본적으로 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
}

GridSectionSpaceItemDecoration

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
}

✍️ BEEP Tech Blog

박명범

양수진

김명석

이지훈

👾 BEEP

🗣 Ground Rule

✏️ Conventions

⚙️ Setting

🌱 Daily Scrum

week 1
week 2
week 3
week 4
week 5
week 6
Clone this wiki locally