android自定义view初探

       好久没写过博客了真的是好懒好懒。。。

       最近在开发公司的新项目,里头有用到一个圆,呃,确切地说是三段圆弧,圆弧之间有一个点,居然不叫UI给图而是叫我直接画。。。。说实话我刚听到的时候很想骂娘,后来想想就当是练练手吧就答应了,没图我说个JB。
这里写图片描述
(话说markdown怎么设置居中啊。。。)
       就是上面那种图中的圆,不要问盾牌上为什么要放个勾,因为我也不知道究竟为什么要放个勾。大概效果就是这样吧,也是最终出来的效果,废话少说,上代码。
       首先是建一个类继承view:

public class SecurityCircle extends View {
private int stokeWidth;
private int defaultPointSize;
private int defaultRadius;
private int radius;
private int defaultSize;
private Paint mPaint = new Paint();
private Context context;
private int color;
private int defaultColor = Color.rgb(248, 248, 255);

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

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

    public SecurityCircle(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context = context;
        defaultRadius = (int) ScaleUtil.dp2px(context.getResources(), 100);
        defaultSize = (int) ScaleUtil.dp2px(context.getResources(), 200);
        defaultPointSize = (int) ScaleUtil.dp2px(context.getResources(), 3);
        stokeWidth = (int) ScaleUtil.dp2px(context.getResources(), 1);

        final TypedArray attributes = context.getTheme().obtainStyledAttributes(attrs, R.styleable.SecurityCircle,
                defStyleAttr, 0);
        initByAttributes(attributes);
        attributes.recycle();
    }
}

       没啥好说的,就是一个类,然后写一下构造方法,声明了几个默认值,这里说一下自定义属性,自定义属性有几个步骤:
       在value文件夹下建立一个attrs.xml文件,在里面声明要使用的属性名:

<declare-styleable name="SecurityCircle">
    <attr name="radius" format="dimension"/>
    <attr name="circle_color" format="color"/>
</declare-styleable>

OK,再放上在xml里面的用法:

<com.xxx.xxx.xxx.SecurityCircle
    android:id="@+id/security_circle"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:padding="10dp"
    android:layout_centerHorizontal="true"
    android:layout_centerVertical="true"
    securitycircle:radius="100dp"
    securitycircle:color="@color/color_white" />

别忘了加上命名空间:

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

这样就可以在view中使用自定义的属性了,我比较懒,在这里只写了两个属性,一个是圆的半径,一个是圆的颜色,如果有其他需要,继续加进去就是了,用法是一样的,在自定义的view中去获取它:

private void initByAttributes(TypedArray attributes) {
    radius = (int) attributes.getDimension(R.styleable.SecurityCircle_radius, defaultRadius);
    color = (int) attributes.getColor(R.styleable.SecurityCircle_circle_color, defaultColor);
}

然后是重写一下onMeasure方法,让我们的控件支持wrap_content和自定义宽高,在这里说一下view的测量模式,一共有三种:EXACTLY,AT_MOST,UNSPECIFIED,对应关系如下图:(图片来自网络)
这里写图片描述
当view的LayoutParams为warp_content时,不论父容器的MeasureSpec为何种模式,所产生了view的MeasureSpec均为AT_MOST模式。直接的效果就是match_parent的效果,所以自定义view时,想要支持wrap_content,就必须自己做处理,也就是自己定义一个大小作为wrap_content的标准,可以看到我在构造方法里面初始化了一个默认大小:

defaultSize = (int) ScaleUtil.dp2px(context.getResources(), 200);

下面贴上onMeasure方法:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    setMeasuredDimension(measure(widthMeasureSpec), measure(heightMeasureSpec));
}

