Question: Need an indicator that supports images or color values Solution: The following effect
<com.core.ex.widget.IndicatorView android:id="@ + id/indicator_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|center_horizontal" android:layout_marginBottom="20dp" app:indicator_item_height="5dp" app:indicator_item_space="10dp" app:indicator_item_width="40dp" app:selected_color="@color/cl_red" app:unSelected_color="@color/cl_32a0fa" />
<com.core.ex.widget.IndicatorView android:id="@ + id/indicator_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginBottom="20dp" app:indicator_item_height="30dp" android:orientation="vertical" app:indicator_item_space="10dp" app:indicator_item_width="5dp" app:selected_color="@color/cl_red" app:unSelected_color="@color/cl_32a0fa" />
<com.core.ex.widget.IndicatorView android:id="@ + id/indicator_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginBottom="20dp" android:orientation="horizontal" app:indicator_item_height="40dp" app:indicator_item_space="10dp" app:indicator_item_width="40dp" app:selected_image="@drawable/icon_main_home" app:unSelected_image="@drawable/icon_main_list" />
package com.core.ex.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.drawable.BitmapDrawable; import android.util.AttributeSet; import android.view.View; import android.view.ViewGroup; import android.widget.FrameLayout; import android.widget.LinearLayout; import com.core.ex.LogHelps; import com.core.ex.R; import com.core.ex.util.BitmapUtils; /** * @Author chentao 0000668668 * @Time 2023/5/8 * @Description indicator display *Origin display: width and height can be equal * <p>-------------------------------------------------- ---------------------------- * Circle--the bottom is displayed horizontally and centered: * android:layout_gravity="bottom|center_horizontal" * android:layout_marginBottom="20dp" * app:indicator_item_height="5dp" * app:indicator_item_space="5dp" * app:indicator_item_width="5dp" * app:selected_color="@color/cl_red" * app:unSelected_color="@color/cl_32a0fa" * android:orientation="horizontal" * <p>-------------------------------------------------- ---------------------------- * The bottom of the rounded rectangle is displayed horizontally and centered: * android:layout_gravity="bottom|center_horizontal" * android:layout_marginBottom="20dp" * app:indicator_item_height="5dp" * app:indicator_item_space="10dp" * app:indicator_item_width="40dp" * app:selected_color="@color/cl_red" * app:unSelected_color="@color/cl_32a0fa" * android:orientation="horizontal" * <p>-------------------------------------------------- ---------------------------- * Circular vertical right center display: * android:layout_gravity="right|center_vertical" * android:layout_marginRight="20dp" * app:indicator_item_height="5dp" * app:indicator_item_space="5dp" * app:indicator_item_width="5dp" * app:selected_color="@color/cl_red" * app:unSelected_color="@color/cl_32a0fa" * android:orientation="vertical" * <p>-------------------------------------------------- ---------------------------- * The rounded rectangle is displayed vertically and centered on the right side: * android:layout_gravity="right|center_vertical" * android:layout_marginBottom="20dp" * app:indicator_item_height="5dp" * app:indicator_item_space="10dp" * app:indicator_item_width="40dp" * app:selected_color="@color/cl_red" * app:unSelected_color="@color/cl_32a0fa" * android:orientation="vertical" * <p>---------------Show pictures as above--------------------------------- ---------------------------------- * app:selected_color="@color/cl_red" * app:unSelected_color="@color/cl_32a0fa" * Replaced with: * app:selected_image="@drawable/icon_main_home" * app:unSelected_image="@drawable/icon_main_list" */ public class IndicatorView extends View { private Paint mPaint; private int mOrientation = LinearLayout.HORIZONTAL; private int currentIndex, totalIndex = 1; private int centreX, startX; private int mItemSpace = 10; // Spacing between items private int mItemWidth = 40; // itme width private int mItemHeight = 5; // item height private int mItemSelectedColor, mItemUnselectedColor; private Bitmap mItemSelectedBitmap, mItemUnselectedBitmap; public IndicatorView(Context context) { this(context, null); } public IndicatorView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public IndicatorView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomViewAttr, defStyleAttr, 0); mOrientation = typedArray.getInt(R.styleable.CustomViewAttr_android_orientation, mOrientation); mItemHeight = (int) typedArray.getDimension(R.styleable.CustomViewAttr_indicator_item_height, mItemHeight); mItemWidth = (int) typedArray.getDimension(R.styleable.CustomViewAttr_indicator_item_width, mItemWidth); mItemSpace = (int) typedArray.getDimension(R.styleable.CustomViewAttr_indicator_item_space, mItemSpace); BitmapDrawable drawable = (BitmapDrawable) typedArray.getDrawable(R.styleable.CustomViewAttr_selected_image); if (drawable != null) { //Set the image width and height to the item width and height to prevent the image from being too large and not fully displayed. mItemSelectedBitmap = drawable.getBitmap(); mItemSelectedBitmap = BitmapUtils.alterBitmapSize(mItemSelectedBitmap, mItemHeight, mItemWidth); } drawable = (BitmapDrawable) typedArray.getDrawable(R.styleable.CustomViewAttr_unSelected_image); if (drawable != null) { //Set the image width and height to the item width and height to prevent the image from being too large and not fully displayed. mItemUnselectedBitmap = drawable.getBitmap(); mItemUnselectedBitmap = BitmapUtils.alterBitmapSize(mItemUnselectedBitmap, mItemHeight, mItemWidth); } LogHelps.i("mItemSelectedBitmap:" + mItemSelectedBitmap, "mItemUnselectedBitmap:" + mItemUnselectedBitmap); mItemSelectedColor = typedArray.getColor(R.styleable.CustomViewAttr_selected_color, Color.LTGRAY); mItemUnselectedColor = typedArray.getColor(R.styleable.CustomViewAttr_unSelected_color, Color.WHITE); typedArray.recycle(); mPaint = new Paint(); mPaint.setAntiAlias(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } private int measureHeight(int measureSpec) { int result; int desired = 0; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); switch (mOrientation) { case LinearLayout.HORIZONTAL: desired = mItemHeight + getPaddingBottom() + getPaddingTop(); break; case LinearLayout.VERTICAL: desired = (totalIndex - 1) * mItemSpace + (totalIndex) * mItemHeight + getPaddingBottom() + getPaddingTop(); break; } if (specMode == MeasureSpec.EXACTLY) { result = Math.max(desired, specSize); } else { if (specMode == MeasureSpec.AT_MOST) { result = Math.min(desired, specSize); } else result = desired; } return result; } private int measureWidth(int measureSpec) { int result; int specMode = MeasureSpec.getMode(measureSpec); int specSize = MeasureSpec.getSize(measureSpec); int desired = 0; switch (mOrientation) { case LinearLayout.HORIZONTAL: desired = (totalIndex - 1) * mItemSpace + (totalIndex) * mItemWidth + getPaddingLeft() + getPaddingRight(); break; case LinearLayout.VERTICAL: desired = mItemWidth + getPaddingLeft() + getPaddingRight(); break; } if (specMode == MeasureSpec.EXACTLY) { result = Math.max(desired, specSize); } else { if (specMode == MeasureSpec.AT_MOST) { result = Math.min(desired, specSize); } else result = desired; } return result; } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); switch (mOrientation) { case LinearLayout.HORIZONTAL: centreX = getWidth() / 2; startX = centreX - ((totalIndex - 1) * mItemSpace + totalIndex * mItemWidth) / 2; for (int i = 0; i < totalIndex; i + + ) { if (mItemSelectedBitmap != null & amp; & amp; mItemUnselectedBitmap != null) { // Prioritize picture drawing canvas.drawBitmap((i == currentIndex) ? mItemSelectedBitmap : mItemUnselectedBitmap, startX, 0, mPaint); } else { mPaint.setColor((i == currentIndex) ? mItemSelectedColor : mItemUnselectedColor); canvas.drawRoundRect(new RectF(startX, 0, startX + mItemWidth, mItemHeight), mItemHeight, mItemHeight, mPaint); } startX + = (mItemSpace + mItemWidth); } break; case LinearLayout.VERTICAL: centreX = getHeight() / 2; startX = centreX - ((totalIndex - 1) * mItemSpace + totalIndex * mItemHeight) / 2; for (int i = 0; i < totalIndex; i + + ) { if (mItemSelectedBitmap != null & amp; & amp; mItemUnselectedBitmap != null) { // Prioritize picture drawing canvas.drawBitmap((i == currentIndex) ? mItemSelectedBitmap : mItemUnselectedBitmap, 0, startX, mPaint); } else { mPaint.setColor((i == currentIndex) ? mItemSelectedColor : mItemUnselectedColor); canvas.drawRoundRect(new RectF(0, startX, mItemWidth, startX + mItemHeight), mItemWidth, mItemWidth, mPaint); } startX + = (mItemSpace + mItemHeight); } break; } } public void setCurrentIndex(int currentIndex) { if (this.currentIndex == currentIndex) { return; } this.currentIndex = currentIndex; requestLayout(); } public int getCurrentIndex() { return currentIndex; } public void setTotalIndex(int totalIndex) { if (this.totalIndex == totalIndex) { return; } int oldTotalIndex = this.totalIndex; if (totalIndex < 1) return; if (totalIndex < oldTotalIndex) { if (currentIndex == totalIndex) currentIndex = totalIndex - 1; } this.totalIndex = totalIndex; LogHelps.iLow("totalIndex=" + totalIndex); post(() -> requestLayout()); } public int getTotalIndex() { return totalIndex; } }
<attr name="selected_image" format="reference" /> <!-- Selected image --> <attr name="unSelected_image" format="reference" /> <!-- Unselected image --> <attr name="selected_color" format="color" /> <!-- Font, etc. selected color --> <attr name="unSelected_color" format="color" /> <!-- Unselected colors such as fonts --> <attr name="indicator_item_height" format="dimension" /> <!-- Indicator item height --> <attr name="indicator_item_width" format="dimension" /> <!-- Indicator item width--> <attr name="indicator_item_space" format="dimension" /> <!-- The spacing between indicator items--> <attr name="android:orientation" />