Android 自定义View-图片文字变色,实现酷炫LoadingView或者进度条

大概半年之前,看过鸿洋大神的一篇博客
Android 自定义控件玩转字体变色 打造炫酷ViewPager指示器
他说大概想了32秒就知道了实现思路,这深深的刺痛了我。最近又看了一遍,决定做点什么

我要自定义的控件是一个盖世英雄,
它不仅仅是一个Loading控件,同时还支持进度条 (ProgressBar)功能 。
它会在你需要的时候出现,
它支持 lefttoprightbottom 四个方向加载(变色),最重要的是,它可以是 文字,也可以是 图片,能够满足开发者一切需求。

如果你想用它来做LoadingView(图片文字都可以,下面用图片演示)

这里写图片描述

这是你想要的效果吗?限制太死?不会!你可以:

  1. 设置重新从底部加载,而不是从顶部折返
  2. 设置动画时间
  3. 设置是否重复执行

如果你想做进度条ProgressBar(图片文字都可以,下面用文字演示)

这里写图片描述

怎么样?看完效果是不是觉得还不错呢?

那么这样一个实用又酷炫的自定义控件到底有多难呢?

实现RLoadView

代码写到一半的时候我忽然理解了鸿洋的那个32秒。这样的控件,简单到爆。

原理:其实就是使用两种不同的颜色绘制两遍文字,通过裁剪画布控制两种颜色的展示

那么重点就是一个方法啦,裁剪画布 canvas.clipRect。知道这个方法的人简直小菜一碟。难怪鸿洋32秒搞定。

不过有一点还是值得注意的:绘制居中文字

绘制居中文字

凡事由简单到复杂。先实现文字类型的功能,之后再加一个图片功能,就几行代码的事情。

为什么说文字居中需要注意呢?以前学习的第一个自定义View应该就是从绘制文字开始,但是大家可能都没有注意到,网上使用的方式绘制居中文字是有问题的。在宽高设置为 wrap_content,并且不设置 padding 的情况下,文本是不能完整绘制的。

后来就特地去研究了一下。关于文字居中我也顺便写了一篇博客。

Android 自定义View-怎么绘制居中文本?

博客中详细的描述,测试,对比了文本居中绘制的各种情况,为了节省时间,简单总结为以下几点:

宽度测量使用:

int width=mPaint.measureText(mText);

mPaint.measureText(mText)精确度高于mBound.width()

高度测量使用:

FontMetrics fontMetrics = mPaint.getFontMetrics();
int height=Math.abs((fontMetrics.bottom - fontMetrics.top));

垂直居中方式:

FontMetricsInt fm = mPaint.getFontMetricsInt();
int startY = getHeight() / 2 - fm.descent + (fm.bottom - fm.top) / 2;

如果不明白的可以看上面那篇文章,很详细说明。

自定义控件

其实一切没多难,孰能生巧,现在写这个自定控件的步骤都腻死了。还是简单带过

  1. 自定义View的属性
  2. 在View的构造方法中获得我们自定义的属性
  3. # 重写onMesure #
  4. 重写onDraw

1. 自定义View的属性

在 /value/attrs.xml 中,

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <attr name="text" format="string" />
    <attr name="text_size" format="dimension" />
    <attr name="text_color_normal" format="color" />
    <attr name="text_color_hightlight" format="color" />
    <attr name="bitmap_src_normal" format="reference" />
    <attr name="bitmap_src_hightlight" format="reference" />
    <attr name="direction">
        <enum name="left" value="1" />
        <enum name="top" value="2" />
        <enum name="right" value="3" />
        <enum name="bottom" value="4" />
    </attr>
    <attr name="load_style">
        <enum name="text" value="1" />
        <enum name="bitmap" value="2" />
    </attr>

    <!-- RLoadView -->
    <declare-styleable name="RLoadView">
        <attr name="text" />
        <attr name="text_size" />
        <attr name="text_color_normal" />
        <attr name="text_color_hightlight" />
        <attr name="direction" />
        <attr name="load_style" />
        <attr name="bitmap_src_normal" />
        <attr name="bitmap_src_hightlight" />
    </declare-styleable>

</resources>

text:文字,文字大小,文字默认颜色,文字高亮颜色
bitmap:默认图片,高亮图片
direction:图片/文字变色的方向,左到右,右到左,上到下,下到上
load_style:加载方式,图片或者文字