private int measure(int measureSpec) {
   int result;
   int mode = MeasureSpec.getMode(measureSpec);
   int size = MeasureSpec.getSize(measureSpec);
   if (mode == MeasureSpec.EXACTLY) {
       //EXACTLY,也就是layout_width和layout_height中指定了view的宽高
       result = size;
   } else {
       result = defaultSize;
       if (mode == MeasureSpec.AT_MOST) {
       //AT_MOST,不管是wrap_content还是match_parent,
       //如果不处理的话,默认都是父元素的宽高
           result = defaultSize;
       }
   }
   return result;
}

       OK,到这里理一下,如果用户指定了view的宽高,则按照用户设置的宽高去设置我们想要得到的view的宽高;如果用户指定的宽高是wrap_content的话,在这里就把宽高定义为默认的宽高defaultSize;如果是match_parent的话,就是父元素的宽高parentSize,在这里解释一下UNSPECIFIED,就是父容器不对view的宽高做大小限定,要多大给多大,这个我们一般不使用,不用管就好。
       最后一步就是绘图onDraw了:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    mPaint.setColor(color);
    mPaint.setStrokeWidth(ScaleUtil.dp2px(getResources(), 2));
    //设置画笔为空心
    mPaint.setStyle(Paint.Style.STROKE);
    //抗锯齿
    mPaint.setAntiAlias(true);
    int delta = (int) ScaleUtil.dp2px(getResources(), 3);
    RectF rectF = new RectF(delta * 2, delta * 2, radius * 2 - delta * 2, radius * 2 - delta * 2);

    canvas.drawArc(rectF, -145, 110, false, mPaint);
    canvas.drawArc(rectF, -25, 110, false, mPaint);
    canvas.drawArc(rectF, 95, 110, false, mPaint);

    //设置为实心去画三个小圆
    mPaint.setStyle(Paint.Style.FILL);
    canvas.drawCircle(radius, radius * 2 - delta * 2f, defaultPointSize, mPaint);
    canvas.drawCircle((float) (radius * (1 - Math.sqrt(3)/2) + delta * 2f),
    radius / 2 + delta, defaultPointSize, mPaint);
    canvas.drawCircle((float) (radius * (1 + Math.sqrt(3)/2) - delta * 2f),
    radius / 2 + delta, defaultPointSize, mPaint);
}

       内容不多,就是把一个圆分成了三个扇形,在这里我自己设定了每个扇形是110度,这样两个扇形之间会有10度的空间留出来画小圆,角度什么的,按照自己的需要去设置哈。说一下RectF这个矩形的大小,其实就是画图的时候的边缘,为了不让view的大小吃掉一点边的宽度,在这里设置了一个距离delta去控制绘图的边框,有兴趣的小伙伴可以把这个矩形定义成rectF = (0, 0, radius * 2, radius * 2),看看我说的”吃掉边的宽度”是什么效果。
       然后是画小圆,这个因为不是很熟悉android的坐标系,查API查了半天也没实现自己想要的效果,下面给出一张图说明一下坐标系:
这里写图片描述
图片转自:http://blog.csdn.net/jason0539/article/details/42743531

       最后突然发现好像根本不用这样,因为之前绘图的时候已经定义了一个矩形,根据矩形来确定最底部的那个点就行了,确定好了这个点之后,出来的效果是这样的(注意考虑边线的宽度)
这里写图片描述
       看着还行吧,可以把另外两个点加上去了,计算的方法有很多种,我想到的是用三角函数,画了个草图:
这里写图片描述
       B点的横坐标就是半径radius,纵坐标是两倍的半径,我这里把边的宽度也考虑进去了,最后的结果是radius * 2 - delta * 2f,OK,已知B点坐标,又知道半径和角度,三角函数很容易就出来了吧,所以最后画三个圆的代码:

canvas.drawCircle(radius, radius * 2 - delta * 2f, defaultPointSize, mPaint);

canvas.drawCircle((float) (radius * (1 - Math.sqrt(3)/2) + delta * 2f),
    radius / 2 + delta, defaultPointSize, mPaint);

canvas.drawCircle((float) (radius * (1 + Math.sqrt(3)/2) - delta * 2f),
    radius / 2 + delta, defaultPointSize, mPaint);

再说一下drawCircle的参数好了:drawCircle(float x, float y, float radius, Paint paint);
xy就是圆心的横纵坐标,radius半径,paint就是画笔(感觉好废话)。。。

OK,大功告成了,最后的效果在文章开头,总结一下整个流程:
1.建一个类继承自view;
2.在value文件夹下建一个attrs.xml,在里面定义要使用的自定义属性;
3.在自定义view的构造方法中读取属性值;
4.重写onMeasure,让自定义view支持wrap_content和自定义宽高;
5.重写onDraw,绘图。
挺简单的,但是涵盖的知识点也挺多的,偶尔写一下练练手感觉还是挺不错的。

第一次用markdown写博客,发现别人看着比较方便,自己编辑好累。。排版什么的大家就不要吐槽了,将就着看吧,如果文中有出现错误或者有歧义的地方,欢迎各位看官指正。

转载请注明出处。http://blog.csdn.net/name_Uncle_Wang/article/details/52684635

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值