FontResize

FontResize is a library that emulates wechat/Alipay font size adjustment controls

Gradle rely on

implementation 'com. LJX. View: fontresize: 1.0.1'
Copy the code

The library contains only one class, FontResizeView, so don’t worry about the library’s size.

rendering

Attributes that

<declare-styleable name="FontResizeView"> <attr name="minSize" format="reference|dimension" /> <! - minimum size - > < attr name = "maxSize" format = "reference | dimension" / > <! <attr name="totalGrade" format="integer" /> <! <attr name="standardGrade" format="integer" /> <! - standard level -- -- > < attr name = "leftText" format = "reference | string" / > <! - the left text, defaults to 'A' - > < attr name = "middleText" format = "reference | string" / > <! - intermediate text, defaults to the 'standard' - > < attr name = "rightText" format = "reference | string" / > <! - on the right side of the text, the default is' A '- > < attr name = "leftTextColor" format = "reference | color" / > <! - the left text color, the default for all black - > < attr name = "middleTextColor" format = "reference | color" / > <! - intermediate text color, the default for all black - > < attr name = "rightTextColor" format = "reference | color" / > <! - on the right side of the text color, the default for all black - > < attr name = "sliderRadius" format = "reference | dimension" / > <! - slide block radius - > < attr name = "sliderColor" format = "reference | color" / > <! - the slider color - > < attr name = "sliderShadowColor" format = "reference | color" / > <! - slide block edge shadow color - > < attr name = "lineColor" format = "reference | color" / > <! - line color - > < attr name = "lineStrokeWidth" format = "reference | dimension" / > <! - line width, the thick degree - > < attr name = "horizontalLineLength" format = "reference | dimension" / > <! - the horizontal line length -- -- > < attr name = "verticalLineLength" format = "reference | dimension" / > <! --> </declare-styleable>Copy the code

The XML configuration

<com.ljx.view.FontResizeView
    android:id="@+id/font_resize_view"
    android:layout_width="match_parent"
    android:layout_height="140dp"
    android:background="@android:color/white"
    app:maxSize="25sp"
    app:minSize="13sp"
    app:sliderColor="@android:color/white"
    app:sliderRadius="12dp"
    app:standardGrade="2"
    app:totalGrade="Seven" />
Copy the code

Set the callback