2. 在View的构造方法中获得我们自定义的属性

    // 最大值
    private static final float MAX = 100;
    // 系统默认:文字正常时颜色
    private static final int TEXT_COLOR_NORMAL = Color.parseColor("#000000");
    // 系统默认:文字高亮颜色
    private static final int TEXT_COLOR_HIGHLIGHT = Color.parseColor("#FF0000");
    // 绘制方向
    private static final int LEFT = 1, TOP = 2, RIGHT = 3, BOTTOM = 4;
    // 文字样式
    private static final int STYLE_TEXT = 1;
    // 图片样式
    private static final int STYLE_BITMAP = 2;
    // 顺序绘制
    private static final int LOAD_ASC = 0;
    // 反向/降序绘制
    private static final int LOAD_DESC = 1;

    /**
     * 画笔
     */
    private Paint mPaint;

    /**
     * 绘制的范围
     */
    private Rect mBound;

    /**
     * 控件绘制位置起始的X,Y坐标值
     */
    private int mStartX = 0, mStartY = 0;

    /**
     * 文字大小
     */
    private int mTextSize = 16;

    /**
     * 文字正常颜色
     */
    private int mTextColorNormal = TEXT_COLOR_NORMAL;

    /**
     * 文字高亮颜色
     */
    private int mTextColorHighLight = TEXT_COLOR_HIGHLIGHT;

    /**
     * 文字
     */
    private String mText;

    /**
     * 绘制方向
     */
    private int mDirection = LEFT;

    /**
     * 控件风格
     */
    private int mLoadStyle = STYLE_TEXT;

    /**
     * bitmap正常/默认
     */
    private Bitmap mBitmapNormal;

    /**
     * bitmap高亮
     */
    private Bitmap mBitmapHighLight;

    /**
     * loading刻度
     */
    private float mProgress = 0;

    /**
     * 是否正在加载,避免开启多个线程绘图
     */
    private boolean mIsLoading = false;

    /**
     * 是否终止线程运行
     */
    private boolean mCanRun = true;

    /**
     * 加载方式{顺序,反向}
     */
    private int mLoadMode = LOAD_ASC;

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

    public RLoadView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.RLoadView);

        mText = a.getString(R.styleable.RLoadView_text);
        mTextColorNormal = a.getColor(R.styleable.RLoadView_text_color_normal,
                TEXT_COLOR_NORMAL);
        mTextColorHighLight = a.getColor(
                R.styleable.RLoadView_text_color_hightlight,
                TEXT_COLOR_HIGHLIGHT);
        mTextSize = a
                .getDimensionPixelSize(R.styleable.RLoadView_text_size, 16);
        mDirection = a.getInt(R.styleable.RLoadView_direction, LEFT);
        mLoadStyle = a.getInt(R.styleable.RLoadView_load_style, STYLE_TEXT);

        // 获取bitmap
        mBitmapNormal = getBitmap(a, R.styleable.RLoadView_bitmap_src_normal);
        mBitmapHighLight = getBitmap(a,
                R.styleable.RLoadView_bitmap_src_hightlight);

        a.recycle();

        /**
         * 初始化画笔
         */
        mBound = new Rect();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Style.FILL);

        if (mLoadStyle == STYLE_TEXT) {
            mPaint.setTextSize(mTextSize);
            mPaint.getTextBounds(mText, 0, mText.length(), mBound);
        } else if (mLoadStyle == STYLE_BITMAP) {
            mBound = new Rect(0, 0, mBitmapNormal.getWidth(),
                    mBitmapNormal.getHeight());
        }

    }

代码很简单,一眼带过就可以
注意一下文字和图片不同的绘制范围控制(mBound)
在获取图片的时候要考虑到 点9 图的情况,分两种情况获取

3. # 重写onMesure

