728x90
안녕하세요. 이번 시간에는 리사이클러뷰에 Decoration으로 Incator 구현을 해보겠습니다.
먼저 RecyclerView 의 추상클래스인 ItamDecoration 을 상속받은 클래스를 다음과 같이 구현합니다.
LinePagerIndicatorDecoration.java
package com.example.recyclerviewevent.Event;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.Interpolator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
public class LinePagerIndicatorDecoration extends RecyclerView.ItemDecoration{
private int colorActive = 0xFFFFFFFF;
private int colorInactive = 0x66FFFFFF;
private static final float DP = Resources.getSystem().getDisplayMetrics().density;
/**
* Height of the space the indicator takes up at the bottom of the view.
* 뷰 맨 아래에서 표시기가 차지하는 공간의 높이입니다
*/
private final int mIndicatorHeight = (int) (DP * 16);
/**
* Indicator stroke width.
*/
private final float mIndicatorStrokeWidth = DP * 1/2;
/**
* Indicator width.
*/
private final float mIndicatorItemLength = DP * 16;
/**
* Padding between indicators.
*/
private final float mIndicatorItemPadding = DP * 4;
/**
* Some more natural animation interpolation
*/
private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();
private final Paint mPaint = new Paint();
public LinePagerIndicatorDecoration() {
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(mIndicatorStrokeWidth);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
int itemCount = parent.getAdapter().getItemCount();
// center horizontally, calculate width and subtract half from center 가로로 중심을 잡고, 너비를 계산하고 중심에서 반을 뺍니다.
float totalLength = mIndicatorItemLength * itemCount;
float paddingBetweenItems = Math.max(0, itemCount - 1) * mIndicatorItemPadding;
float indicatorTotalWidth = totalLength + paddingBetweenItems;
float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2F;
// center vertically in the allotted space 할당된 공간에 수직으로 중심을 잡다
float indicatorPosY = parent.getHeight() - mIndicatorHeight / 2F;
drawInactiveIndicators(c, indicatorStartX, indicatorPosY, itemCount);
// find active page (which should be highlighted) 활성 페이지 찾기(강조되어야 함) 활성 페이지 찾기(강조되어야 함)
LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
int activePosition = layoutManager.findFirstVisibleItemPosition();
if (activePosition == RecyclerView.NO_POSITION) {
return;
}
// find offset of active page (if the user is scrolling) 활성 페이지의 오프셋 찾기(사용자가 스크롤 중인 경우)
final View activeChild = layoutManager.findViewByPosition(activePosition);
int left = activeChild.getLeft();
int width = activeChild.getWidth();
// on swipe the active item will be positioned from [-width, 0] 스와이프 시 활성 항목은 [-width, 0]에서 위치하게 됩니다.
// interpolate offset for smooth animation 부드러운 애니메이션을 위해 간격띄우기 보간
float progress = mInterpolator.getInterpolation(left * -1 / (float) width);
drawHighlights(c, indicatorStartX, indicatorPosY, activePosition, progress, itemCount);
}
private void drawInactiveIndicators(Canvas c, float indicatorStartX, float indicatorPosY, int itemCount) {
mPaint.setColor(colorInactive);
// width of item indicator including padding 패딩을 포함한 indicator 폭
final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;
float start = indicatorStartX;
for (int i = 0; i < itemCount; i++) {
// draw the line for every item
c.drawLine(start, indicatorPosY, start + mIndicatorItemLength, indicatorPosY, mPaint);
start += itemWidth;
}
}
private void drawHighlights(Canvas c, float indicatorStartX, float indicatorPosY,
int highlightPosition, float progress, int itemCount) {
mPaint.setColor(colorActive);
// width of item indicator including padding
final float itemWidth = mIndicatorItemLength + mIndicatorItemPadding;
if (progress == 0F) {
// no swipe, draw a normal indicator
float highlightStart = indicatorStartX + itemWidth * highlightPosition;
c.drawLine(highlightStart, indicatorPosY,
highlightStart + mIndicatorItemLength, indicatorPosY, mPaint);
} else {
float highlightStart = indicatorStartX + itemWidth * highlightPosition;
// calculate partial highlight
float partialLength = mIndicatorItemLength * progress;
// draw the cut off highlight
c.drawLine(highlightStart + partialLength, indicatorPosY,
highlightStart + mIndicatorItemLength, indicatorPosY, mPaint);
// draw the highlight overlapping to the next item as well
if (highlightPosition < itemCount - 1) {
highlightStart += itemWidth;
c.drawLine(highlightStart, indicatorPosY,
highlightStart + partialLength, indicatorPosY, mPaint);
}
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.bottom = mIndicatorHeight;
}
}
그 다음 리사이클러뷰에 필요한 데이터클래스와 어댑터를 정의합니다.
Photo.java
package com.example.recyclerviewevent;
public class Photo {
private int path;
public Photo(int path) {
this.path = path;
}
public int getPath() {
return path;
}
public void setPath(int path) {
this.path = path;
}
@Override
public String toString() {
return "Photo{" +
"path=" + path +
'}';
}
}
GalleyAdapter.java
package com.example.recyclerviewevent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class GalleyAdapter extends RecyclerView.Adapter<GalleyAdapter.ViewHolder>{
private ArrayList<Photo> uriList ;
public GalleyAdapter(ArrayList<Photo> uriList) {
this.uriList = uriList;
}
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView image;
public ViewHolder(@NonNull View itemView) {
super(itemView);
this.image=itemView.findViewById(R.id.image);
}
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.image_item, parent, false);
ViewHolder vh=new ViewHolder(view);
return vh;
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.image.setImageResource(uriList.get(position).getPath());
}
@Override
public int getItemCount() {
return uriList.size();
}
}
MainActivity.java
package com.example.recyclerviewevent;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.PagerSnapHelper;
import androidx.recyclerview.widget.RecyclerView;
import android.os.Bundle;
import com.example.recyclerviewevent.Event.LinePagerIndicatorDecoration;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private String TAG=this.getClass().getSimpleName();
private GalleyAdapter adapter;
private LinearLayoutManager layoutManager;
private ArrayList<Photo> photoList=new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView=findViewById(R.id.rv);
initRecyclerView();
}
//Dummy Data
void setData(){
photoList.add(new Photo(R.drawable.dog1));
photoList.add(new Photo(R.drawable.dog2));
photoList.add(new Photo(R.drawable.dog3));
photoList.add(new Photo(R.drawable.dog4));
}
//리사이클러뷰 초기화
private void initRecyclerView() {
setData();
layoutManager=new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
recyclerView.setLayoutManager(layoutManager);
adapter=new GalleyAdapter(photoList);
recyclerView.setAdapter(adapter);
setRecyclerViewEvent();
}
//리사이클러뷰 이벤트 설정
private void setRecyclerViewEvent() {
/**
* Implementation of the SnapHelper supporting pager style snapping in either vertical or horizontal orientation.
* PagerSnapHelper can help achieve a similar behavior to androidx.viewpager.widget.ViewPager. Set both RecyclerView and the items of the RecyclerView.Adapter to have android.view.ViewGroup.LayoutParams.MATCH_PARENT height and width and then attach PagerSnapHelper to the RecyclerView using attachToRecyclerView(RecyclerView).*/
PagerSnapHelper snapHelper = new PagerSnapHelper();
/**
* Add an RecyclerView.ItemDecoration to this RecyclerView. Item decorations can affect both measurement and drawing of individual item views.
* Item decorations are ordered. Decorations placed earlier in the list will be run/queried/drawn first for their effects on item views. Padding added to views will be nested; a padding added by an earlier decoration will mean further item decorations in the list will be asked to draw/pad within the previous decoration's given area.
* Params:
* decor – Decoration to add
* */
recyclerView.addItemDecoration(new LinePagerIndicatorDecoration());
/**
* Attaches the SnapHelper to the provided RecyclerView, by calling RecyclerView.setOnFlingListener(RecyclerView.OnFlingListener). You can call this method with null to detach it from the current RecyclerView.
* Params:
* recyclerView – The RecyclerView instance to which you want to add this helper or null if you want to remove SnapHelper from the current RecyclerView.
* Throws:
* IllegalArgumentException – if there is already a RecyclerView.OnFlingListener attached to the provided RecyclerView.*/
snapHelper.attachToRecyclerView(recyclerView);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.recyclerview.widget.RecyclerView
android:background="#E91E63"
android:id="@+id/rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="5dp"
android:layout_marginTop="200dp"
android:layout_marginEnd="5dp"
android:layout_marginBottom="200dp" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
image_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="5dp"
>
<ImageView
android:id="@+id/image"
android:layout_width="300dp"
android:layout_height="300dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</RelativeLayout>
'안드로이드 자바' 카테고리의 다른 글
[Android][JAVA] overridePendingTransition 을 이용한 액티비티 전환 애니메이션 설정 (0) | 2023.02.27 |
---|---|
[Android][Java] PickView 라이브러리 사용하기 (0) | 2023.02.22 |
[JAVA][Android] SearchView 구현하기 (0) | 2023.02.14 |
[Java][Android] 소켓(Socket)을 이용해 에코서버(Echo Server) 구현 (0) | 2023.02.13 |
[Android][Java] 알라딘 API로 책 검색하기 (0) | 2023.02.11 |