笔记 Androd 自定义控件学习(四)

###说明:文章来自《Android群英传》学习笔记

###重写View来实现全新的控件
当Android系统原生的控件无法满足我们的需求时,我们就可以完全创建一个新的自定义View来实现需要的功能。创建一个自定义的View,难点在于绘制控件和实现交互。通常自定义View需要继承View类,并重写他的onDraw(),onMeasure()方法来实现绘制逻辑,同时通过重写onTouchEvent()等触控事件来实现交互逻辑。

下面通过一个案列,介绍一下自定义View:
先看一下效果图:
这里写图片描述
首先我们分析一下这个图的组成部分,这个图分三个部分,分为中间的圆形,中间显示的文字和外圈的弧形,所以我们只需要在onDraw()方法中去绘制这些图形就可以了,看过之前的文章,相应这些单独图形的绘制是非常容易的事,只是在这里进行一下组合。
代码如下:

public class CustomCircleView extends View {

    float mCircleX,mCircleY;//定义圆心位置
    float mSweepVlae; //旋转角度
    float mRadius;//圆的半径
    String mShowText;//显示的文字
    float mShowTextSize;//显示文字的Size

    Paint mCirclePaint;//圆心画笔
    Paint mArcPaint;//弧形画笔
    Paint mTextPaint;//文字画笔
    RectF mArcRectF ;//弧形的外接矩形
    Context context;

    public CustomCircleView(Context context) {
        super(context);
        this.context = context;
    }

    public CustomCircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
    }

	//获取屏幕宽度
    private float getWidthPixels(Context context){
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(outMetrics);
        int width = outMetrics.widthPixels;
        return width;
    }

	//获取屏幕高度
    private float getHeightPixels(Context context){
        WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics outMetrics = new DisplayMetrics();
        manager.getDefaultDisplay().getMetrics(outMetrics);
        int height = outMetrics.heightPixels;
        return height;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        float heightPixels = getHeightPixels(context);
        //mCircleX = getWidthPixels(context) /2;
        mCircleY = heightPixels/2 - 100 ;
        mRadius = (float) (mCircleY*0.6/2);
        Log.i("zdd","mRadius=="+mRadius);
        //定义弧形矩形框
        mArcRectF = new RectF((float)(heightPixels*0.3) ,(float)(heightPixels*0.3) -100,
                (float)(heightPixels*0.7),(float)(heightPixels*0.7) -100);
		//定义圆形Paint
        mCirclePaint = new Paint();
        mCirclePaint.setAntiAlias(true);
        mCirclePaint.setColor(Color.GREEN);

		//定义弧形Paint
        mArcPaint = new Paint();
        mArcPaint.setAntiAlias(true);
        mArcPaint.setColor(Color.BLUE);
        mArcPaint.setStrokeWidth(35);//设置线宽
        mArcPaint.setStyle(Paint.Style.STROKE);//设置空心
		
		//定义文字Paint
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.RED);
        mTextPaint.setTextSize(mShowTextSize);

        //绘制椭圆
        canvas.drawArc(mArcRectF,270,mSweepVlae,false,mArcPaint);
        canvas.drawCircle(mCircleY+100,mCircleY,mRadius,mCirclePaint);
        canvas.drawText(mShowText,mCircleY-mShowText.length()*6+100,mCircleY+(mShowTextSize/4),mTextPaint);
    }

    //设置圆弧比例
    public void setmSweepVlae(float sweepVlae){
        if (sweepVlae < 0){
            mSweepValue = 0 ;
        }else{
            //最大角度不能超过360,这里有个360的满角度和零角度的问题,大家可以根据自己的需求来单独处理
            mSweepValue = sweepVlae % 360;
        }
        setShowText(numberFormat(mSweepValue));
        this.invalidate();
    }

    //设置显示的文字
    public void setShowText(String text){
        mShowText = text +“%”;
    }

	//设置显示的文字的大小
    public void setShowTextSize(float textSize){
        mShowTextSize = textSize;
    }
}

调用方法如下:

public class MainActivity extends AppCompatActivity {
    CustomCircleView circleView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        circleView = (CustomCircleView) findViewById(R.id.circle);
        circleView.setmSweepVlae(220);
        circleView.setShowTextSize(52);
    }
}