重写onMesure 方法,重新测量控件的宽高

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int width = onMeasureR(0, widthMeasureSpec);
        int height = onMeasureR(1, heightMeasureSpec);
        setMeasuredDimension(width, height);

    }
    /**
     * 计算控件宽高
     * 
     * @param attr属性
     *            [0宽,1高]
     * @param oldMeasure
     * @author Ruffian
     */
    public int onMeasureR(int attr, int oldMeasure) {
        int newSize = 0;
        int mode = MeasureSpec.getMode(oldMeasure);
        int oldSize = MeasureSpec.getSize(oldMeasure);

        switch (mode) {
        case MeasureSpec.EXACTLY:
            newSize = oldSize;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.UNSPECIFIED:

            float value = 0;

            if (attr == 0) {

                if (mLoadStyle == STYLE_TEXT) {

                    value = mPaint.measureText(mText);

                } else if (mLoadStyle == STYLE_BITMAP) {

                    value = mBound.width();

                }
                // newSize
                newSize = (int) (getPaddingLeft() + value + getPaddingRight());

            } else if (attr == 1) {

                if (mLoadStyle == STYLE_TEXT) {

                    FontMetrics fontMetrics = mPaint.getFontMetrics();
                    value = Math.abs((fontMetrics.bottom - fontMetrics.top));

                } else if (mLoadStyle == STYLE_BITMAP) {

                    value = mBound.height();

                }
                // newSize
                newSize = (int) (getPaddingTop() + value + getPaddingBottom());

            }

            break;
        }

        return newSize;
    }

文字和图片的宽高获取方式不同,需要判断获取。
可以先按照类型为文字的情况一路看下来思路比较清晰,理解了文字类型的绘制,再看图片的更容易接受。

4. 重写onDraw

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        /**
         * X,Y控件居中绘制 <br/>
         * 对于文本居中绘制<br/>
         * 1.mPaint.measureText(mText)精确度高于mBound.width()
         * 2.文字高度测量:Math.abs((fontMetrics.bottom - fontMetrics.top))
         * 3.http://blog.csdn.net/u014702653/article/details/51985821
         */

        if (mLoadStyle == STYLE_TEXT) {

            // 控件高度/2 + 文字高度/2,绘制文字从文字左下角开始,因此"+"

            FontMetricsInt fm = mPaint.getFontMetricsInt();
            mStartY = getMeasuredHeight() / 2 - fm.descent
                    + (fm.bottom - fm.top) / 2;

            mStartX = (int) (getMeasuredWidth() / 2 - mPaint.measureText(mText) / 2);

        } else if (mLoadStyle == STYLE_BITMAP) {

            mStartX = getMeasuredWidth() / 2 - mBound.width() / 2;
            mStartY = getMeasuredHeight() / 2 - mBound.height() / 2;
        }

        onDrawR(canvas);
    }

这段代码说明一下 mStartXmStartY
mStartX:开始绘制文字/图片的X轴起始坐标值,这里要求水平居中
mStartY:开始绘制文字/图片的Y轴起始坐标值,这里要求垂直居中
特别注意:Android中文本会中Y轴是从文字底部开始绘制,
text:mStartY=控件高度/2 + 文字高度/2,绘制文字从文字左下角开始,因此”+”

    /**
     * 绘制文字或者图片
     * 
     * @param canvas
     * @param normalOrHightLight
     *            [0:正常模式,1:高亮模式]
     * @param start
     * @param end
     * @author Ruffian
     */
    protected void onDrawTextOrBitmap(Canvas canvas, int normalOrHightLight,
            int start, int end) {

        canvas.save(Canvas.CLIP_SAVE_FLAG);

        switch (mDirection) {
        case LEFT:
        case RIGHT:

            // X轴画图
            canvas.clipRect(start, 0, end, getMeasuredHeight());

            break;
        case TOP:
        case BOTTOM:

            // Y轴画图
            canvas.clipRect(0, start, getMeasuredWidth(), end);

            break;
        }

        if (mLoadStyle == STYLE_TEXT) {

            // 绘制文字
            if (normalOrHightLight == 0) {
                mPaint.setColor(mTextColorNormal);
            } else {
                mPaint.setColor(mTextColorHighLight);
            }
            canvas.drawText(mText, mStartX, mStartY, mPaint);

        } else if (mLoadStyle == STYLE_BITMAP) {

            // 绘制图片
            if (normalOrHightLight == 0) {
                canvas.drawBitmap(mBitmapNormal, mStartX, mStartY, mPaint);
            } else {
                canvas.drawBitmap(mBitmapHighLight, mStartX, mStartY, mPaint);
            }

        }

        canvas.restore();

    }

