【Android 进阶之路】自定义View控件

  • 时间:2016年12月25日
  • 设备:Android 7.0 小米4
  • 需求:自定义一个齿轮
  • 地点:安师大秋实园

本博客内容一致同步到本人的博客站点:http://www.zhoutaotao.xyz 欢迎访问留言交流

前言

+尽管安卓SDK提供了非常丰富的控件供大家使用,但是在一些特殊的场合,仍然不能够满足需求,所以我们要学会自定义符合自己的需求,在此学习了一些简单的心得,代码比较简单,分享一下自己的心得,并记录学习过程。由于是新手,如果错误的理解,多谢指正,我会立即改正,以免误导他人。谢谢!

  • 这个需求是看了 http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0606/3001.html
    这篇文章之后觉得不错,自己整理思路,不过好像方法不同,以后再做慢慢研究吧,有兴趣的可以看看。

预期效果

简单的才静态齿轮效果,可调节颜色,齿轮半径。先做个静态的,后面学习扎实了,再做个动态的齿轮,效果图如下:

【Android 进阶之路】自定义View控件_第1张图片
QQ拼音截图未命名.png

由于我是初次接触自定义View组件,在记录的过程中,难免有误解出现,敬请告知和谅解。

步骤

一、 设置属性内容
二、定义类并继承View
三、编写相应的方法
四、在布局中使用

正文

  • 废话不多说,开始主题

设置属性内容

1、在资源文件目录value中新建资源文件attrs.xml



    
    
    
    
    
    
    
        
        
        
        
    
    

这里的name比较重要,并非随意的命名,需要和其java文件相匹配,后面会用到,里面有三个属性,很简单的声明。

定义类并继承View

  • 新建Java文件Gear.java,文件名要和第一步的name一致,否则不能使用的
  • 其构造函数有四个,由于刚接触,没有深入研究,这里就不说区别了,一般我们使用两个参数的那一个
public class Gear extends View {
    public Gear(Context context) {
        super(context);
    }
    public Gear(Context context, AttributeSet attrs) {
        super(context, attrs);
      //我们一般使用这一个,当然也可以使用第三个构造函数
    }
    public Gear(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    public Gear(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}
  • 构造完成之后,需要获取在xml布局文件传入的值

//声明全局变量,保存属性参数
private int gearColor;
private float gearRadius;
private float gearLength;
//画笔
private Paint paint;

public Gear(Context context, AttributeSet attrs) {
    super(context, attrs);
    //获取属性集合
    TypedArray typedArray=context.obtainStyledAttributes(attrs,R.styleable.Gear);
    //为全局变量赋值
    //获取颜色,第一位参数属性,第二个是默认值,注意是8位的16进制数,前两位为不透明度,要写上,不可省略
    gearColor=typedArray.getColor(R.styleable.Gear_grarColor,0xFF000000);
    //获取半径,后面的为默认值,由于传入的数据单位是dp,需要转化为px
    gearRadius=typedArray.getDimension(R.styleable.Gear_gearRadius,dp2px(0));
    //从属性集中获取属性完成之后一定要回收
    typedArray.recycle();
    //初始化画笔
    paint=new Paint();
    //设置画笔的宽度
    paint.setStrokeWidth(dp2px(4));
}

public float dp2px(int dpValue){
    return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,dpValue,getResources().getDisplayMetrics());
}

编写相应的方法

  • 测量的方法onMeasure()
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int width=getRealValue(widthMeasureSpec);
//由于布局中的宽度存在三种类型,精确的数据,包含,和占用父布局,所以我们需要处理数据
    int height=getRealValue(heightMeasureSpec);
//计算完成后,调用此方法,传入实际的需要宽度和高度
    setMeasuredDimension(width,height); 
}
public int getRealValue(int value){
    int mode=MeasureSpec.getMode(value);//得到数据的模式,模式占用32位数据的前两位
    int size=MeasureSpec.getSize(value);//得到系统给的数值
    int result=0;
    if (mode==MeasureSpec.EXACTLY)    {//如果是精准的数值的话,就使用系统的值
        result=size;
    }else    { 
       result= (int) paint.descent();
//如果比包含的话,使用画笔的数据,也就是包含了,这之前一定要调用paint.setStrokeWidth(float width)
        if (mode==MeasureSpec.AT_MOST)//占用父布局的话
        { 
           result=size; 
       }
    }
    return result;
}
  • 测量的方法onMeasure()

