Android知识梳理之自定义View

虽然android本身给我们提供了形形色色的控件,基本能够满足日常开发的需求,但是面对日益同质化的app界面,和不同的业务需求.我们可能就需要自定义一些View来获得比较好的效果.自定义View是android开发者走向高级开发工程师必须要走的一关.

转载请标明出处:http://blog.csdn.net/unreliable_narrator/article/details/51274264

一,构造函数:

当我们创建一个类去继承View的时候,会要求我们至少去实现一个构造函数.

public
MyView(Context context)    该构造函数是直接在代码里面进行创建控件的时候调用.如我们创建一个MyView myView=new MyView(this)的时候将会调用该函数.

public MyView(Context context, AttributeSet attrs)这个是在xml创建但是没有指定style的时候被调用.多了一个AttributeSet类型的参数,在通过布局文件xml创建一个view时,会把XML内的参数通过AttributeSet带入到View内。

public MyView(Context context, AttributeSet attrs, int defStyleAttr)构造函数中第三个参数是默认的Style,这里的默认的Style是指它在当前Application或Activity所用的Theme中的默认Style,且只有在明确调用的时候才会生效.

@TargetApi(Build.VERSION_CODES.LOLLIPOP)

public MyView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)该构造函数是在api21的时候才添加上的,暂不考虑.

注意:即使你在View中使用了Style这个属性也不会调用三个参数的构造函数,所调用的依旧是两个参数的构造函数。

想要深入了解的详情可以查看这篇文章:http://blog.csdn.net/yuzhouxiang/article/details/6958017
   android view构造函数研究

一般我们构造函数可以写成这样:
 public MyView(Context context) {
        this(context, null);
    }
    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

二.自定义命名空间:

 
             当我们自定义View的时候许多的属性我们当然不希望被写死.例如我自定义了一个圆.这个圆的颜色我更希望不在自定义控件里面写死,而是在使用的时候在布局文件中进         行指定这个颜色的时候.我们就需要用到自定义命名空间来对自定义View的属性进行设置了.

步骤1:首先在Values文件夹里面新建attrs文件.

步骤2.编写attrs文件.attrs有两种写法.

( 1.)针对于单个View自己去定义不同的属性.

例如:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--属性申明必须是自定义控件的名称-->
    <declare-styleable name="MyView">
        <!--采用驼峰命名规则,可以随意命名,format是属性的单位-->
        <attr name="roundColor" format="color"></attr>
    </declare-styleable>
</resources>

( 2.)如果是有公共的属性部分,可以将属性包含在公共属性部分里面.也就是说公共属性可以被多个自定义控件属性样式使用.

例如:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--采用驼峰命名规则,可以随意命名,format是属性的单位-->
    <attr name="roundColor" format="color"></attr>
    <!--属性申明必须是自定义控件的名称-->
    <declare-styleable name="MyView">
        <attr name="roundColor"></attr>
    </declare-styleable>
</resources>

所有的format类型

reference     引用

color            颜色

boolean       布尔值

dimension   尺寸值

float            浮点值

integer        整型值

string          字符串

enum          枚举值

步骤3:在布局文件中队自定义的属性进行使用。

打开布局文件我们可以看到有很多的以xmlns开头的字段。其实这个就是XML name space 的缩写。我们仿照系统定义好的自己来定义一个命名空间。

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:上面说过了是XML name space 的缩写。

app  :是命名空间的名称可随意书写

上方是在android studio里面万能的书写方式,在eclipse里面一般是这样写xmlns:app="http://schemas.android.com/apk/res/com.dapeng.viewdemo"

com.dapeng.viewdemo为本应用的包名.

步骤4.在自定义View中将我们定义好的属性拿到.

通过context.obtainStyledAttributes将构造函数中的attrs进行解析出来,就可以拿到相对应的属性.

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);

mColor = typedArray.getColor(R.styleable.MyView_roundColor, 0XFF00FF00);

需要注意的一点是:在我们获取尺寸的时候有三个函数进行使用,我们来看看他们之间的区别.

getDimension()是基于当前DisplayMetrics进行转换,获取指定资源id对应的尺寸。文档里并没说这里返回的就是像素,要注意这个函数的返回值是float,像素肯定               是int。

getDimensionPixelSize()与getDimension()功能类似,不同的是将结果转换为int,并且小数部分四舍五入。

getDimensionPixelOffset()与getDimension()功能类似,不同的是将结果转换为int,并且偏移转换(offset conversion,函数命名中的offset是这个意思)是直接截              断小数位,即取整(其实就是把float强制转化为int,注意不是四舍五入哦)。

