Foreword
A sidebar that implements quick indexing, has been completely encapsulated and can be used directly
Some notes on using it
I use RecyclerView to use it together. I can use 2 methods to scroll to the specified position. Choose one of the two methods, one is fast scrolling and the other is smooth scrolling. However, the scrolling itemPosition requires you to calculate the position in advance. You cannot obtain the specified position through the onBindViewHolder method in the adapter, because this method will not be executed if the list does not scroll to the specified position. Here I use a Map collection to calculate and save the positions of all first letter codes in advance, and then get the itemPosition position that needs to be scrolled from the Map according to the callback of SideBarView.
mRecyclerView.scrollToPosition(itemPosition); mRecyclerView.smoothScrollToPosition(itemPosition);
Renderings
Code
import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Point; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import androidx.annotation.ColorInt; import androidx.annotation.Nullable; import java.util.ArrayList; import java.util.List; /** * content: Customized letter positioning sidebar * time: 2020-4-24 * * @author: guanxinjin */ public class SideBarView extends View { private List<String> mContentDataList = new ArrayList<>(); private int mBackgroundColor = Color.TRANSPARENT; private int mPaddingTop = 0; private int mPaddingBottom = 0; private int mPaddingLeft = 0; private int mPaddingRight = 0; private float mTextSize = 15; private int mTextColor = Color.BLACK; private int mItemSpace = 15; //Customized item interval private boolean mIsEqualItemSpace = true; //Whether the height intervals of the items are evenly divided according to the height of the View private Paint mPaint; private int mWidth = 0; private int mHeight = 0; private Point mOneItemPoint = new Point(); //Coordinate value of the first item private int mItemHeightSize = 0; //Height size of a single Item private OnClickListener mListener; public SideBarView(Context context) { super(context); initPaint(); } public SideBarView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); initPaint(); } public SideBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initPaint(); } public SideBarView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); initPaint(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); mWidth = MeasureSpec.getSize(widthMeasureSpec); mHeight = MeasureSpec.getSize(heightMeasureSpec); if (mIsEqualItemSpace) {<!-- -->//Equally divide Item spacing mode mItemHeightSize = (mHeight - (mPaddingTop + mPaddingBottom)) / mContentDataList.size();//Height - Top and bottom margins / Number of Items = Height of an Item } else { //Customize Item spacing mode mItemHeightSize = ((mHeight - (mPaddingTop + mPaddingBottom)) / mContentDataList.size()) + mItemSpace; } mOneItemPoint.x = (mWidth - (mPaddingLeft + mPaddingRight)) / 2; //Width - left and right margins / 2 = X coordinate value of the first Item mOneItemPoint.y = mPaddingTop + mItemHeightSize; //Top margin + Item height = Y coordinate value of the first Item setMeasuredDimension(mWidth, mHeight); } @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); int itemAllHeight = mHeight - (mPaddingTop + mPaddingBottom); switch (action) { case MotionEvent.ACTION_DOWN: //According to the division ratio between the current clicked position and the total height of the overall item, round the divided number to obtain the position in the corresponding set. int downPosition = Math.round(mContentDataList.size() / (itemAllHeight / event.getY())); downPosition = Math.max(downPosition, 1); //Values less than 1 are not allowed downPosition = Math.min(downPosition, mContentDataList.size());//No values larger than the collection length are allowed downPosition = downPosition - 1; if (mListener != null) { mListener.onItemDown(downPosition, mContentDataList.get(downPosition)); } return true; case MotionEvent.ACTION_MOVE: int movePosition = Math.round(mContentDataList.size() / (itemAllHeight / event.getY())); movePosition = Math.max(movePosition, 1); movePosition = Math.min(movePosition, mContentDataList.size()); movePosition = movePosition - 1; if (mListener != null) { mListener.onItemMove(movePosition, mContentDataList.get(movePosition)); } return true; case MotionEvent.ACTION_UP: int upPosition = Math.round(mContentDataList.size() / (itemAllHeight / event.getY())); upPosition = Math.max(upPosition, 1); upPosition = Math.min(upPosition, mContentDataList.size()); upPosition = upPosition - 1; if (mListener != null) { mListener.onItemUp(upPosition, mContentDataList.get(upPosition)); } return true; } return super.onTouchEvent(event); } private void initPaint() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(mTextColor); mPaint.setTextSize(mTextSize); mPaint.setTextAlign(Paint.Align.CENTER); } public void setContentDataList(List<String> list) { mContentDataList.clear(); mContentDataList.addAll(list); postInvalidate(); } /** * Set text size * * @param spValue unit sp */ public void setTextSize(float spValue) { mTextSize = sp2px(spValue); mPaint.setTextSize(mTextSize); postInvalidate(); } public void setTextColor(@ColorInt int color) { mTextColor = color; mPaint.setColor(mTextColor); postInvalidate(); } public void setBackgroundColor(@ColorInt int color) { mBackgroundColor = color; postInvalidate(); } /** * Set whether to evenly divide the spacing of items according to the height of the View * * @param isEqualItemSpace true=Use equalization false=Do not use equalization */ public void setEqualItemSpace(boolean isEqualItemSpace) { mIsEqualItemSpace = isEqualItemSpace; postInvalidate(); } /** * spacing of items * * @param itemSpace unit dp */ public void itemSpace(int itemSpace) { mItemSpace = dip2px(itemSpace); mIsEqualItemSpace = false; postInvalidate(); } /** * Set padding * * @param top top margin, unit dp * @param bottom bottom margin, unit dp * @param left left margin, unit dp * @param right right margin, unit dp */ public void setPadding(int top, int bottom, int left, int right) { mPaddingTop = dip2px(top); mPaddingBottom = dip2px(bottom); mPaddingLeft = dip2px(left); mPaddingRight = dip2px(right); postInvalidate(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(mBackgroundColor); drawContent(canvas); } private void drawContent(Canvas canvas) { if (mContentDataList.isEmpty()) { return; } for (int i = 0; i < mContentDataList.size(); i + + ) { String itemContent = mContentDataList.get(i); if (i == 0) { canvas.drawText(itemContent, mOneItemPoint.x, mOneItemPoint.y, mPaint); continue; } int y = mOneItemPoint.y + (mItemHeightSize * i); canvas.drawText(itemContent, mOneItemPoint.x, y, mPaint); } } private int sp2px(float spValue) { final float fontScale = getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } private int dip2px(float dpValue) { float scale = getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } public void setOnClickListener(OnClickListener listener) { this.mListener = listener; } public interface OnClickListener { void onItemDown(int position, String itemContent); void onItemMove(int position, String itemContent); void onItemUp(int position, String itemContent); } }
End