当然onDraw()之前还有一个方法

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
}
//onLayout方法是ViewGroup中子View的布局方法,用于放置子View的位置。
//放置子View很简单,只需在重写onLayout方法,然后获取子View的实例
//调用子View的layout方法实现布局。
//在实际开发中,一般要配合onMeasure测量方法一起使用。
//由于这个Demo很简单,Layout就不做处理了,有兴趣的自行研究学习。

+下面看重点onDraw()方法,我们进行绘制,首先使用数学工具分析我们的需求:

【Android 进阶之路】自定义View控件_第2张图片
QQ拼音截图未命名.png
  • 先画大圆,半径为R,圆心位置为中心
  • 在画小圆,半径为R/3,圆心位置为中心
  • 在画圆心,并且连接齿轮上的某一点
  • 其次画齿轮,圆周分60份,要认识到在特殊的点,比如0,5,10,15,20,25,30的长度略高于其他长度
  • 齿轮本质为线,只要确定起点和终点即可,我们尝试着来计算。
  • 起点:(x+RSin a,y+Rcos a)
  • 终点 : (x+ (R+l)sin a,y+(R+l)cos a) 其中l为齿轮的长度,在特殊的点略大点

下面看代码

@Overrideprotected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //圆心位置
    int circle_x = getWidth() / 2;
    int circle_y = getHeight() / 2;
    //设置画笔颜色
    paint.setColor(gearColor);
    //设置画笔为绘制边框
    paint.setStyle(Paint.Style.STROKE);
    //设置线宽
    paint.setStrokeWidth(dp2px(4));
    //绘制大圆形
    canvas.drawCircle(circle_x, circle_y, dp2px((int) gearRadius), paint);
    //绘制小圆,半径为大圆形的1/3
    canvas.drawCircle(circle_x, circle_y, dp2px((int) gearRadius) / 3, paint);
    //绘制圆心点
    canvas.drawPoint(circle_x, circle_y, paint);
    //绘制齿轮
    //1/60刻度的弧度值
    float kedu = (float) (2 * Math.PI / 60);
    paint.setStrokeWidth(dp2px(3));
    for (int i = 0; i < 60; i++) {
        int len = 18;
        if (i % 5 == 0)
            len = 30;
        float start_X = (float) (circle_x + dp2px((int) gearRadius) * Math.sin(kedu * i));
        float start_Y = (float) (circle_y + dp2px((int) gearRadius) * Math.cos(kedu * i));
        float end_X = (float) (circle_x + (dp2px((int) gearRadius) + len) * Math.sin(kedu * i));
        float end_Y = (float) (circle_y + (dp2px((int) gearRadius) + len) * Math.cos(kedu * i));
        canvas.drawLine(start_X, start_Y, end_X, end_Y, paint);
        if (i==6)
            canvas.drawLine(circle_x,circle_y,start_X,start_Y,paint);
    }
}

在布局中使用

  • 很简单的使用方法,需要在根布局中设置一个属性,如下所示:



    

  • 效果图
【Android 进阶之路】自定义View控件_第3张图片
QQ拼音截图未命名.png

附录:动态设置颜色和半径

  • 思路:在自定义View中写入公有的方法,在代码中绑定后调用,然后调用相应的设置方法进行重绘
  • 布局文件:



    
    
  • Gear.java中的方法
public void setGearColor(int gearColor) {
    //重新赋值
    this.gearColor = gearColor;
    //重绘
    invalidate();
    //备注:子线程重绘
   // postInvalidate();
}
public void setGearRadius(float gearRadius) {
    //如上注释
    this.gearRadius = gearRadius;
    invalidate();
}
  • Activity中的代码文件
public class MainActivity extends AppCompatActivity {
    private EditText radius;
    private Button reDraw;
    private Gear gear;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        radius= (EditText) findViewById(R.id.edit_raduis);
        reDraw= (Button) findViewById(R.id.btn_draw);
        gear= (Gear) findViewById(R.id.gear);
        reDraw.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            //仅仅写了修改半径,如果修改其他的类似的方法
                gear.setGearRadius(Float.parseFloat(radius.getText().toString().trim()));
            }
        });
    }
}

效果

默认半径【也就是xml布局中的文件设置的半径】

【Android 进阶之路】自定义View控件_第4张图片
morenbanjiang.png

Radius=20

【Android 进阶之路】自定义View控件_第5张图片
20.png

Radius=150

【Android 进阶之路】自定义View控件_第6张图片
150.png

本博客内容一致同步到本人的博客站点:http://www.zhoutaotao.xyz 欢迎访问留言交流

你可能感兴趣的:(【Android 进阶之路】自定义View控件)