由此可见,这三个函数返回的都是绝对尺寸,而不是相对尺寸(dp\sp等)。如果getDimension()返回结果是20.5f,那么getDimensionPixelSize()返回结果就是                   21,getDimensionPixelOffset()返回结果就是20。

理论知识讲的有点多,可能有点空洞,下面通过一个小的例子,来测试一下我们的命名空间是否可以正常使用.

例子:我们自定义一个View,这个View的形状是一个圆形,并且我们不希望将圆的颜色写死,可以在布局文件中进行设置.

其他的我们都先不管,只是测试一下自定义命名空间.

步骤1.创建一个View继承自View.并且重写它的构造函数     
public class MyView extends View {
    public MyView(Context context) {
        this(context, null);
    }
    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

步骤2:在Values文件夹下面创建一个attrs的文件,写上自定义的属性.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--属性申明必须是自定义控件的名称-->
    <declare-styleable name="MyView">
        <!--采用驼峰命名规则,可以随意命名,format是属性的单位-->
        <attr name="roundColor" format="color"></attr>
        <attr name="radius" format="dimension"></attr>
    </declare-styleable>
</resources>

步骤3:在布局文件中进行引用自定义命名空间.这里给设置的颜色是橘黄色.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.dapeng.viewdemo.MainActivity">
    <com.dapeng.viewdemo.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_red_light"
        app:roundColor="@android:color/holo_blue_bright"
        app:radius="50dp"
        />
</RelativeLayout>

步骤4:在自定义View中设置我们自定义的属性.

public class MyView extends View {
    private int mColor;
    private Paint mP;
    private float mRadius;
    public MyView(Context context) {
        this(context, null);
    }
    public MyView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //拿到自定义属性
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView);
        mColor = typedArray.getColor(R.styleable.MyView_roundColor, 0XFF00FF00);
        mRadius = typedArray.getDimension(R.styleable.MyView_radius, 50);
        //回收资源
        typedArray.recycle();
        //创建画笔
        mP = new Paint();
        //设置画笔颜色
        mP.setColor(mColor);
        //设置抗锯齿
        mP.setAntiAlias(true);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画圆
        canvas.drawCircle(mRadius, mRadius, mRadius, mP);
    }
}

Ok代码书写完毕,我们来看看实现的效果是怎么样的.

如果将颜色设置为蓝色就是如下效果:

    当然,这里只是实现了自定义控件的一小部分功能,接着我们来看看一个问题:

我们将我们自定义控件的background设置为红色来看看效果.这里控件都是设置包裹内容的.  
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.dapeng.viewdemo.MainActivity">
    <com.dapeng.viewdemo.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@android:color/holo_red_light"
        app:roundColor="@android:color/holo_blue_bright"
        />
</RelativeLayout>

效果如下图所示:

看到这个效果可能会有点惊奇了,明明我设置的是包裹内容的,为什么控件确实填充了父窗体?带着这样的疑问我们接下来学习,自定义控件的另外一个非常重要的函数:onmeasure();

onmeuse()方法:测量自己的大小,为正式布局提供建议。(注意,只是建议,至于用不用,要看onLayout); 

            主要的作用就是处理自定义VIewgroup的时候是wrap_content的时候该ViewGrop的大小.

            定义:如果layout_widht和layout_height是match_parent或具体的xxxdp,那就非常简单了,直接调用setMeasuredDimension()方       法,设置ViewGroup的宽高即可.But如果是wrap_content,就比较麻烦了,如果不重写 onMeasure() 方法,系统则会不知道该默认多          大尺寸,就会默认填充整个父布局,所以,重写 onMeasure() 方法的目的,就是为了能够给 View 一个 wrap_content 属性下的默认大        小。

            调用此方法会传进来的两个参数:int widthMeasureSpec, int heightMeasureSpec .他们是父类传递过来给当前view的一个建议值,       即把当前view的尺寸设置为宽widthMeasureSpec,高heightMeasureSpec 虽然表面上看起来他们是int类型的数字,其实他们是由              mode+size两部分组成的。 widthMeasureSpec和heightMeasureSpec转化成二进制数字表示,他们都是30位的。前两位代表mode(测量模   式),后面28位才是他们的实际数值(size)。 

           MeasureSpec.getMode()获取模式

      MeasureSpec.getSize()获取尺寸  

 mode的值有三种为:

        EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值(也就是我们在布局文件中设定的值如50dp)、match_parent时,ViewGroup会将其设置为EXACTLY;

AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;

UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
我们需要判断当布局文件中设置控件为包裹内容的时候,控件的大小的值就可以了.因此重写onmeasure()方法如下:
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //获取测量的模式
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        //获取测量的值
        int withSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        //设置控件的大小
        setMeasuredDimension(widthMode == MeasureSpec.AT_MOST ? (int) mRadius * 2 : withSize, heightMode == MeasureSpec.AT_MOST ? (int) mRadius * 2 : heightSize);
    }

设置成功以后,直接将工程运行起来就可以看到效果了:

     上面的例子都是演示的画圆,如果想画其他的形状应该怎么办呢?我们需要通过重写onDraw() 方法对控件重写进行绘制就可以了.

   Ondraw().

draw就是画的意思从字面意思我们也可以知道.通过重写该方法我们可以对绘制出相关的控件.那么绘制的时候在是在什么上面进行绘制呢?我们    先来重写ondraw()看看:
@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

    }

我们可以看到通过重写ondraw()会传过来一个Canvas类,这个类实际上就是一块儿画布,我们可以创建画笔在上面进行绘图.

Canvas的使用.

            这个类相当于一个画布,你可以在里面画很多东西;

      我们可以把这个Canvas理解成系统提供给我们的一块内存区域(但实际上它只是一套画图的API,真正的内存是下面的              Bitmap),而且它还提供了一整套对这个内存区域进行操作的方法,所有的这些操作都是画图API。也就是说在这种方式

      下我们已经能一笔一划或者使用画笔来画我们所需要的东西了,要画什么要显示什么都由我们自己控制。这种方式根据环       境还分为两种:一种就是使用普通View的canvas画图,还有一种就是使用专门的SurfaceView的canvas来画图。两种的         主要是区别就是可以在SurfaceView中定义一个专门的线程来完成画图工作,应用程序不需要等待View的刷图,提高性            能。前面一种适合处理量比较小,帧率比较小的动画,比如说简单的View样式或者是象棋游戏之类的;而后一种主要用        在游戏,高品质动画方面的画图。

         Canvas可以绘制的对象有:弧线(arcs)、填充颜色(argb和color)、 Bitmap、圆(circle和oval)、点(point)、线(line)、矩形       (Rect)、图片(Picture)、圆角矩形 (RoundRect)、文本(text)、顶点(Vertices)、路径(path)。通过组合这些对象我们可以画出一     些简单有趣的界面出来,但是光有这些功能还是不够的,如果我要画一个仪表盘(数字围绕显示在一个圆圈中)呢? 幸好Android       还提供了一些对Canvas位置转换的方法:rorate、scale、translate、skew(扭曲)等,而且它允许你通过获得它的转换矩阵对象     (getMatrix方法) 直接操作它。这些操作就像是虽然你的笔还是原来的地方画,但是画纸旋转或者移动了,所以你画的东西的方       位就产生变化。为了方便一些转换操作,Canvas 还提供了保存和回滚属性的方法(save和restore),比如你可以先保存目前画纸     的位置(save),然后旋转90度,向下移动100像素后画一些图形,画完后调用restore方法返回到刚才保存的位置.

    画一些比较常见的几何图形:

画圆:canvas.drawCircle()

 canvas.drawCircle(100, 100, 90, paint);   

画弧形:canvas.drawArc();

 //绘制弧线区域
    //先要绘制矩形
    RectF rect = new RectF(0, 0, 100, 100);   

    canvas.drawArc(rect, //弧线所使用的矩形区域大小
            270,  //开始角度
            90, //扫过的角度
            true, //是否使用中心
            paint); //画笔  

颜色填充:canvas.drawColor();

 canvas.drawColor(Color.BLUE);   

画一条线:canvas.drawLine()

canvas.drawLine(10,//x起点位置
                10, //y起点位置
                100, //x终点位置
                100, //y终点位置
                paint); //画笔

画椭圆:

    //定义一个矩形区域
    RectF oval = new RectF(0,0,200,300);
    //矩形区域内切椭圆
    canvas.drawOval(oval, paint);   

画带有弧度的文字:canvas.drawPosText();

//按照既定点 绘制文本内容
    canvas.drawPosText("Android", new float[]{
            10,10, //第一个字母在坐标10,10
            20,20, //第二个字母在坐标20,20
            30,30, //....
            40,40,
            50,50,
            60,60,
            70,70,
    }, paint);   

