安卓自定义圆形裁剪控件,圆形seekbar可拖动

安卓自定义圆形裁剪控件,圆形seekbar可拖动_第1张图片

如图所示,最近做了一个圆形的裁剪控件,我也觉得这有点奇怪,横的不好吗,不过没办法设计出来就是要搞,那只能画了。

首先简单分析下怎么搞。我们把它拆分成两部分就容易了,简单的就是两个圆形的seekbar。然后裁剪的时间范围肯定不能小于0,所以开始的seekbar不能超过结束的seekbar。分析完成就开始画了。三部曲就不说了,首页画一个圆形的可以拖动的seekbar。

1、画一个底色的圆弧,就是那灰色的一圈,

 canvas.drawArc(mRectF, 0, 360, false, circlePaint);

简单解释下第二、三个参数,开始画的地方,要画的角度。

2、画原点了

canvas.drawBitmap(dotBitmapStart, new Rect(0, 0, pointWidth, pointHeight),
                    new Rect((int) (markPointX - pointWidth / 2), (int) (markPointY - pointWidth / 2),
                            (int) (markPointX + pointWidth / 2), (int) (markPointY + pointHeight / 2)), circlePaint);

3、就是动态的画原点和扫过的角度了。

拆分就很简单了。

下面贴下代码:

/*
 * Copyright (C) 2017 BullyBoo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.lewanjia.dancelog.views;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import com.lewanjia.dancelog.R;


/**
 * by lzj
 */

public class CutMusicCircleSeekBar extends View {

    private Paint circlePaint;//
    private Paint circlePaintOver;//


    private float mHeight;
    private float mWidth;
    //x轴的原点坐标
    private int xOri;
    //y轴的原点坐标
    private int yOri;

    private Context mContext;

    private RectF mRectF;

    private Bitmap dotBitmapStart;
    private Bitmap dotBitmapEnd;


    private Drawable dotDrawableEnd;
    private float dotRadius;

    private float circleB = 0;//半径


    private double pointDegrees = 270;//初始圆的位置

    private double pointDegreesEnd =270;//初始圆的位置

    private final static int VAULE_3 = 3;
    private final static int VAULE_6 = 6;

    private float markPointX = 0;
    private float markPointY = 0;

    private float markPointXEnd = 0;
    private float markPointYEnd = 0;

    private float angle = 90;
    private float startangle = 270;


    private int maxProgress = 100;
    private int progressPercent = 0;
    private int progress;

    private Matrix matrix;
    private float startProcess = 0;

    private float endProcess = 360;


    private int pointWidth;
    private int pointHeight;

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

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

    public CutMusicCircleSeekBar(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        initView(context, attrs, defStyleAttr);
    }