绘制文字或者图片的方法,保存当前画布之后进行裁剪画布在绘制文字。
比如说现在需要绘制“一串文字”的后半部分,就把前半部分裁减掉,然后绘制,当恢复画布之后效果就是前面一半是空白,后面一半是文字

    /**
     * 控件绘制
     * 
     * @param canvas
     * @author Ruffian
     */
    public void onDrawR(Canvas canvas) {

        /**
         * 主要思想:绘制两遍文字/图像,通过裁剪画布拼接两部分文字/图像,实现进度绘制的效果
         */

        // 需要变色的宽高总值(长度)
        int drawTotalWidth = 0;
        int drawTotalHeight = 0;

        // X,Y变色的进度实时值
        int spliteXPro = 0;
        int spliteYPro = 0;

        // X,Y变色的最大值(坐标)
        int spliteXMax = 0;
        int spliteYMax = 0;

        // 开始变色的X,Y起始坐标值
        int spliteYStart = 0;
        int spliteXStart = 0;

        FontMetricsInt fm = mPaint.getFontMetricsInt();

        if (mLoadStyle == STYLE_TEXT) {

            drawTotalWidth = (int) mPaint.measureText(mText);
            drawTotalHeight = Math.abs(fm.ascent);

            spliteYStart = (fm.descent - fm.top) - Math.abs(fm.ascent)
                    + getPaddingTop();
            // 开始裁剪的Y坐标值:(http://img.blog.csdn.net/20160721172427552)图中descent位置+paddingTop

            spliteYMax = Math.abs(fm.top) + (fm.descent);
            // Y变色(裁剪)的进度最大值(坐标):(http://img.blog.csdn.net/20160721172427552)看图

        } else if (mLoadStyle == STYLE_BITMAP) {

            drawTotalWidth = mBound.width();
            drawTotalHeight = mBound.height();

            spliteYStart = mStartY;// 开始裁剪的Y坐标值:图片开始绘制的地方
            spliteYMax = mStartY + drawTotalHeight;
            // Y变色(裁剪)的进度最大值(坐标):图片开始绘制的地方+需要变色(裁剪)的高总值(长度)
        }

        spliteXPro = (int) ((mProgress / MAX) * drawTotalWidth);
        spliteYPro = (int) ((mProgress / MAX) * drawTotalHeight);

        spliteXStart = mStartX;// 开始裁剪的X坐标值:文字开始绘画的地方
        spliteXMax = mStartX + drawTotalWidth;
        // X变色(裁剪)的进度最大值(坐标):X变色(裁剪)起始位置+需要变色(裁剪)的宽总值(长度)

        switch (mDirection) {
        case TOP:

            // 从上到下,分界线上边是高亮颜色,下边是原始默认颜色
            onDrawTextOrBitmap(canvas, 1, spliteYStart, spliteYStart
                    + spliteYPro);
            onDrawTextOrBitmap(canvas, 0, spliteYStart + spliteYPro, spliteYMax);

            break;
        case BOTTOM:

            // 从下到上,分界线下边是默认颜色 ,上边是高亮颜色

            onDrawTextOrBitmap(canvas, 0, spliteYStart, spliteYMax - spliteYPro);
            onDrawTextOrBitmap(canvas, 1, spliteYMax - spliteYPro, spliteYMax);

            break;
        case LEFT:

            // 从左到右,分界线左边是高亮颜色,右边是原始默认颜色
            onDrawTextOrBitmap(canvas, 1, spliteXStart, spliteXStart
                    + spliteXPro);
            onDrawTextOrBitmap(canvas, 0, spliteXStart + spliteXPro, spliteXMax);

            break;
        case RIGHT:

            // 从右到左,分界线左边是默认颜色 ,右边是高亮颜色
            onDrawTextOrBitmap(canvas, 0, spliteXStart, spliteXMax - spliteXPro);
            onDrawTextOrBitmap(canvas, 1, spliteXMax - spliteXPro, spliteXMax);

            break;
        }

    }

这个方法是控制裁剪起始和终止坐标值的关键方法。如果看着很晕

Android 自定义View-怎么绘制居中文本?

可以结合这篇博客理解

说一下思路,思路很关键啊,拿笔记好啦。哈哈

文章开头就说了,文字/图片变色原理是使用两种不同的颜色(或者两张图片)绘制两遍文字/图片

