Android development View_custom quick index sidebar SideBarView

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