接下来我们给自定义的View 添加触摸事件,在外狐滑动时,实时刷新所占比,和动态显示圆中的文字,
这里我们重写View的onTouchEvent方法来实现我们滑动的动作:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        float dx,dy;
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                L.i("ACTION_DOWN");
                //这里最好用 getX(),getY()方法,跟我们上边获取屏幕的方法对应上
                dx = event.getX();
                dy = event.getY();
                L.i("dx==="+String.valueOf(dx));
                L.i("dy==="+String.valueOf(dy));
                //判断是否点击在外弧形内,在圆弧内,我们返回true,拦截触摸事件。
                if (Math.abs(Math.hypot(Math.abs(dx-mCircleX),Math.abs(dy-mCircleY))-mRadius2) <= 60){
                    L.i("in Arc true");
                    return true;
                }else {
                    L.i("in Arc false");
                    return false;
                }
            case MotionEvent.ACTION_MOVE:
                L.i("ACTION_MOVE");
                float currentX = event.getX();
                float currentY = event.getY();
                if (Math.abs(Math.hypot(Math.abs(currentX-mCircleX),Math.abs(currentY-mCircleY))-mRadius2) <= 60){
                    //如果在圆弧内才执行滑动动作
                    //分4个区域分别求角度,这里我用Math.atan方法求弧度,方法建议画坐标系,会更好理解
                    //如果大家有什么好的方便,快捷的方法,欢迎留言。
                    if(currentX > mCircleX && currentY < mCircleY){//第一象限
                         mSweepValue = (float) (Math.atan((currentX-mCircleX)/(mCircleY-currentY))/Math.PI*180);
                    }else if (currentX < mCircleX && currentY < mCircleY){//第二象限
                        mSweepValue = 360 - (float) (Math.atan((mCircleX-currentX)/(mCircleY-currentY))/Math.PI*180);
                    }else if (currentX < mCircleX && currentY < mCircleY){//第三象限
                        mSweepValue = 180 + (float) (Math.atan((mCircleX-currentX)/(currentY-mCircleY))/Math.PI*180);
                    }else{//第三象限
                        mSweepValue = 180 - (float) (Math.atan((currentX-mCircleX)/(currentY-mCircleY))/Math.PI*180);
                    }
                    setShowText(numberFormat(mSweepValue));//根据我们触摸的位置,刷新中间显示的百分比
                    this.invalidate();//刷新
                    L.i("mSweepValue==="+mSweepValue);
                }
                break;
            case MotionEvent.ACTION_UP:
                L.i("ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }

显示中间百分比,我这里保留了一个小数,代码如下:

    //转换数值,保留一位小数
    public String numberFormat(float value){
        DecimalFormat decimalFormat = new DecimalFormat(".#");
        return String.valueOf(decimalFormat.format(value/360*100));
    }

欢迎大家留言讨论。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,关于 Android 开关按钮的背景图和自定义控件,我可以为您提供一些学习笔记和分享。 1. Android 开关按钮背景图 在 Android 中,开关按钮的背景图可以通过 drawable 文件夹下的 xml 文件来实现。以下是一个简单的例子: ```xml <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/switch_on" android:state_checked="true"/> <item android:drawable="@drawable/switch_off"/> </selector> ``` 其中,我们使用了 selector 标签来表示按钮的状态。在 checked 为 true 时,使用 switch_on 图片作为背景图;否则使用 switch_off 图片作为背景图。 2. Android 自定义控件之开关按钮学习笔记分享 如果您想完全自定义一个开关按钮控件,以下是一些实现步骤: 1) 创建一个继承自 CompoundButton 的类,例如 SwitchButton。 2) 实现 SwitchButton 的构造函数和一些必要的属性。 ```java public class SwitchButton extends CompoundButton { public SwitchButton(Context context) { super(context); init(); } public SwitchButton(Context context, AttributeSet attrs) { super(context, attrs); init(); } public SwitchButton(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { // 初始化一些属性 } } ``` 3) 在 init() 方法中,设置一些默认属性,例如背景图、文字等。 4) 重写 onMeasure() 方法,计算出 SwitchButton 的宽高。 ```java @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); setMeasuredDimension(widthSize, heightSize); } ``` 5) 重写 onDraw() 方法,绘制 SwitchButton 的背景图和文字。 ```java @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 绘制背景图和文字 } ``` 6) 重写 onTouchEvent() 方法,处理用户的触摸事件,例如改变 SwitchButton 的状态。 ```java @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // 处理按下事件 break; case MotionEvent.ACTION_MOVE: // 处理移动事件 break; case MotionEvent.ACTION_UP: // 处理抬起事件 break; } return super.onTouchEvent(event); } ``` 7) 最后,将 SwitchButton 添加到布局文件中即可。 ```xml <com.example.myapp.SwitchButton android:id="@+id/switchButton" android:layout_width="wrap_content" android:layout_height="wrap_content"/> ``` 以上就是自定义开关按钮控件的一些步骤和注意事项。希望对您有帮助!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值