android 自定义控件初步

1.android 自定义控件初步

    以之前一个项目内使用的自定义圆环为例,当用户滑动屏幕时,可以选择不同的刻度,开设置目标值。效果如下:


                                                           

2.实现过程:

    2.1.定义一个自定义View类,继承自View类,同时注意必须使用构造方法,在构造方法中首先调用超类的构造方法

    2.2.values文件夹下创建一个attrs.xml 文件定义这个控件的一些基本属性

    <!-- 滑动选择目标步数的控件属性设置 -->

    <declare-styleable name="SetGoalStepProgressView">  

        <attr name="color_start" format="color" />  

         <attr name="color_end" format="color" />  

        <attr name="circleDimensionRate" format="dimension" />  

        <attr name="triangleDimensionRate" format="dimension" />

    </declare-styleable>

    2.3.一般需要重写onMesure() 、OnDraw()方法,如果要在屏幕变换时显示不同,也要重写onSizeChanged()方法

    onMesure()可以调用系统的

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  

    {  

        super.onMeasure(widthMeasureSpec, heightMeasureSpec);  

    }  也可以自己重写

    系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。

所以,当设置了WRAP_CONTENT时,我们需要自己进行测量,即重写onMesure方法”:重写之前先了解MeasureSpec的specMode,一共三种类型:

EXACTLY:一般是设置了明确的值或者是MATCH_PARENTAT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT UNSPECIFIED:表示子布局想要多大就多大,很少使用

    onDraw()方法的重写非常重要,它接受了一个canvas(画布)作为参数,

    这个控件需要跟用户交互,所以要重写view 的onTouchEvent()方法,这个自定义控件也是在onTouchEvent()方法内完成滑动角度的计算的

    直接看代码吧:

package com.example.myautoviewdemo;


import android.content.Context;

import android.content.Intent;

import android.content.SharedPreferences;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Matrix;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.Rect;

import android.graphics.RectF;

import android.util.AttributeSet;

import android.view.MotionEvent;

import android.view.View;


public class SetGoalStepProgressView extends View {


    private int color_start;

    private int color_end;

    public static int flag;

    private float circleDimensionRate = 0.1f;// 圆环比例

    private float triangleDimensionRate = 0.1f;// 三角形比例

    private float s;// 三角形边长

    private float strokeWidth;// 圆环宽度

    private float angle = getContext().getSharedPreferences("goalstepfiles",

            Context.MODE_WORLD_READABLE)

            .getFloat("setgoalangle", (float) 135.0);// 角度0-360

                                                        // 第一次设置默认为72度

    private RectF oval = new RectF();

    private Paint paint = new Paint();

    private Path path = new Path();// 绘制三角形的路径

    private Path path_matrix = new Path();// 经过matrix变换的三角形路径

    private Matrix matrix = new Matrix();// 三角形旋转矩阵

    Rect rect = new Rect();// 测量文字所占的高度宽度

    private String TAG = "SetGoalStepProgressView";

    private int goalstepcount;

    private String text;

    private SharedPreferences goalPreferences;

    int stepcount;


    private final float sqrt3 = (float) Math.sqrt(3);


    public SetGoalStepProgressView(Context context) {

        super(context);

        init(null, 0);

    }


    public SetGoalStepProgressView(Context context, AttributeSet attrs) {

        super(context, attrs);

        init(attrs, 0);

    }


    public SetGoalStepProgressView(Context context, AttributeSet attrs,

            int defStyle) {

        super(context, attrs, defStyle);

        init(attrs, defStyle);


    }


    private void init(AttributeSet attrs, int defStyle) {

        // Load attributes

        final TypedArray a = getContext().obtainStyledAttributes(attrs,

                R.styleable.SetGoalStepProgressView, defStyle, 0);


        color_start = a.getColor(

                R.styleable.SetGoalStepProgressView_color_start,

                Color.parseColor("#79CDCD"));// 默认绿色

        color_end = a.getColor(R.styleable.SetGoalStepProgressView_color_end,

                Color.LTGRAY);// 默认灰色

        circleDimensionRate = a.getFloat(

                R.styleable.SetGoalStepProgressView_circleDimensionRate, 0.1f);

        triangleDimensionRate = a

                .getFloat(

                        R.styleable.SetGoalStepProgressView_triangleDimensionRate,

                        0.1f);


        a.recycle();


    }


    private void initDrawVar(float width) {

        if (s != triangleDimensionRate * width) {

            s = triangleDimensionRate * width;// 三角形边长

            strokeWidth = circleDimensionRate * width;// 圆环宽度

            // 绘制圆环的矩形区域

            oval.set(s + strokeWidth / 2f, s + strokeWidth / 2f, width - s

                    - strokeWidth / 2f, width - s - strokeWidth / 2f);

            // 绘制三角形的路径

            path.reset();

            path.moveTo(0, -s / sqrt3);

            path.lineTo(s / 2f, sqrt3 / 6 * s);

            path.lineTo(-s / 2f, sqrt3 / 6 * s);

            path.close();

        }

    }


    @Override