画矩形:canvas.drawRect();

 RectF rect = new RectF(50, 50, 200, 200);   

        canvas.drawRect(rect, paint);  

画带有弧度的矩形:canvas.drawRoundRect();

  RectF rect = new RectF(50, 50, 200, 200);   

    canvas.drawRoundRect(rect,
                        30, //x轴的半径
                        30, //y轴的半径
                        paint);   

画封闭的图形:

  Path path = new Path(); //定义一条路径
    path.moveTo(10, 10); //移动到 坐标10,10
    path.lineTo(50, 60);
    path.lineTo(200,80);
    path.lineTo(10, 10);   

    canvas.drawPath(path, paint);   

画文字跟随一条线:

  Path path = new Path(); //定义一条路径
            path.moveTo(10, 10); //移动到 坐标10,10
            path.lineTo(50, 60);
            path.lineTo(200,80);
            path.lineTo(10, 10);
            canvas.drawTextOnPath("Android", path, 10, 10, paint);

画图片:

drawBitmap(Bitmap bitmap, float left, float top, Paint paint)

canvas的一些常规的方法:

 canvas.rotate(360 / count,//旋转的角度
                        0f, x轴的坐标
                        0f); //旋转画纸
 canvas.translate(200, 200); //将位置移动画纸的坐标点到x为200,y为200
canvas.save();              //保存画布的状态
canvas.restore();           //取出保存的状态 

canvas.save();和canvas.restore();是两个相互匹配出现的,作用是用来保存画布的状态和取出保存的状态的。这里稍微解释一下,

当我们对画布进行旋转,缩放,平移等操作的时候其实我们是想对特定的元素进行操作,比如图片,一个矩形等,但是当你用canvas的方法来进行这些操作的时候,其实是对整个画布进行了操作,那么之后在画布上的元素都会受到影响,所以我们在操作之前调用canvas.save()来保存画布当前的状态,当操作之后取出之前保存过的状态,这样就不会对其他的元素进行影响.

画笔Paint

  从上面列举的几个Canvas.drawXxx()的方法看到,其中都有一个类型为paint的参数,可以把它理解为一个"画笔",通过这个画笔,在Canvas这张画布上作画。 它位于"android.graphics.Paint"包下,主要用于设置绘图风格,包括画笔颜色、画笔粗细、填充风格等。

  Paint中提供了大量设置绘图风格的方法,这里仅列出一些常用的:

  • setARGB(int a,int r,int g,int b):设置ARGB颜色。
  • setColor(int color):设置颜色。
  • setAlpha(int a):设置透明度。
  • setPathEffect(PathEffect effect):设置绘制路径时的路径效果。
  • setShader(Shader shader):设置Paint的填充效果。
  • setAntiAlias(boolean aa):设置是否抗锯齿。
  • setStrokeWidth(float width):设置Paint的笔触宽度。
  • setStyle(Paint.Style style):设置Paint的填充风格。
  • setTextSize(float textSize):设置绘制文本时的文字大小。

invalidate()和postInvalidate()的区别.

通过上面的讲解,我在自定义一个静态的View已经是一件非常容易的事情了,但是我们使用的自定义的View有很多是需要根据一个变量去不断绘制的,这个时候就引出了新的函数invalidate()和postinvalidate(),使用此函数可以是的ondraw()不断的去执行从而达到不断绘制的效果.

Android中实现view的更新有两组方法,一组是invalidate,另一组是postInvalidate,其中前者是在UI线程自身中使用,而后者在非UI线程中使用。

接下来通过一个稍微综合一点的例子来对自定义View做一个总结:

先来看看实现的效果:

我们先来分析一下,这个效果实际上就是外面是不断的去绘制一个扇形,然后中间盖了一个小的圆:

好了,接下来我们来讲一讲实现的步骤:

步骤一:首先定义attrs文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="ProgressView">
        <attr name="smallRoundColor" format="color"></attr>
        <attr name="smallRoundRadius" format="dimension"></attr>
        <attr name="arcRadius" format="dimension"></attr>
        <attr name="arcColor" format="color"></attr>
        <attr name="textColor" format="color"></attr>
        <attr name="textSize" format="dimension"></attr>
    </declare-styleable>
</resources>

步骤二:编写自定义View:

