Android安卓自定义View

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

2016-12-25  本文已影响353人  雁归来兮

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

前言

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

预期效果

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

QQ拼音截图未命名.png

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

步骤

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

正文

设置属性内容

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

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--齿轮颜色颜色-->
    <attr name="grarColor" format="color"></attr>
    <!--齿轮半径 单位:dp-->
    <attr name="gearRadius" format="dimension"></attr>
    <!--这是声明属性 name的值要和自定义View的java类名一致,后面就会看到-->
    <!--把上面的属性给复制下来,移除format属性-->
    <declare-styleable name="Gear">
        <!--齿轮颜色颜色-->
        <attr name="grarColor"></attr>
        <!--齿轮半径 单位:dp-->
        <attr name="gearRadius"></attr>
    </declare-styleable>
    </resources>

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

定义类并继承View

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);
    }
}

//声明全局变量,保存属性参数
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());
}

编写相应的方法

@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;
}

当然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()方法,我们进行绘制,首先使用数学工具分析我们的需求:

QQ拼音截图未命名.png

下面看代码

@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);
    }
}

在布局中使用

<?xml version="1.0" encoding="utf-8"?>
<!--在布局中加入    xmlns:tools="http://schemas.android.com/apk/res-auto"-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <cn.app.hybord.tao.myapplication.Gear
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:gearRadius="50dp"
        tools:grarColor="#FFFF00FF"
        />
</RelativeLayout>
QQ拼音截图未命名.png

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

<?xml version="1.0" encoding="utf-8"?>
<!--在布局中加入    xmlns:tools="http://schemas.android.com/apk/res-auto"-->
<LinearLayout    xmlns:android="http://schemas.android.com/apk/res/android" 
   xmlns:tools="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:padding="15dp"
    android:layout_height="match_parent">
    <EditText
        android:id="@+id/edit_raduis"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:hint="请输入半径长度"
        />
    <Button
        android:id="@+id/btn_draw"
        android:layout_width="match_parent"
        android:layout_height="48dp"
        android:hint="重新绘制"
                />
    <cn.app.hybord.tao.myapplication.Gear
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:gearRadius="50dp"
        tools:grarColor="#FFFF00FF"
        />
</LinearLayout>
public void setGearColor(int gearColor) {
    //重新赋值
    this.gearColor = gearColor;
    //重绘
    invalidate();
    //备注:子线程重绘
   // postInvalidate();
}
public void setGearRadius(float gearRadius) {
    //如上注释
    this.gearRadius = gearRadius;
    invalidate();
}
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布局中的文件设置的半径】

morenbanjiang.png

Radius=20

20.png

Radius=150

150.png

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

上一篇下一篇

猜你喜欢

热点阅读