android自定义View----文字部分渐变效果

今天做的是一个简单支持文字部分渐变效果的控件,还是先放上成果:


loading

tab

如上图,这个控件可以做特殊的loading动画,比如下载、上传、等,也可以用在viewpager切换时的tab,实现文字部分变色等。

  • 实现原理:

  • 拿到文字,先把它渲染在画布上,作为底色

  • 然后对画布进行矩形裁剪clipRect(),paint换一种颜色,再把文字绘制一遍,即可

  • 裁剪的尺寸是根据外部传入的progress、渐变方向、以及文字的宽高确定

    • 先铺代码:

attrs.xml 比较简单,定义两种对比的颜色、文字大小和文字内容 :

<declare-styleable name="ShadeTextView">
        <attr name="text" format="string"/>
        <attr name="firstColor" format="color"/>
        <attr name="secondColor" format="color"/>
        <attr name="textSize" format="dimension"/>
    </declare-styleable>
  • 写一个view的子类,(其实继承TextView更方便一点)

全局变量

    private int mDirection ; //渐变的方向

    public static final int DIRECTION_LEFT = 0;
    public static final int DIRECTION_RIGHT = 1;
    public static final int DIRECTION_TOP = 2;
    public static final int DIRECTION_BOTTOM = 3;

    public void setDirection(int direction) {
        mDirection = direction;
        postInvalidate();
    }

    private Paint mPaint;

    private String mText;//显示的文字

    private int mTextSize;//文字大小

    private float mProgress;//渐变位置

    private int mFirstColor;//base文字颜色

    private int mSecondColor;//变化的文字颜色

    public void setmProgress(float mProgress) {
        this.mProgress = mProgress;
        postInvalidate();
    }

构造器,我的千篇一律的写法~~

    public ShadeTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ShadeTextView, 0, defStyleAttr);

        for (int i = 0; i < typedArray.length(); i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.ShadeTextView_text:
                    mText = typedArray.getString(attr);
                    break;
                case R.styleable.ShadeTextView_firstColor:
                    mFirstColor = typedArray.getColor(attr, Color.BLACK);
                    break;
                case R.styleable.ShadeTextView_secondColor:
                    mSecondColor = typedArray.getColor(attr, Color.BLUE);
                    break;
                case R.styleable.ShadeTextView_textSize:
                    mTextSize = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
                /*case R.styleable.ShadeTextView_progress:
                    mProgress = typedArray.getInteger(attr, 30);
                    break;*/
            }
        }
        typedArray.recycle();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(mTextSize);
    }

onMeasure()的处理,如果继承TextView的话可以省略很多代码。。。

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        int width = 0, height = 0;

        switch (specMode) {
            case MeasureSpec.EXACTLY:
                width = specSize + getPaddingRight() + getPaddingLeft();
                break;

            case MeasureSpec.AT_MOST:
                width = (int) (mPaint.measureText(mText) + getPaddingLeft() + getPaddingRight());//先确定mPaint是否已经设置textsize
                break;
        }

        specMode = MeasureSpec.getMode(heightMeasureSpec);
        specSize = MeasureSpec.getSize(heightMeasureSpec);
        switch (specMode) {
            case MeasureSpec.EXACTLY:
                height = specSize + getPaddingTop() + getPaddingBottom();
                break;
            case MeasureSpec.AT_MOST:
                height = (int) (Math.abs(mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top) + getPaddingTop() + getPaddingBottom());
                break;
        }

        setMeasuredDimension(width, height);

    }

重头戏 onDraw(),本篇的思想精华所在。

    @Override
    protected void onDraw(Canvas canvas) {
        float textWidth = mPaint.measureText(mText);
        float mLeft = (getMeasuredWidth() - textWidth) / 2;
        float textHeight = Math.abs(mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top);
        float mTop = (getMeasuredHeight() - textHeight) /2 ;

        //先画底层的文字
        mPaint.setColor(mFirstColor);
        canvas.drawText(mText, mLeft, getY(), mPaint);
      
        mPaint.setColor(mSecondColor);
        // 接着根据传入的渐变方向、颜色等来裁剪,接着在同样的位置重新渲染文字
        if (mDirection == DIRECTION_LEFT) {
            canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(mLeft, 0, textWidth * mProgress + mLeft, getMeasuredHeight());
            canvas.drawText(mText, mLeft, getY(), mPaint);
            canvas.restore();
        } else if (mDirection == DIRECTION_RIGHT){
            canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(textWidth - textWidth * mProgress + mLeft , 0 , textWidth + mLeft, getMeasuredHeight());
            canvas.drawText(mText, + mLeft, getY(), mPaint);
            canvas.restore();
        } else if (mDirection == DIRECTION_TOP){
            canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(0, mTop , getWidth(), textHeight * mProgress + mTop);
            canvas.drawText(mText, + mLeft, getY(), mPaint);
            canvas.restore();
        }else if (mDirection == DIRECTION_BOTTOM){
            canvas.save(Canvas.CLIP_SAVE_FLAG);
            canvas.clipRect(0, textHeight - textHeight * mProgress + mTop, getWidth(), textHeight + mTop );
            canvas.drawText(mText, + mLeft, getY(), mPaint);
            canvas.restore();
        }
    }
    public float getY() {
        Paint.FontMetricsInt fm = mPaint.getFontMetricsInt();
        return (getHeight() + fm.descent - fm.ascent) / 2 - fm.descent;
    }

值得说明的是,
float textHeight = Math.abs(mPaint.getFontMetrics().bottom - mPaint.getFontMetrics().top);
return (getHeight() + fm.descent - fm.ascent) / 2 - fm.descent;
这里借鉴了Android 自定义View-怎么绘制居中文本?的研究成果,怒学。

  • View基本写完,可以先测试了,先用seekbar测试四个方向的渐变效果,代码比较简单,只贴一段根据seekbar的progress值设置我们自定义View的渐变方向和渐变位置的代码:

 @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
                shadeTextView.setDirection(ShadeTextView.DIRECTION_TOP);
                shadeTextView.setmProgress(progress * 1.0f / 100);
            }

运行测试,基本OK


运用到viewpager中的代码也比较简单,只贴核心调用部分,其他大家都很熟悉了。

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
       if (positionOffset > 0){

            ShadeTextView left = shadeTextViews.get(position);
            ShadeTextView right = shadeTextViews.get(position + 1);

            left.setDirection(1);
            right.setDirection(0);

            left.setmProgress(1-positionOffset);
            right.setmProgress(positionOffset);
      }
}

然后效果就是文首的viewpager切换效果了。

源码点击查看

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

推荐阅读更多精彩内容

  • 1: 获取控件宽高 控件View有getHeight()和getwidth()方法可以获取宽高,但是如果直接在on...
    自由人是工程师阅读 1,773评论 0 0
  • 效果图如下: 一。view的组成 看到上图效果,大概可以确定这个view由三部分组成 1.绕一圈的正方形 2.中间...
    小郑阅读 391评论 0 0
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,907评论 25 707
  • ……即使是明显爱孩子的家长也不能永远做到行为上尊重孩子……他们无视孩子的要求、怠慢孩子生气时的感受、轻视孩子的恐惧...
    球爸小新阅读 303评论 0 1
  • 每个人都有自己的归宿,干着自己的喜欢而且收入不错的工作,暇时去过过夜生活,不浮夸,也不过度小资,平静而又热情。然而...
    午夜的星星阅读 179评论 0 0