先看几个变量

        // 需要变色的宽高总值(长度)
        int drawTotalWidth = 0;
        int drawTotalHeight = 0;

        // X,Y变色的进度实时值
        int spliteXPro = 0;
        int spliteYPro = 0;

        // X,Y变色的最大值(坐标)
        int spliteXMax = 0;
        int spliteYMax = 0;

        // 开始变色的X,Y起始坐标值
        int spliteYStart = 0;
        int spliteXStart = 0;
  • 需要变色的宽度总值(drawTotalWidth ):这里就是文字或者图片自身的宽度
  • X变色的进度实时值(spliteXPro ):表示变色进度的实时值。
    比如变色到50%的时候 spliteXPro = (int) (( 50 / MAX) * drawTotalWidth);
  • X开始变色的起始坐标值(spliteXStart ):记住这是一个坐标值,从哪里开始变色。
  • X变色的最大值(spliteXMax ):表示最多能变色到什么位置 ,记住这是一个坐标值,所以需要加上起始值。
    spliteXMax = spliteXStart + drawTotalWidth;

好好理解这几个概念,接下来就是绘制控件了。

绘制的原理都是一样的,只要理解了,从哪个方向开始变色都是一样的,无非是起始值和结束值的计算。计算之前一定要搞清楚上面的几个概念,不然,呵呵。说句不炫耀的话,写代码的时候,我差点把自己弄晕。

#以从左到右变色为例讲解变色原理

// 从左到右,分界线左边是高亮颜色,右边是原始默认颜色
onDrawTextOrBitmap(canvas, 1, spliteXStart, spliteXStart + spliteXPro);
onDrawTextOrBitmap(canvas, 0, spliteXStart + spliteXPro, spliteXMax);

onDrawTextOrBitmap(canvas, 1, spliteXStart, spliteXStart + spliteXPro);
第一个参数表示画布,
第二个参数表示文本类型(1:高亮,0:默认),
第三个参数变色开始的X值,
第四个参数变色结束X值

先绘制一遍高亮颜色的文字,再绘制一遍默认颜色的。

开始变色 ↓- - - - - - - - - - 变色最大值

高亮颜色从 开始变色的地方,绘制到,某个值停止
默认颜色从 高亮颜色停止的位置,绘制到,变色最大值位置
从progress=0,到progress=100,随着progress增加 高亮颜色慢慢向右递增,默认颜色慢慢递减,形成变色效果
其实就是画布裁剪出一部分来绘制 高亮颜色,然后再裁剪出一部分颜色来绘制 默认颜色。
如果进度停在50%,就会看到一半高亮,一半默认

OK,看看这个自定义类里面都有哪些方法

  1. RLoadView:构造方法,获取基本属性
  2. #onMeasure:重写onMeasure方法计算控件宽高
  3. onMeasureR():自定义方法,计算控件宽高
  4. #onDraw:重写onDraw方法,绘制控件
  5. onDrawR():自定义方法,绘制控件之前计算,处理
  6. onDrawTextOrBitmap:自定义方法,绘制文本/图片逻辑
  7. start:自定义方法,开始执行文本/图片变色
  8. stop:自定义方法,结束文本/图片变色
  9. getProgress:获取进度值
  10. setProgress:设置进度值
  11. getBitmap:获取图片属性,区分 点9图
  12. #onSaveInstanceState:重写方法,保存信息(进度值等)
  13. #onRestoreInstanceState:重写方法,重新设置信息(进度值等)

其中对外提供的方法:startstopgetProgresssetProgress

start

public void start(final long duration, final boolean isRepeat, final boolean isReverse)

start 开始执行loading,使用在 LoadingView 情形中
第一参数:执行时间,设置变色执行的时间
第二个参数:是否循环重新变色,true循环变色,并且重头开始变色
第三个参数:是否反向褪色,true则表示变色完成之后反向褪色
可以自由组合第二第三个参数,实现不同的效果。

stop

public void stop()

停止变色。配合 start 使用,由于start开启子线程实现变色,通过stop停止线程执行,停止变色。

setProgress

public void setProgress(float progress)

设置进度值。使用在ProgressBar 情形中。
在下载文件情形下,实时设置progress,方法会重绘控件,更新进度条。

getProgress

public float getProgress()

获取进度值。更适合使用在 ProgressBar 情形中。
在 LoadingView 情形中获取进度值没有意义,会在0-100之间不断变化。

在xml布局文件中使用控件

    <cn.r.loadview.android.view.RLoadView
        xmlns:rlv="http://schemas.android.com/apk/res-auto"
        android:id="@+id/id_loadView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:layout_gravity="center_horizontal"
        rlv:bitmap_src_hightlight="@drawable/base_hightlight"
        rlv:bitmap_src_normal="@drawable/base_normal"
        rlv:direction="bottom"
        rlv:load_style="bitmap"
        rlv:text="Downloading..."
        rlv:text_color_hightlight="#FF0000"
        rlv:text_color_normal="#000000"
        rlv:text_size="25sp" />