    private void initView(Context context, AttributeSet attrs, int defStyleAttr) {
        TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CutMusicCircleSeekBar, defStyleAttr, 0);
        int count = array.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr = array.getIndex(i);
            switch (attr) {

            }
        }
        array.recycle();

    }

    /**
     * 初始化画笔
     */
    private void init() {
        circlePaint = new Paint();
        circlePaint.setColor(Color.parseColor("#a4a4a4"));
        circlePaint.setAntiAlias(true);
        circlePaint.setStyle(Paint.Style.STROKE);// 设置中空的样式
        circlePaint.setFlags(Paint.ANTI_ALIAS_FLAG);// 帮助消除锯齿
        circlePaint.setStrokeWidth(dip2px(mContext, VAULE_6));

        circlePaintOver = new Paint();
        circlePaintOver.setColor(Color.parseColor("#ff0000"));
        circlePaintOver.setAntiAlias(true);
        circlePaintOver.setStyle(Paint.Style.STROKE);// 设置中空的样式
        circlePaintOver.setFlags(Paint.ANTI_ALIAS_FLAG);// 帮助消除锯齿
        circlePaintOver.setStrokeWidth(dip2px(mContext, VAULE_6));

        mRectF = new RectF();
        matrix = new Matrix();

        Drawable dotDrawable = mContext.getResources().getDrawable(R.mipmap.ic_play_progress_handle_big);
        dotDrawableEnd = mContext.getResources().getDrawable(R.mipmap.ic_play_progress_handle_big);
        if (dotDrawable != null) {
            dotBitmapStart = ((BitmapDrawable) dotDrawable).getBitmap();
            dotBitmapEnd = ((BitmapDrawable) dotDrawableEnd).getBitmap();
            dotRadius = Math.max(dotRadius, Math.max(dotBitmapStart.getWidth() / 2, dotBitmapStart.getHeight() / 2));
            circleB = getWidth() / 2 - dip2px(mContext, VAULE_3) - dotBitmapStart.getWidth() / 4;
            pointWidth = dotBitmapEnd.getWidth();
            pointHeight = dotBitmapEnd.getHeight();
        }
        dotRadius = dotBitmapStart.getWidth() / 2;
        mRectF = new RectF(dip2px(mContext, VAULE_3) + dotBitmapStart.getWidth() / 4, dip2px(mContext, VAULE_3) + dotBitmapStart.getWidth() / 4,
                getWidth() - dip2px(mContext, VAULE_3) - dotBitmapStart.getWidth() / 4, getHeight() - dip2px(mContext, VAULE_3) - dotBitmapStart.getWidth() / 4);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width;
        int height;
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        if (widthMode == MeasureSpec.EXACTLY) {
            width = widthSize;
        } else {
            width = widthSize * 1 / 2;
        }
        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            height = heightSize * 1 / 2;
        }

        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        mHeight = getHeight();
        mWidth = getWidth();
        xOri = getWidth() / 2;
        yOri = getHeight() / 2;

        init();
    }

    @SuppressLint("DrawAllocation")
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Log.e("234", "canvas");
        float swipAngle = endProcess - startProcess;
        float start = 270 + startProcess;
        if (start > 360) {
            start = start - 360;
        }
        canvas.drawArc(mRectF, 0, 360, false, circlePaint);
        canvas.drawArc(mRectF, start, swipAngle, false, circlePaintOver);
        drawBitmpStart(canvas);

        canvas.save();

    }

    float lastx;
    float lasty;
    boolean isOntouch = false;
    boolean moveFirst = false;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        isOntouch = true;
        float swipAngle = endProcess - startProcess;
        switch (event.getAction()) {

            case MotionEvent.ACTION_DOWN:
                lastx = event.getX();
                lasty = event.getY();
                double fisrt = distance(lastx, lasty, markPointX, markPointY);
                double second = distance(lastx, lasty, markPointXEnd, markPointYEnd);
                moveFirst = fisrt <= second;
                if (swipAngle < 0) {
                    moveFirst = false;
                }
                moved(lastx, lasty, false, event, moveFirst);
                return true;
            case MotionEvent.ACTION_MOVE:
                lastx = event.getX();
                lasty = event.getY();
                if (swipAngle < 0) {
                    moveFirst = false;
                }
                moved(lastx, lasty, false, event, moveFirst);
                Log.e("", "");
                return true;
            case MotionEvent.ACTION_UP:
                return true;
        }
        return false;
    }

    private boolean isOnArc(MotionEvent event) {
        final float distance = distance(event.getX(), event.getY(),
                xOri, yOri);

        final float radius = mRectF.width() / 2f;

        final float halfStrokeWidth = dotRadius * 4;

        return Math.abs(distance - radius) <= halfStrokeWidth;
    }

    private float distance(float x1, float y1, float x2, float y2) {
        return (float) Math.sqrt(Math.pow(x1 - x2, 2) + Math.pow(y1 - y2, 2));
    }

    public int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    private float computeCos(float x, float y) {
        float width = x - this.mWidth / 2;
        float height = y - this.mHeight / 2;
        float slope = (float) Math.sqrt(width * width + height * height);
        return height / slope;
    }

    /**
     * @param canvas
     */
    private void drawBitmpStart(Canvas canvas) {

        if (moveFirst) {
            if (!isOntouch) {
                markPointX = (float) (Math.cos(Math.toRadians(pointDegrees)) * circleB + xOri);
                markPointY = (float) (Math.sin(Math.toRadians(pointDegrees)) * circleB + yOri);

                markPointXEnd = (float) (Math.cos(Math.toRadians(pointDegreesEnd)) * circleB + xOri);
                markPointYEnd = (float) (Math.sin(Math.toRadians(pointDegreesEnd)) * circleB + yOri);
            } else {
                markPointX = (float) (xOri + circleB
                        * Math.cos(Math.atan2(lastx - xOri, yOri - lasty) - (Math.PI / 2)));
                markPointY = (float) (yOri + circleB
                        * Math.sin(Math.atan2(lastx - xOri, yOri - lasty) - (Math.PI / 2)));
            }

            canvas.drawBitmap(dotBitmapStart, new Rect(0, 0, pointWidth, pointHeight),
                    new Rect((int) (markPointX - pointWidth / 2), (int) (markPointY - pointWidth / 2),
                            (int) (markPointX + pointWidth / 2), (int) (markPointY + pointHeight / 2)), circlePaint);

            canvas.drawBitmap(dotBitmapEnd, new Rect(0, 0, pointWidth, pointHeight),
                    new Rect((int) (markPointXEnd - pointWidth / 2), (int) (markPointYEnd - pointWidth / 2),
                            (int) (markPointXEnd + pointWidth / 2), (int) (markPointYEnd + pointHeight / 2)), circlePaint);
        } else {
            drawBitmpEnd(canvas);
        }
    }


    /**
     * @param canvas
     */
    private void drawBitmpEnd(Canvas canvas) {


        if (!isOntouch) {
            markPointXEnd = (float) (Math.cos(Math.toRadians(pointDegreesEnd)) * circleB + xOri);
            markPointYEnd = (float) (Math.sin(Math.toRadians(pointDegreesEnd)) * circleB + yOri);
            markPointX = (float) (Math.cos(Math.toRadians(pointDegrees)) * circleB + xOri);
            markPointY = (float) (Math.sin(Math.toRadians(pointDegrees)) * circleB + yOri);
//            canvas.drawBitmap(dotBitmapEnd, new Rect(0, 0, pointWidth, pointHeight),
//                    new Rect((int) (markPointXEnd - pointWidth / 2), (int) (markPointYEnd - pointWidth / 2),
//                            (int) (markPointXEnd + pointWidth / 2), (int) (markPointYEnd + pointHeight / 2)), circlePaint);

        } else {
            markPointXEnd = (float) (xOri + circleB
                    * Math.cos(Math.atan2(lastx - xOri, yOri - lasty) - (Math.PI / 2)));
            markPointYEnd = (float) (yOri + circleB
                    * Math.sin(Math.atan2(lastx - xOri, yOri - lasty) - (Math.PI / 2)));


        }
//        matrix.setRotate(endProcess);
//        dotBitmapEnd = null;
//        dotBitmapEnd = ((BitmapDrawable) dotDrawableEnd).getBitmap();
//        dotBitmapEnd = Bitmap.createBitmap(dotBitmapEnd, 0, 0, pointWidth, pointHeight, matrix, false);
//        drawRotateBitmap(canvas,circlePaint,endProcess,markPointXEnd,markPointYEnd);
        canvas.drawBitmap(dotBitmapEnd, new Rect(0, 0, pointWidth, pointHeight),
                new Rect((int) (markPointXEnd - pointWidth / 2), (int) (markPointYEnd - pointWidth / 2),
                        (int) (markPointXEnd + pointWidth / 2), (int) (markPointYEnd + pointHeight / 2)), circlePaint);
        canvas.drawBitmap(dotBitmapStart, new Rect(0, 0, pointWidth, pointHeight),
                new Rect((int) (markPointX - pointWidth / 2), (int) (markPointY - pointWidth / 2),
                        (int) (markPointX + pointWidth / 2), (int) (markPointY + pointHeight / 2)), circlePaint);


    }

    /**
     * 绘制自旋转位图
     *
     * @param canvas
     * @param paint
     * @param rotation 旋转度数
     * @param posX     在canvas的位置坐标
     * @param posY
     */
    private void drawRotateBitmap(Canvas canvas, Paint paint,
                                  float rotation, float posX, float posY) {
        Bitmap bitmap = ((BitmapDrawable) dotDrawableEnd).getBitmap();
        Matrix matrix = new Matrix();
        float offsetX = bitmap.getWidth() / 2;
        float offsetY = bitmap.getHeight() / 4;
        matrix.postTranslate(-offsetX, -offsetY);
        matrix.postRotate(rotation);
        matrix.postTranslate(posX + offsetX, posY + offsetY);
        canvas.drawBitmap(bitmap, matrix, paint);
    }

    private void moved(float x, float y, boolean up, MotionEvent event, boolean moveFirst) {

        if (isOnArc(event) && !up) {
            /*
             * 根据三角函数整切定理计算得到X.Y的坐标
             */
            if (moveFirst) {
                float degrees = (float) ((float) ((Math.toDegrees(Math.atan2(
                        x - xOri, yOri - y)) + 360.0)) % 360.0);
                if (degrees < 0) {
                    degrees += 2 * Math.PI;
                }
                if (endProcess < degrees) {
                    return;
                }
                if (startProcess > 0 && startProcess < 0.1) {
                    startProcess = 0f;
                } else {
                    startProcess = degrees;
                }

                Log.e("234", "startProcess===" + startProcess);
                Log.e("234", "endProcess===" + endProcess);
            } else {
                float degrees = (float) ((float) ((Math.toDegrees(Math.atan2(
                        x - xOri, yOri - y)) + 360.0)) % 360.0);
                if (degrees < 0) {
                    degrees += 2 * Math.PI;
                }
                if (degrees < startProcess) {
                    return;
                }
                if (endProcess > 359.8 && endProcess < 360f) {//解决不能裁剪100%问题
                    endProcess = 360f;
                } else {
                    endProcess = degrees;
                }


                Log.e("234", "startProcess111===" + startProcess);
                Log.e("234", "endProcess==111=" + endProcess);
            }
            if (onProcessChangeLister != null) {
                onProcessChangeLister.changeEnd(getEndproces());
                onProcessChangeLister.changeStart(getStartproces());
            }

            invalidate();
        } else {
//            invalidate();
        }
    }


    /**
     * 获取进度条百分比
     */
    public float getStartproces() {
        return (startProcess / 360);
    }

    /**
     * 获取进度条百分比
     */
    public float getEndproces() {

        return (endProcess / 360);

    }

    private OnProcessChangeLister onProcessChangeLister;

    public interface OnProcessChangeLister {
        void changeStart(float process);

        void changeEnd(float preocess);
    }

    public void setOnProcessChangeLister(OnProcessChangeLister onProcessChangeLister) {
        this.onProcessChangeLister = onProcessChangeLister;

    }

    public void setAngleEnd(long angle) {
        this.angle = angle;

    }

    public int getMaxProgress() {
        return maxProgress;
    }

    public void setProgressPercent(int progressPercent) {
        this.progressPercent = progressPercent;
    }


}

代码通俗易通,时间问题我没有去陪xml的值,直接写死的颜色。有需要的可以自己加顺便锻炼下写自定义view的能力。

有问题加下面的图问。

你可能感兴趣的:(安卓)