public class ProgressView extends View {
    private float mArcRadius;
    private float mSmallRoundRadius;
    private int mArcColor;
    private int mSmallRoundColor;
    private Paint mRoundpaint;
    private Paint mArcpaint;
    private float sweepAngle;
    private int mTextColor;
    private Paint mTextPaint;
    private int mTextSize;
    public ProgressView(Context context) {
        this(context, null);
    }
    public ProgressView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public ProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView);
        //扇形半径
        mArcRadius = array.getDimension(R.styleable.ProgressView_arcRadius, 50);
        //小圆半径
        mSmallRoundRadius = array.getDimension(R.styleable.ProgressView_smallRoundRadius, 50);
        //扇形颜色
        mArcColor = array.getColor(R.styleable.ProgressView_arcColor, 0XFF00FF00);
        //小圆颜色
        mSmallRoundColor = array.getColor(R.styleable.ProgressView_smallRoundColor, 0XFF00FF00);
        //百分比字体颜色
        mTextColor = array.getColor(R.styleable.ProgressView_textColor, 0XFF00FF00);
        //百分比字体大小
        mTextSize = array.getDimensionPixelSize(R.styleable.ProgressView_textSize, 15);
        //释放资源
        array.recycle();
        //画圆的画笔
        mRoundpaint = new Paint();
        mRoundpaint.setColor(mSmallRoundColor);
        mRoundpaint.setAntiAlias(true);
        //画扇形的画笔
        mArcpaint = new Paint();
        mArcpaint.setColor(mArcColor);
        mArcpaint.setAntiAlias(true);
        //绘制中间文字部分的画笔文本
        mTextPaint = new Paint();
        mTextPaint.setColor(mTextColor);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(mTextSize);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //设置View的大小
        int withMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int withSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightsize = MeasureSpec.getSize(heightMeasureSpec);
        setMeasuredDimension(withMode == MeasureSpec.AT_MOST ? (int) mArcRadius * 2 : withSize, heightMode == MeasureSpec.AT_MOST ? (int) mArcRadius * 2 : heightsize);
    }
    @Override
    protected void onDraw(Canvas canvas) {
        //画圆弧
        RectF rect = new RectF(0, 0, (int) mArcRadius * 2, (int) mArcRadius * 2);
        canvas.drawArc(rect, 270, (float) (sweepAngle * 3.6), true, mArcpaint);
        //画小圆
        canvas.drawCircle(mArcRadius, mArcRadius, mSmallRoundRadius, mRoundpaint);
        String text = (int) (sweepAngle) + "%";
        float textLength = mTextPaint.measureText(text);
        //把文本画在圆心居中
        canvas.drawText(text, mArcRadius - textLength / 2, mArcRadius, mTextPaint);
        super.onDraw(canvas);
    }
    //提供一个给外界的方法可以不断的去设置扇形的弧度
    public void percent(float sweepAngle) {
        if (sweepAngle <= 100) {
            this.sweepAngle = sweepAngle;
            //刷新界面
            postInvalidate();
        }
    }
}

步骤三:在布局文件中进行使用:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.dapeng.viewdemo.SecondActivity">
    <com.dapeng.viewdemo.ProgressView
        android:id="@+id/pv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:arcColor="@android:color/holo_blue_bright"
        app:arcRadius="120dp"
        app:textSize="20sp"
        app:smallRoundColor="@android:color/transparent"
        app:textColor="@android:color/holo_orange_light"
        app:smallRoundRadius="100dp"/>
    <Button
        android:id="@+id/btn"
        android:text="开始绘制"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"/>
</RelativeLayout>

步骤四:在需要用到的地方模拟数据去使用自定义的View.