Activity中代码调用

//获取自定义控件
RLoadView mLoadView = (RLoadView) findViewById(R.id.id_loadView);


/**
 * 使用情形1:LoadingView
 */
//变色时间,变色模式,开始LoadingView
mLoadView.start(1500, true, false);
//停止Loading
mLoadView.stop();



/**
 * 使用情形2:ProgressBar
 */
//设置进度值,模拟下载设置下载进度值
mLoadView.setProgress(mProgress);
//获取进度值,模拟下载获取已下载进度值
mLoadView.getProgress();

使用起来从未如此简单,方便。两行代码搞定一切的既视感!关键是效果酷炫!

还在等什么?赶快下载体验吧

源码下载

Github:https://github.com/RuffianZhong/RLoadView


这是一条华丽的分割线


顺便说一下另外一个自定义控件,简单实用的ViewPageIndicator,RVPIndicator

1.什么是 RVPIndicator

简单实用的ViewPageIndicator,支持item自身滚动

高仿MIUI但更胜于MIUI,提供多种指示器类型{下滑线,三角形,全背景}

这里写图片描述

觉得这不满足你的需求?没问题,RVPIndicator 还支持使用图片作为指示器。一张图实现你的愿望

这里写图片描述

不会作图?你想自定义?OK,添加两三行代码就可以增加新的指示器样式

2. RVPIndicator 使用

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:rvp="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ffffffff"
    android:orientation="vertical" >

    <cn.r.vpindicator.android.view.RVPIndicator
        android:id="@+id/id_indicator"
        android:layout_width="match_parent"
        android:layout_height="45dp"
        android:background="#ADD597"
        android:orientation="horizontal"
        rvp:indicator_color="#f29b76"
        rvp:indicator_src="@drawable/heart_love"
        rvp:indicator_style="triangle"
        rvp:item_count="4"
        rvp:text_color_hightlight="#FF0000"
        rvp:text_color_normal="#fb9090" />

    <android.support.v4.view.ViewPager
        android:id="@+id/id_vp"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />
</LinearLayout>

2.1 自定义属性解释

        rvp:indicator_color="#f29b76"               //指示器颜色
        rvp:indicator_src="@drawable/heart_love"    //指示器图片{指示器类型为bitmap时需要}
        rvp:indicator_style="triangle"              //指示器类型
                                                   //{bitmap:图片;line:下划线;square:方形全背景;triangle:三角形}

        rvp:item_count="4"                          //item展示个数
        rvp:text_color_hightlight="#FF0000"         //item文字高亮颜色
        rvp:text_color_normal="#fb9090"             //item文字正常颜色

2.2 代码调用

        // 设置Tab上的标题
        mIndicator.setTabItemTitles(mDatas);
        // 设置关联的ViewPager
        mIndicator.setViewPager(mViewPager, 0);

源码下载

Github:https://github.com/RuffianZhong/RVPIndicator

敢不敢留个言,点个赞,证明真的有人在看,哈哈

本页内容版权归属为原作者,如有侵犯您的权益,请通知我们删除。
首先为权限: uses-permission android:name="android.permission.INTERNET" / uses-permission android:name="com.android.vending.BILLING" / uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" / uses-permission android:name="android.permission.AC
Day01 html与css基础入门 1.html的常见标签和实战 1.1 a标签 a href=#> 1.2 img标签 img src="plmm.jpg" width="100px" height="200px" alt="图片信息丢失!"/// alt属性的值表示当图片找不到时显示的文字信息 1.3 列表标签 ol type="I" start="1" li我是天才1号/li li我是天才2号/li li我是天才3号/li li我是天才4号/li/olul type="circle" li我是逗逼

EventBus 源码分析 - 2016-07-23 14:07:10

0. 前言 EventBus 是一款针对Android优化的发布/订阅事件总线。主要功能是替代 Intent , Handler , BroadCast 在 Fragment , Activity , Service ,线程之间传递消息。优点是开销小,代码更优雅,以及将发送者和接收者解耦。此文将对最新的 EventBus 3.0 的源码进行简要的分析。 1. 用法回顾 EventBus 3.0 的用法较之前的版本有所变化,它使用了最近较为流行的注解形式取代以前的 onEvent 开头作为方法名,但使用步骤

