Android 仿微信/支付宝 字体大小 调整控件

FontResize

FontResize是一个仿微信/支付宝 字体大小 调整控件的库

gradle依赖

implementation 'com.ljx.view:fontresize:1.0.1'

源码下载 该库里面只有一个类FontResizeView,故不用担心库很大。

效果图

image

属性说明

<declare-styleable name="FontResizeView">
    <attr name="minSize" format="reference|dimension" /> <!--最小size-->
    <attr name="maxSize" format="reference|dimension" /> <!--最大size-->
    <attr name="totalGrade" format="integer" />   <!--可调整的等级、即总的等级-->
    <attr name="standardGrade" format="integer" />  <!--标准等级-->

    <attr name="leftText" format="reference|string" />  <!--左边文本,默认为 'A'-->
    <attr name="middleText" format="reference|string" />  <!--中间文本,默认为 '标准'-->
    <attr name="rightText" format="reference|string" />  <!--右边文本,默认为 'A'-->
    <attr name="leftTextColor" format="reference|color" />  <!--左边文本颜色,默认为全黑-->
    <attr name="middleTextColor" format="reference|color" />  <!--中间文本颜色,默认为全黑-->
    <attr name="rightTextColor" format="reference|color" />  <!--右边文本颜色,默认为全黑-->

    <attr name="sliderRadius" format="reference|dimension" />  <!--滑块半径-->
    <attr name="sliderColor" format="reference|color" />  <!--滑块颜色-->
    <attr name="sliderShadowColor" format="reference|color" />  <!--滑块边缘阴影颜色-->

    <attr name="lineColor" format="reference|color" />  <!--线条颜色-->
    <attr name="lineStrokeWidth" format="reference|dimension" />  <!--线条宽度,即粗细程度-->
    <attr name="horizontalLineLength" format="reference|dimension" />  <!--横向线条长度-->
    <attr name="verticalLineLength" format="reference|dimension" />  <!--纵向线条长度-->

</declare-styleable>                                                                                                          

xml配置

<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="7" />

设置回调

FontResizeView fontResizeView = findViewById(R.id.font_resize_view);
fontResizeView.setOnFontChangeListener(new OnFontChangeListener() {
    @Override
    public void onFontChange(float fontSize) {
        //字体size改变回调  单位:sp
    }
});

这样配置过后,代码跑起来效果就跟上面的 gif 动图一样

我们把所有属性都加上,看看效果

<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="标准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" />

效果如下


在这里插入图片描述

相信以上属性已经能满足大部分场景,如果不能满足您的场景,请提出您的需求,我会第一时间回应您。

强烈推荐

RxHttp 一条链发送请求,新一代Http请求神器(一)

RxLife 一款轻量级别的RxJava生命周期管理库(一)

Android 史上最优雅的实现文件上传、下载及进度的监听

最后,放出源码

/**
 * 字体大小调整控件
 * User: ljx
 * Date: 2018/05/11
 * Time: 09:53
 */
public class FontResizeView extends View {

    //默认线条颜色
    private static final int DEFAULT_LINE_COLOR = Color.parseColor("#222222");

    private boolean isCoincide;//是否重合

    private int width, height;//FontAdjustView的宽高
    private float minSize;//最小字体大小
    private float maxSize;//最大字体大小
    private float standardSize;//标准字体大小

    private String leftText;   //左边文本
    private String middleText; //中间文本
    private String rightText;  //右边文本

    private int leftTextColor;     //左边文本颜色
    private int middleTextColor;   //中间文本颜色
    private int rightTextColor;    //右边文本颜色

    private int totalGrade;             //总的等级
    private int standardGrade;          //标准等级
    private int lineColor;              //线条颜色
    private int horizontalLineLength;   //横向线段长度
    private int verticalLineLength;     //纵向线段长度
    private int lineStrokeWidth;        //线条宽度
    private int lineAverageWidth;       //每段水平线条的长度

    private int   sliderGrade;       //滑块等级
    private int   sliderColor;       //滑块颜色
    private int   sliderShadowColor; //滑块阴影颜色
    private Point sliderPoint;       //滑块位置