public class SecondActivity extends AppCompatActivity {
    private int mTotalProgress;
    private int mCurrentProgress;
    private ProgressView mPv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mPv = (ProgressView) findViewById(R.id.pv);
        initVariable();
        findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mCurrentProgress=0;
                new Thread(new ProgressRunable()).start();
            }
        });
    }
    private void initVariable() {
        mTotalProgress = 100;
        mCurrentProgress = 0;
    }
    class ProgressRunable implements Runnable {
        @Override
        public void run() {
            while (mCurrentProgress < mTotalProgress) {
                mCurrentProgress += 1;
                mPv.percent((float) mCurrentProgress);
                try {
                    Thread.sleep(50);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

至此大功告成.

时间: 2024-11-06 11:40:02

Android知识梳理之自定义View的相关文章

Android应用开发之自定义View触摸相关工具类全解

背景 最近有些乱,各种事情,各种交叉.好在还有一点上进心,于是继续将自定义这个系列的核心知识再梳理一下吧.关于自定义控件前面博文说过了,这里不会教你拿来主义,只授之以渔,如果你喜欢拿来主义,不好意思,请绕行,如果你喜欢得渔,那请继续. 前面我们已经叙述过了几篇关于自定义View涉及的东西,大家可以自己回过头去看我之前的博客,譬如事件处理.坐标系.工具类等.下面我们还是继续补充一些常用的自定义控件工具类. [工匠若水 http://blog.csdn.net/yanbober 未经允许严禁转载,请

Android进阶之绘制-自定义View完全掌握(一)

Android的UI设计可以说是决定一个app质量的关键因素,因为人们在使用app的时候,最先映入眼帘的就是app的界面了,一个美观.充实的界面能够给用户带来非常好的体验,会在用户心中留下好的印象. 对于UI设计,Android原生的控件加上一些开源库的使用,已经能够满足大部分的UI需求,但是,某些比较新颖.花哨的控件效果,我们只能通过自定义View来实现,那么,从该篇博客开始,我将记录关于Android自定义View的学习内容,并将其整理呈现给大家. 我们来实现一个优酷菜单案例,在案例中会涉及

Android进阶之绘制-自定义View完全掌握(二)

这是自定义View系列的第二篇博客,我们继续来学习关于自定义View的知识. 今天我们来实现一下广告条案例. 我们要实现的是这样的一个效果. 要想实现这样的效果,我们可以借助ViewPager控件,然后加上自定义的一些控件即可完成.那么现在就开始吧. 新建一个android项目. 修改activity_main.xml文件. <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:a

Android 如何 画 柱状图 -------自定义View

实现了 柱状图 根据 SeekBar的滑动 改变的效果: 图示效果: 自定义View的代码: package com.example.coustomviewdemo; import android.R.color; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.g

Android属性动画与自定义View——实现vivo x6更新系统的动画效果

晚上好,现在是凌晨两点半,然后我还在写代码.电脑里播放着<凌晨两点半>,晚上写代码,脑子更清醒,思路更清晰. 今天聊聊属性动画和自定义View搭配使用,前面都讲到自定义View和属性动画,但是一起用的还是不多,刚巧今晚手机提示我更新系统,我看到那个更新的动画还不错,仔细的分析了一下,于是我也决定写一个,不是一模一样的,但是效果和原理是一样的. 先看看图: 这是一张静态的图,这里有三个波浪线,当下载完之后,波浪线会往上活动,一直到消失. 所以难点也是在这个波浪线上.这个波浪线类似于一个水波纹,也

Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)

转载请注明地址:http://blog.csdn.net/xiaanming/article/details/10298163 很多的时候,系统自带的View满足不了我们功能的需求,那么我们就需要自己来自定义一个能满足我们需求的View,自定义View我们需要先继承View,添加类的构造方法,重写父类View的一些方法,例如onDraw,为了我们自定义的View在一个项目中能够重用,有时候我们需要自定义其属性,举个很简单的例子,我在项目中的多个界面使用我自定义的View,每个界面该自定义View

Android ——利用OnDraw实现自定义View(转)

自定义View的实现方式大概可以分为三种,自绘控件.组合控件.以及继承控件.本文将介绍自绘控件的用法.自绘控件的意思是,这个控件上的内容是用onDraw函数绘制出来的.关于onDraw函数的介绍可参看 Android视图绘制流程完全解析,带你一步步深入了解View(二) . 例子1:在layout文件中使用自绘控件 出处:http://blog.csdn.net/guolin_blog/article/details/17357967 下面我们准备来自定义一个计数器View,这个View可以响应

【Android 应用开发】 自定义 View 组件 -- 圆形进度条

转载著名出处 : http://blog.csdn.net/shulianghan/article/details/40351487 代码下载 : -- CSDN 下载地址 : http://download.csdn.net/detail/han1202012/8069497 ; -- GitHub 地址 : https://github.com/han1202012/CircleProcess.git ; -- 工程示例 : 一. 相关知识点解析 1. 自定义 View 组件构造方法 构造方

Android Matrix手势缩放自定义view 不止于Imageview

转载请注明出处:http://blog.csdn.net/coderyue/article/details/51397409 之前写过一篇文章Android TextView 横竖排切换(字方向不变) 是自定义了一个LinearLayout, 实现了当然还不够, 还要对它进行操作, 平移,旋转 and 缩放, 相信很多小伙伴都知道对图片的平移等等操作最好用的就是矩阵了,因为有个方法叫做imageview.setImageMatrix(matrix), 直接构造一个矩阵对象然后设置到图片上就进行相