diff --git a/.idea/misc.xml b/.idea/misc.xml index ea255e67..7bc501b2 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -43,7 +43,7 @@ - + diff --git a/app/src/main/java/com/cappielloantonio/play/helper/recyclerview/DotsIndicatorDecoration.java b/app/src/main/java/com/cappielloantonio/play/helper/recyclerview/DotsIndicatorDecoration.java new file mode 100644 index 00000000..647a90ae --- /dev/null +++ b/app/src/main/java/com/cappielloantonio/play/helper/recyclerview/DotsIndicatorDecoration.java @@ -0,0 +1,112 @@ +package com.cappielloantonio.play.helper.recyclerview; + +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.view.View; + +import androidx.annotation.ColorInt; +import androidx.recyclerview.widget.GridLayoutManager; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; + +import org.jetbrains.annotations.NotNull; + +public class DotsIndicatorDecoration extends RecyclerView.ItemDecoration { + private static final String TAG = "DotsIndicatorDecoration"; + + private final int indicatorHeight; + private final int indicatorItemPadding; + private final int radius; + + private final Paint inactivePaint = new Paint(); + private final Paint activePaint = new Paint(); + + public DotsIndicatorDecoration(int radius, int padding, int indicatorHeight, @ColorInt int colorInactive, @ColorInt int colorActive) { + float strokeWidth = Resources.getSystem().getDisplayMetrics().density * 1; + this.radius = radius; + + inactivePaint.setStrokeCap(Paint.Cap.ROUND); + inactivePaint.setStrokeWidth(strokeWidth); + inactivePaint.setStyle(Paint.Style.STROKE); + inactivePaint.setAntiAlias(true); + inactivePaint.setColor(colorInactive); + + activePaint.setStrokeCap(Paint.Cap.ROUND); + activePaint.setStrokeWidth(strokeWidth); + activePaint.setStyle(Paint.Style.FILL); + activePaint.setAntiAlias(true); + activePaint.setColor(colorActive); + + this.indicatorItemPadding = padding; + this.indicatorHeight = indicatorHeight; + } + + @Override + public void onDrawOver(@NotNull Canvas c, @NotNull RecyclerView parent, @NotNull RecyclerView.State state) { + super.onDrawOver(c, parent, state); + + if (parent.getAdapter() == null) return; + + int itemCount = (int) Math.ceil((double) parent.getAdapter().getItemCount() / 5); + + // center horizontally, calculate width and subtract half from center + float totalLength = this.radius * 2 * itemCount; + float paddingBetweenItems = Math.max(0, itemCount - 1) * indicatorItemPadding; + float indicatorTotalWidth = totalLength + paddingBetweenItems; + float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2f; + + // center vertically in the allotted space + float indicatorPosY = parent.getHeight() - indicatorHeight - (float) indicatorItemPadding / 3; + + drawInactiveDots(c, indicatorStartX, indicatorPosY, itemCount); + + final int activePosition; + + if (parent.getLayoutManager() instanceof GridLayoutManager) { + activePosition = ((GridLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition(); + } else if (parent.getLayoutManager() instanceof LinearLayoutManager) { + activePosition = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition(); + } else { + // not supported layout manager + return; + } + + if (activePosition == RecyclerView.NO_POSITION) { + return; + } + + // find offset of active page if the user is scrolling + final View activeChild = parent.getLayoutManager().findViewByPosition(activePosition); + if (activeChild == null) { + return; + } + + drawActiveDot(c, indicatorStartX, indicatorPosY, activePosition); + } + + private void drawInactiveDots(Canvas c, float indicatorStartX, float indicatorPosY, int itemCount) { + // width of item indicator including padding + final float itemWidth = this.radius * 2 + indicatorItemPadding; + + float start = indicatorStartX + radius; + for (int i = 0; i < itemCount; i++) { + c.drawCircle(start, indicatorPosY, radius, inactivePaint); + start += itemWidth; + } + } + + private void drawActiveDot(Canvas c, float indicatorStartX, float indicatorPosY, int highlightPosition) { + // width of item indicator including padding + final float itemWidth = this.radius * 2 + indicatorItemPadding; + float highlightStart = (float) Math.ceil(indicatorStartX + radius + itemWidth * highlightPosition / 5); + c.drawCircle(highlightStart, indicatorPosY, radius, activePaint); + } + + @Override + public void getItemOffsets(@NotNull Rect outRect, @NotNull View view, @NotNull RecyclerView parent, @NotNull RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + outRect.bottom = indicatorHeight; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java b/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java index 769e1f61..f6114fdc 100644 --- a/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java +++ b/app/src/main/java/com/cappielloantonio/play/ui/fragment/HomeFragment.java @@ -11,6 +11,7 @@ import android.view.ViewGroup; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; import androidx.core.view.ViewCompat; import androidx.fragment.app.Fragment; import androidx.lifecycle.ViewModelProvider; @@ -31,6 +32,7 @@ import com.cappielloantonio.play.adapter.SongHorizontalAdapter; import com.cappielloantonio.play.adapter.YearAdapter; import com.cappielloantonio.play.databinding.FragmentHomeBinding; import com.cappielloantonio.play.helper.recyclerview.CustomLinearSnapHelper; +import com.cappielloantonio.play.helper.recyclerview.DotsIndicatorDecoration; import com.cappielloantonio.play.model.Album; import com.cappielloantonio.play.model.Artist; import com.cappielloantonio.play.model.Song; @@ -358,6 +360,15 @@ public class HomeFragment extends Fragment { SnapHelper starredTrackSnapHelper = new PagerSnapHelper(); starredTrackSnapHelper.attachToRecyclerView(bind.starredTracksRecyclerView); + + bind.starredTracksRecyclerView.addItemDecoration( + new DotsIndicatorDecoration( + getResources().getDimensionPixelSize(R.dimen.radius), + getResources().getDimensionPixelSize(R.dimen.radius) * 4, + getResources().getDimensionPixelSize(R.dimen.dots_height), + requireContext().getResources().getColor(R.color.titleTextColor, null), + requireContext().getResources().getColor(R.color.titleTextColor, null)) + ); } private void initStarredAlbumsView() { @@ -380,6 +391,15 @@ public class HomeFragment extends Fragment { SnapHelper starredAlbumSnapHelper = new PagerSnapHelper(); starredAlbumSnapHelper.attachToRecyclerView(bind.starredAlbumsRecyclerView); + + bind.starredAlbumsRecyclerView.addItemDecoration( + new DotsIndicatorDecoration( + getResources().getDimensionPixelSize(R.dimen.radius), + getResources().getDimensionPixelSize(R.dimen.radius) * 4, + getResources().getDimensionPixelSize(R.dimen.dots_height), + requireContext().getResources().getColor(R.color.titleTextColor, null), + requireContext().getResources().getColor(R.color.titleTextColor, null)) + ); } private void initStarredArtistsView() { @@ -402,6 +422,15 @@ public class HomeFragment extends Fragment { SnapHelper starredArtistSnapHelper = new PagerSnapHelper(); starredArtistSnapHelper.attachToRecyclerView(bind.starredArtistsRecyclerView); + + bind.starredArtistsRecyclerView.addItemDecoration( + new DotsIndicatorDecoration( + getResources().getDimensionPixelSize(R.dimen.radius), + getResources().getDimensionPixelSize(R.dimen.radius) * 4, + getResources().getDimensionPixelSize(R.dimen.dots_height), + requireContext().getResources().getColor(R.color.titleTextColor, null), + requireContext().getResources().getColor(R.color.titleTextColor, null)) + ); } private void initRecentAddedAlbumView() { diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 158d1208..001488b0 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -11,4 +11,6 @@ 128dp + 2dp + 2dp \ No newline at end of file