Qt之资源系统 - 2016-07-23 14:07:10

简述 Qt 的资源系统用于存储应用程序的可执行二进制文件,它采用平台无关的机制。当你的程序总需要这样的一系列文件(图标、翻译文件等)并且不想冒丢失某些文件的风险时,这就显得十分有用。 资源系统基于 qmake、rcc(Qt 资源编译器) 和 QFile 之间的紧密合作。 简述 资源集合文件qrc 外部二进制资源 内编译资源 压缩 在程序中使用资源 在库中使用资源 更多参考 资源集合文件(.qrc) 与程序相关的资源在被指定在一个 .qrc 文件中,其基于 XML 的文件格式列出了磁盘上的文件,可以为它们指
一.前提条件 1.纯熟扎实的语言基础   如果你学java,却对反射、泛型、注解一直半解,还是不要去读什么框架了,回去把java基础打扎实反而对你自身更有益。 2.UML能力   在软件工程中,UML在软件的不同生命周期阶段扮演着非常重要的角色,没有好的UML水平,面对大型的项目源码会束手无策。 3.对业务的理解   如果你要阅读的项目业务性比较强,事先对业务有一定的了解是必须的。 4.设计模式、重构的掌握   编程语言什么的没什么好说。着重提一个:设计模式由于Android源代码用到各种各样的设计模式,
在上一篇博文《 LTE下行物理层传输机制(7)-DCI2格式和预编码矩阵的选择 》中已经提到,如果当前UE的传输模式是TM4,且可以执行空分复用(一个PDSCH信道传输2个TB块),那么需要采用DCI2格式来承载控制信息域,使用的预编码矩阵需要参考UE反馈的PMI值,因此属于闭环性质的空分复用。相应的,LTE系统中也有一种开环的空分复用: 如果当前UE的传输模式是TM3,且可以执行空分复用,那么此时PDCCH需要采用DCI2A格式发送,这时的空分复用就属于开环性质的空分复用,不需要参考UE反馈的PMI值。
好久没写android的博客,最近在做一个android的项目,里面用到我们经常用的一个控件就是对话框,大家都知道android自带的对话框是很丑的,android5.x之后除外.所以就出现了自定义view,自己定义美观的对话框.好我们就来自定义对话框. 整体思路:定义一个类然后去继承Dialog类,然后重写相应的构造器方法.大家都知道一般的对话框的创建过程都是来一个AlertDialog.Builder对象,然后使用一些set方法来设置标题内容以及设置一些自定义的view和点击的Button以及相应的点
1.android 的UI线程阻超过5秒就会引发ANR(Application not responding)异常,如果等待超过3秒,你就会失去用户。 2.在android中组件的启动线程被称为主线程(也称UI线程),一般不在这线程中进行耗时的工作,所以我们将线程分为两种,分别是main thread和worker thread,当应用程度运行是时,系统默认的启动线程就是主线程,主要用来加载UI,完成和用户间的交互,所有这些都在同一个线程中进行,所以不能在这个线程中进行耗时工作,不能阻塞UI,androi
接触过自定义控件的开发者一看,笑了,立马关了网页。但是…你真的知道怎么绘制居中文本吗? 我不会?开玩笑,不就是: X=控件宽度/2 - 文本宽度/2;Y=控件高度/2 + 文本宽度/2 好吧,那我试一下。 1.自定义控件基本步骤 自定义View的属性 在View的构造方法中获得我们自定义的属性 #重写onMesure # 重写onDraw OK,简单,直接干起来。 1. 自定义View的属性 按照最简单的来,属性有:文本,文本颜色,文本大小。 我们在 /value/attrs.xml 中这么写: ?xml
首先,自定义控件分为三类: 自定义的组合控件 继承View的自定义控件 继承ViewGroup的自定义控件 在这里,我要写的是第二种,也就是继承自View的自定义控件,第一种自定义的组合控件,我已经写过了,可以在我的博客中可以找到 现在来看一下继承View的自定义控件 首先,需要写一个类继承自View,那么,它也有三个构造方法,有一个参数的构造方法实在代码中new这个自定义控件时被调用;有两个参数的构造方法是在布局中使用这个自定义控件的时候调用,有三个参数的构造方法,实在使用到这个自定义控件的样式时被调用