FontResizeView fontResizeView = findViewById(R.id.font_resize_view);
fontResizeView.setOnFontChangeListener(new OnFontChangeListener() {
    @Override
    public void onFontChange(float fontSize) {
        // Font size changes to a callback unit :sp}});Copy the code

Once configured, the code should look like the GIF above

Let’s add all the attributes and see what happens

<com.ljx.view.FontResizeView
    android:id="@+id/font_resize_view"
    android:layout_width="match_parent"
    android:layout_height="140dp"
    android:background="@android:color/white"
    app:horizontalLineLength="300dp"
    app:leftText="AA"
    app:leftTextColor="#FF0000"
    app:lineColor="# 009999"
    app:lineStrokeWidth="2dp"
    app:maxSize="31sp"
    app:minSize="15sp"
    app:middleText="Standard 1"
    app:middleTextColor="#00FF00"
    app:rightText="AAA"
    app:rightTextColor="#0000FF"
    app:sliderColor="@android:color/white"
    app:sliderRadius="13dp"
    app:sliderShadowColor="#FF0000"
    app:standardGrade="3"
    app:totalGrade="9"
    app:verticalLineLength="15dp" />
Copy the code

Results the following

It is highly recommended

RxHttp a chain to send a request, a new generation of Http request magic (1)

RxLife is a lightweight Java lifecycle management library.

Android has the most elegant implementation of file upload, download and progress monitoring

Finally, release the source code

/** * Font size control * User: LJX * Date: 2018/05/11 * Time: 09:53 */
public class FontResizeView extends View {

    // Default line color
    private static final int DEFAULT_LINE_COLOR = Color.parseColor("# 222222");

    private boolean isCoincide;// Whether it is overlapped

    private int width, height;/ / FontAdjustView wide high
    private float minSize;// Minimum font size
    private float maxSize;// Maximum font size
    private float standardSize;// Standard font size

    private String leftText;   // Left text
    private String middleText; // Intermediate text
    private String rightText;  // Right side text

    private int leftTextColor;     // Left text color
    private int middleTextColor;   // Middle text color
    private int rightTextColor;    // Right side text color

    private int totalGrade;             // Total level
    private int standardGrade;          // Standard level
    private int lineColor;              // Line color
    private int horizontalLineLength;   // The length of the horizontal segment
    private int verticalLineLength;     // The length of the longitudinal line segment
    private int lineStrokeWidth;        // Line width
    private int lineAverageWidth;       // The length of each horizontal line

    private int   sliderGrade;       // Slider level
    private int   sliderColor;       // Slider color
    private int   sliderShadowColor; // Slider shadow color
    private Point sliderPoint;       // Slider position

    private Paint                mPaint;/ / brush
    private Line                 mHorizontalLine;   // a horizontal line
    private Line[]               mVerticalLines;    / / n vertical lines
    private GestureDetector      mGestureDetector;  // Gesture detection
    private OnFontChangeListener onFontChangeListener; // Font size changes listener

    public FontResizeView(Context context) {
        this(context, null);
    }

    public FontResizeView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public FontResizeView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        int padding = dp2px(35);
        setPadding(padding, padding, padding, padding);
        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.FontResizeView);
        minSize = ta.getDimension(R.styleable.FontResizeView_minSize, dp2px(15));
        maxSize = ta.getDimension(R.styleable.FontResizeView_maxSize, dp2px(25));
        totalGrade = ta.getInt(R.styleable.FontResizeView_totalGrade, 6);
        standardGrade = ta.getInt(R.styleable.FontResizeView_standardGrade, 2);
        if (standardGrade < 1 || standardGrade > 6) {
            standardGrade = 1;
        }
        sliderGrade = standardGrade;

        leftText = ta.getString(R.styleable.FontResizeView_leftText);
        if (TextUtils.isEmpty(leftText)) leftText = "A";
        middleText = ta.getString(R.styleable.FontResizeView_middleText);
        if (TextUtils.isEmpty(middleText))
            middleText = context.getString(R.string.font_resize_standard);
        rightText = ta.getString(R.styleable.FontResizeView_rightText);
        if (TextUtils.isEmpty(rightText)) rightText = "A";

        leftTextColor = ta.getColor(R.styleable.FontResizeView_leftTextColor, Color.BLACK);
        middleTextColor = ta.getColor(R.styleable.FontResizeView_middleTextColor, Color.BLACK);
        rightTextColor = ta.getColor(R.styleable.FontResizeView_rightTextColor, Color.BLACK);

        lineColor = ta.getColor(R.styleable.FontResizeView_lineColor, DEFAULT_LINE_COLOR);
        lineStrokeWidth = ta.getDimensionPixelOffset(R.styleable.FontResizeView_lineStrokeWidth, dp2px(0.5 f));
        horizontalLineLength = ta.getDimensionPixelOffset(R.styleable.FontResizeView_horizontalLineLength, -1);
        verticalLineLength = ta.getDimensionPixelOffset(R.styleable.FontResizeView_verticalLineLength, -1);

        sliderColor = ta.getColor(R.styleable.FontResizeView_sliderColor, Color.WHITE);
        sliderShadowColor = ta.getColor(R.styleable.FontResizeView_sliderShadowColor, Color.GRAY);
        float sliderRadius = ta.getDimension(R.styleable.FontResizeView_sliderRadius, dp2px(25));
        ta.recycle();

        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.FILL);

        width = getResources().getDisplayMetrics().widthPixels;
        height = dp2px(140);

        standardSize = (maxSize - minSize) / (totalGrade - 1) * (standardGrade - 1) + minSize;

        mHorizontalLine = new Line();
        mVerticalLines = new Line[totalGrade];
        for (int i = 0; i < mVerticalLines.length; i++) {
            mVerticalLines[i] = new Line();
        }

        sliderPoint = new Point(sliderRadius);
        mGestureDetector = new GestureDetector(context, gestureListener);
    }

    GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener() {

        @Override
        public boolean onDown(MotionEvent e) {
            isCoincide = sliderPoint.coincide(e.getX(), e.getY());
            getParent().requestDisallowInterceptTouchEvent(true);
            return super.onDown(e);
        }

        /** * click event */
        @Override
        public boolean onSingleTapUp(MotionEvent e) {
            final Line horizontalLine = mHorizontalLine;
            float x = e.getX();
            if (x > horizontalLine.stopX) {
                x = horizontalLine.stopX;
            } else if (x < horizontalLine.startX) {
                x = horizontalLine.startX;
            }
            moveSlider(x - horizontalLine.startX, true);
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            getParent().requestDisallowInterceptTouchEvent(true);
            if (isCoincide) {
                float x = sliderPoint.getX();
                setSliderPointX(x - distanceX, false);
                postInvalidate();
                return true;
            }
            return super.onScroll(e1, e2, distanceX, distanceY); }};@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int specWidthSize = MeasureSpec.getSize(widthMeasureSpec);
        int specWidthMode = MeasureSpec.getMode(widthMeasureSpec);

        int specHeightSize = MeasureSpec.getSize(heightMeasureSpec);
        int specHeightMode = MeasureSpec.getMode(heightMeasureSpec);

        switch (specWidthMode) {
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:
                width = Math.min(width, specWidthSize);
                break;
            case MeasureSpec.EXACTLY:
                width = specWidthSize;
                break;
        }
        switch (specHeightMode) {
            case MeasureSpec.UNSPECIFIED:
            case MeasureSpec.AT_MOST:
                height = Math.min(height, specHeightSize);
                break;
            case MeasureSpec.EXACTLY:
                height = specHeightSize;
                break;
        }
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (horizontalLineLength == -1)
            horizontalLineLength = w - getPaddingLeft() - getPaddingRight();
        if (verticalLineLength == -1) verticalLineLength = dp2px(10);

        lineAverageWidth = horizontalLineLength / (totalGrade - 1);
        // Initialize the starting position of the horizontal line
        int horizontalLineStartX = (width - horizontalLineLength) / 2;
        int horizontalLineStartY = (int) (height * 0.6);
        // Initialize the starting and ending positions of the horizontal line
        mHorizontalLine.set(horizontalLineStartX, horizontalLineStartY, horizontalLineStartX + horizontalLineLength, horizontalLineStartY);
        float lineAverageWidth = horizontalLineLength * 1.0 f / (totalGrade - 1);
        final Line[] verticalLines = mVerticalLines;
        for (int i = 0; i < verticalLines.length; i++) {
            float startX = horizontalLineStartX + lineAverageWidth * i;
            verticalLines[i].set(startX, horizontalLineStartY - verticalLineLength / 2f, startX, horizontalLineStartY + verticalLineLength / 2f);
        }
        // Initialize the level and position of the slider
        sliderPoint.setGrade(sliderGrade - 1);
        setSliderPointX(verticalLines[sliderGrade - 1].startX, true);
        sliderPoint.setY(verticalLines[sliderGrade - 1].startY + verticalLines[sliderGrade - 1].getHeight() / 2);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        final Line horizontalLine = mHorizontalLine;
        mPaint.setColor(lineColor);
        mPaint.setStrokeWidth(lineStrokeWidth);

        // Draw a horizontal line
        canvas.drawLine(horizontalLine.startX, horizontalLine.startY, horizontalLine.stopX, horizontalLine.stopY, mPaint);

        // Draw a line segment
        for (Line line : mVerticalLines) {
            canvas.drawLine(line.startX, line.startY, line.stopX, line.stopY, mPaint);
        }

        // Draw the left text
        mPaint.setColor(leftTextColor);
        mPaint.setTextSize(minSize);
        float width = mPaint.measureText(leftText);
        float startY = horizontalLine.startY - dp2px(20);
        canvas.drawText(leftText, horizontalLine.startX - width / 2, startY, mPaint);

        // Draw the text on the right
        mPaint.setColor(rightTextColor);
        mPaint.setTextSize(maxSize);
        width = mPaint.measureText(rightText);
        canvas.drawText(rightText, horizontalLine.stopX - width / 2, startY, mPaint);

        // Draw intermediate text
        mPaint.setColor(middleTextColor);
        mPaint.setTextSize(standardSize);
        width = mPaint.measureText(middleText);
        float startX = mVerticalLines[standardGrade - 1].startX - width / 2;
        if (standardGrade == 1 || standardGrade == totalGrade) {
            startY -= dp2px(7) + standardSize;
        }
        canvas.drawText(middleText, startX, startY, mPaint);

        // Draw the slider
        mPaint.setColor(sliderColor);
        float radius = sliderPoint.getRadius();
        mPaint.setShadowLayer(10f.2.2, sliderShadowColor);
        canvas.drawCircle(sliderPoint.getX(), sliderPoint.getY(), radius, mPaint);
        mPaint.setShadowLayer(0.0.0, sliderShadowColor);// Close shadows
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mGestureDetector.onTouchEvent(event)) {
            return true;
        }
        if (event.getAction() == MotionEvent.ACTION_UP && isCoincide) {
            // If the slider is not on the hour, move it to the hour
            float x = sliderPoint.getX() - mHorizontalLine.startX;
            moveSlider(x, false);
        }
        return true;
    }

    /** * move the slider */
    private void moveSlider(float destX, final boolean isClick) {
        int grade = (int) destX / lineAverageWidth;// Target level
        float remainder = destX % lineAverageWidth;
        if (remainder > lineAverageWidth / 2) grade++;

        final int tempGrade = grade;
        int gradeDiffer = Math.abs(sliderPoint.getGrade() - tempGrade);
        if (gradeDiffer == 0) {
            if (isClick) return;
            gradeDiffer = 1;
        }
        ValueAnimator animator = ValueAnimator.ofFloat(sliderPoint.getX(), mVerticalLines[tempGrade].startX);
        animator.setDuration(100 + gradeDiffer * 30);
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.addUpdateListener(new AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float value = (Float) animation.getAnimatedValue();
                // If the movement event is triggered by a click, only the position of the slider will be updated before the animation ends, and then the level of the slider will be updated after the animation endssetSliderPointX(value, isClick); postInvalidate(); }}); animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                setSliderPointX(mVerticalLines[tempGrade].startX, false); }}); animator.start(); }/** * sets the uniform entry of the slider's X coordinates **@paramOnlySetX only updates the position of the slider */
    private void setSliderPointX(float x, boolean onlySetX) {
        float horizontalLineStartX = mHorizontalLine.startX;
        float horizontalLineStopX = mHorizontalLine.stopX;
        if (x < horizontalLineStartX) {
            x = horizontalLineStartX;
        } else if (x > horizontalLineStopX) {
            x = horizontalLineStopX;
        }
        sliderPoint.setX(x);
        if (onlySetX) return;

        int oldGrade = sliderPoint.getGrade();
        int newGrade = (int) (x - horizontalLineStartX) / lineAverageWidth;
        if (oldGrade == newGrade) return;
        sliderPoint.setGrade(newGrade);

        if(onFontChangeListener ! =null) {
            float size = (maxSize - minSize) / (totalGrade - 1);
            floatsp = (minSize + size * newGrade) / getResources().getDisplayMetrics().scaledDensity; onFontChangeListener.onFontChange(sp); }}/ * * *@returnReturns the current font size unit: sp */
    public float getFontSize(a) {
        float size = (maxSize - minSize) / (totalGrade - 1);
        return (minSize + size * (sliderGrade - 1)) / getResources().getDisplayMetrics().scaledDensity;
    }

    /** * Sets the current font size **@paramFontSize fontSize unit: sp */
    public void setFontSize(float fontSize) {
        fontSize *= getResources().getDisplayMetrics().scaledDensity;
        int grade = (int) ((fontSize - minSize) / ((maxSize - minSize) / (totalGrade - 1))) + 1;
        setSliderGrade(grade);
    }

    /** * Set slider level **@paramGrade Indicates the slider grade */
    public void setSliderGrade(int grade) {
        if (grade < 0) grade = 1;
        if (grade > totalGrade) grade = totalGrade;
        sliderGrade = grade;
    }

    private int dp2px(float dipValue) {
        final float scale = getContext().getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5 f);
    }

    public void setOnFontChangeListener(OnFontChangeListener onFontChangeListener) {
        this.onFontChangeListener = onFontChangeListener;
    }

    public interface OnFontChangeListener {
        / * * *@paramFontSize fontSize sp */
        void onFontChange(float fontSize);
    }

    class Point {
        // Slider position and radius
        float x;
        float y;
        float radius;

        int grade; // Slider level

        Point(float radius) {
            this.radius = radius;
        }

        float getX(a) {
            return x;
        }

        void setX(float x) {
            this.x = x;
        }

        float getY(a) {
            return y;
        }

        void setY(float y) {
            this.y = y;
        }

        float getRadius(a) {
            return radius;
        }

        int getGrade(a) {
            return grade;
        }

        void setGrade(int grade) {
            this.grade = grade;
        }

        // Determine whether the point pressed by the finger coincides with the slider
        boolean coincide(float movingX, float movingY) {
            If the distance between two points is less than the specified radius r, we define it as coincidence
            return Math.sqrt((x - movingX) * (x - movingX)
                    + (y - movingY) * (y - movingY)) < radius + dp2px(20); }}class Line {

        float startX;
        float startY;
        float stopX;
        float stopY;

        void set(float startX, float startY, float stopX, float stopY) {
            this.startX = startX;
            this.startY = startY;
            this.stopX = stopX;
            this.stopY = stopY;
        }

        float getHeight(a) {
            returnMath.abs(stopY - startY); }}}Copy the code

This article is originally published. If reprinted, please indicate the source, 🙏🙏.