    private Paint                mPaint;//画笔
    private Line                 mHorizontalLine;   //一条横线
    private Line[]               mVerticalLines;    //n条竖线
    private GestureDetector      mGestureDetector;  //手势检测
    private OnFontChangeListener onFontChangeListener; //字体size改变监听器

    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.5f));
        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);
        }

        /**
         * 单击事件
         */
        @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);
        //初始化横线起点位置
        int horizontalLineStartX = (width - horizontalLineLength) / 2;
        int horizontalLineStartY = (int) (height * 0.6);
        //初始化横线起点、终点位置
        mHorizontalLine.set(horizontalLineStartX, horizontalLineStartY, horizontalLineStartX + horizontalLineLength, horizontalLineStartY);
        float lineAverageWidth = horizontalLineLength * 1.0f / (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);
        }
        //初始化滑块的等级及位置
        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);

        //绘制横线
        canvas.drawLine(horizontalLine.startX, horizontalLine.startY, horizontalLine.stopX, horizontalLine.stopY, mPaint);

        //绘制线段
        for (Line line : mVerticalLines) {
            canvas.drawLine(line.startX, line.startY, line.stopX, line.stopY, mPaint);
        }

        //绘制左边文本
        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);

        //绘制右边文本
        mPaint.setColor(rightTextColor);
        mPaint.setTextSize(maxSize);
        width = mPaint.measureText(rightText);
        canvas.drawText(rightText, horizontalLine.stopX - width / 2, startY, mPaint);

        //绘制中间文本
        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);

        //绘制 滑块
        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);//关闭阴影
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (mGestureDetector.onTouchEvent(event)) {
            return true;
        }
        if (event.getAction() == MotionEvent.ACTION_UP && isCoincide) {
            //手指抬起时,滑块不在整点,则移动到整点
            float x = sliderPoint.getX() - mHorizontalLine.startX;
            moveSlider(x, false);
        }
        return true;
    }

    /**
     * 移动滑块
     */
    private void moveSlider(float destX, final boolean isClick) {
        int grade = (int) destX / lineAverageWidth;//目标等级
        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();
                //如果是单击触发的移动事件,动画未结束前仅更新滑块的位置,动画结束后再更新滑块的等级
                setSliderPointX(value, isClick);
                postInvalidate();
            }
        });
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                setSliderPointX(mVerticalLines[tempGrade].startX, false);
            }
        });
        animator.start();
    }

    /**
     * 设置滑块X坐标的统一入口
     *
     * @param onlySetX 是否仅仅更新滑块的位置
     */
    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);
            float sp = (minSize + size * newGrade) / getResources().getDisplayMetrics().scaledDensity;
            onFontChangeListener.onFontChange(sp);
        }
    }

    /**
     * @return 返回当前设置的字体大小  单位 :sp
     */
    public float getFontSize() {
        float size = (maxSize - minSize) / (totalGrade - 1);
        return (minSize + size * (sliderGrade - 1)) / getResources().getDisplayMetrics().scaledDensity;
    }

    /**
     * 设置当前字体大小
     *
     * @param fontSize 字体大小 单位 :sp
     */
    public void setFontSize(float fontSize) {
        fontSize *= getResources().getDisplayMetrics().scaledDensity;
        int grade = (int) ((fontSize - minSize) / ((maxSize - minSize) / (totalGrade - 1))) + 1;
        setSliderGrade(grade);
    }

    /**
     * 设置滑块等级
     *
     * @param 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.5f);
    }

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

    public interface OnFontChangeListener {
        /**
         * @param fontSize 字体大小、单位sp
         */
        void onFontChange(float fontSize);
    }

    class Point {
        //滑块位置及半径
        float x;
        float y;
        float radius;

        int grade; //滑块等级

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

        float getX() {
            return x;
        }

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

        float getY() {
            return y;
        }

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

        float getRadius() {
            return radius;
        }

        int getGrade() {
            return grade;
        }

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

        //判断手指按下的点是否与滑块重合
        boolean coincide(float movingX, float movingY) {
            //开方,如果两点之间的距离小于规定的半径r则定义为重合
            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() {
            return Math.abs(stopY - startY);
        }
    }
}

本文为原创文章,如需转载,请注明出处,🙏🙏。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342

推荐阅读更多精彩内容

  • Swift1> Swift和OC的区别1.1> Swift没有地址/指针的概念1.2> 泛型1.3> 类型严谨 对...
    cosWriter阅读 11,082评论 1 32
  • 第一部分 HTML&CSS整理答案 1. 什么是HTML5? 答:HTML5是最新的HTML标准。 注意:讲述HT...
    kismetajun阅读 27,400评论 1 45
  • 请允许我借鉴前辈们的东西~~~~ 感激不尽~~~~~ 以下为Android 框架排行榜 么么哒~ Android...
    嗯_新阅读 1,993评论 3 32
  • 文章转自:http://gank.io/post/560e15be2dca930e00da1083作者:扔物线在正...
    xpengb阅读 7,017评论 9 73
  • 平行而近,平行而尽。
    阿默K阅读 120评论 0 0