    protected void onDraw(Canvas canvas) {

        // Log.e(TAG, "run here!  onDraw() angle ="+angle);

        super.onDraw(canvas);

        int width = this.getWidth();

        initDrawVar(width);

        // 画绿色环形

        paint.setAntiAlias(true);

        paint.setColor(color_start);

        paint.setStrokeWidth(strokeWidth);

        paint.setStyle(Paint.Style.STROKE); // 设置空心

        // 正右方为0度,顺时针旋转,正上方为270度


        canvas.drawArc(oval, 270 - angle, angle, false, paint);


        // 画灰色环形

        paint.setColor(color_end);

        canvas.drawArc(oval, 270, 360 - angle, false, paint);


        // 画三角形

        paint.setStrokeWidth(1);

        paint.setColor(color_start);

        paint.setStyle(Paint.Style.FILL);

        matrix.reset();

        matrix.setRotate(180 - angle);// 旋转

        float angle_rad = (float) (angle / 180f * Math.PI);// 化成弧度制

        matrix.postTranslate(// 平移

                (float) (width / 2f - (width / 2f - s + s / sqrt3)

                        * Math.sin(angle_rad)), (float) (width / 2f - (width

                        / 2f - s + s / sqrt3)

                        * Math.cos(angle_rad)));

        path_matrix.set(path);

        path_matrix.transform(matrix);


        canvas.drawPath(path_matrix, paint);


        // 显示百分比

        paint.setTextSize(width * 0.1f);

        if (angle == 360.0) {

            goalPreferences = getContext().getSharedPreferences(

                    "goalstepfiles", Context.MODE_WORLD_READABLE);

            goalstepcount = goalPreferences.getInt("setgoalstepcount", 10000); // 默认步数为10000步


            text = goalstepcount + "";


            // paint.getFontMetrics()方法测量不准确,无法保证居中显示;paint.getTextBounds方法测量较准确

            paint.getTextBounds(text, 0, text.length(), rect);// 测量text所占宽度和高度

            canvas.drawText(text, width / 2f - rect.width() / 2, width / 2f

                    + rect.height() / 2, paint);

        } else {

            // 角度小于72度(目标为4000)时自动设置为最小4000


            // text = String.valueOf((Math.round((angle / 360 * 24000)+4000)));

            int goaltext_int = (int) ((400 * angle / 9) + 4000);

            text = String.valueOf(goaltext_int);


            // Log.e("SetGoalStepView", "text_4000 ="+text);

            int text_int = Integer.parseInt(text);

            if (text_int >= 100) {


                int ittext_int = (text_int / 100) * 100;


                text = ittext_int + "";

                if (text_int > 19950) {

                    text = 20000 + "";

                }

            }


            // 通过广播的形式给activity发送消息

            Intent intent = new Intent("broadcast_action"); // action是broadcast_action;

            intent.putExtra("stepcount", text);

            intent.putExtra("stepangle", angle);

            getContext().sendBroadcast(intent);


            paint.getTextBounds(text, 0, text.length(), rect);// 测量text所占宽度和高度

            canvas.drawText(text, width / 2f - rect.width() / 2, width / 2f

                    + rect.height() / 2, paint);

        }


    }


    @Override

    public boolean onTouchEvent(MotionEvent event) {

        switch (event.getAction()) {

        case MotionEvent.ACTION_DOWN:

        case MotionEvent.ACTION_MOVE:


            float x = event.getX();

            float y = event.getY();

            float r = this.getWidth() / 2 - s - strokeWidth;// 圆环半径

            // 判断在圆环外 则处理触摸事件

            if (Math.pow(x - this.getWidth() / 2, 2)

                    + Math.pow(y - this.getWidth() / 2, 2) > Math.pow(r, 2)) {

                double angle = Math.atan((this.getWidth() / 2 - x)

                        / (this.getWidth() / 2 - y));

                angle = angle / Math.PI * 180;

                if (x > this.getWidth() / 2 && y <= this.getWidth() / 2) {// 第一象限

                    angle += 360;

                } else if (y > this.getWidth() / 2) {// 第三四象限

                    angle += 180;

                }

                if (Math.abs(this.angle - angle) > 1) {

                    this.angle = (float) angle;

                    this.invalidate();

                }

                // Log.e(TAG, "run here! onTouchEvent()--angle ="+angle);

            }

            return true;

        }

        return super.onTouchEvent(event);

    }


    // 获取当前选择的比例

    public float getRate() {

        return angle / 360f;

    }


    public int getColor_start() {

        return color_start;

    }


    public void setColor_start(int color_start) {

        this.color_start = color_start;

    }


    public int getColor_end() {

        return color_end;

    }


    public void setColor_end(int color_end) {

        this.color_end = color_end;

    }


    public float getCircleDimensionRate() {

        return circleDimensionRate;

    }


    public void setCircleDimensionRate(float circleDimensionRate) {

        this.circleDimensionRate = circleDimensionRate;

    }


    public float getTriangleDimensionRate() {

        return triangleDimensionRate;

    }


    public void setTriangleDimensionRate(float triangleDimensionRate) {

        this.triangleDimensionRate = triangleDimensionRate;

    }


}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值