Android 自定义形状按钮

Android 自定义形状按钮,android,view,android-imageview,onclicklistener,android-button,Android,View,Android Imageview,Onclicklistener,Android Button,我想把馅饼的每一片都做成纽扣。饼图是图像视图中的一组矢量绘图。我不一定需要点击实际的饼片。我想用Path来画一个透明的形状,然后把它放在上面,把它做成按钮,但据我所知,可画的东西是不可点击的 我读过一篇博客文章,其中显然使用了路径来创建自定义形状的图像视图,我知道图像视图是可单击的,但在博客文章中的实现中,图像视图似乎仍然是矩形的,但博客作者在示例中使用的位图只是在图像视图中修剪为自定义形状。这是我指的帖子: 请像一个五岁的孩子一样给我解释一下。我对编程比较陌生。如果不是Android Stud

我想把馅饼的每一片都做成纽扣。饼图是图像视图中的一组矢量绘图。我不一定需要点击实际的饼片。我想用Path来画一个透明的形状,然后把它放在上面,把它做成按钮,但据我所知,可画的东西是不可点击的

我读过一篇博客文章,其中显然使用了路径来创建自定义形状的图像视图,我知道图像视图是可单击的,但在博客文章中的实现中,图像视图似乎仍然是矩形的,但博客作者在示例中使用的位图只是在图像视图中修剪为自定义形状。这是我指的帖子:

请像一个五岁的孩子一样给我解释一下。我对编程比较陌生。如果不是Android Studio的自动一切,我不会在这里


谢谢。

您可以使用drawArc和drawCircle绘制一个径向菜单,并使用接触点和中心点之间的距离和角度来检测当前正在单击的切片。我为你写了一个样本:

public class RadioButtons extends View {

    //the number of slice
    private int mSlices = 6;

    //the angle of each slice
    private int degreeStep = 360 / mSlices;

    private int quarterDegreeMinus = -90;

    private float mOuterRadius;
    private float mInnerRadius;

    //using radius square to prevent square root calculation
    private float outerRadiusSquare;
    private float innerRadiusSquare;

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private RectF mSliceOval = new RectF();

    private static final double quarterCircle = Math.PI / 2;

    private float innerRadiusRatio = 0.3F;

    //color for your slice
    private int[] colors = new int[]{Color.GREEN, Color.GRAY, Color.BLUE, Color.CYAN, Color.DKGRAY, Color.RED};

    private int mCenterX;
    private int mCenterY;

    private OnSliceClickListener mOnSliceClickListener;
    private int mTouchSlop;

    private boolean mPressed;
    private float mLatestDownX;
    private float mLatestDownY;

    public interface OnSliceClickListener{
        void onSlickClick(int slicePosition);
    }

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

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

    public RadioButtons(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);

        ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
        mTouchSlop = viewConfiguration.getScaledTouchSlop();

        mPaint.setStrokeWidth(10);
    }

    public void setOnSliceClickListener(OnSliceClickListener onSliceClickListener){
        mOnSliceClickListener = onSliceClickListener;
    }

    @Override
    public void onSizeChanged(int w, int h, int oldw, int oldh){
        mCenterX = w / 2;
        mCenterY = h / 2;

        mOuterRadius = mCenterX > mCenterY ? mCenterY : mCenterX;
        mInnerRadius = mOuterRadius * innerRadiusRatio;

        outerRadiusSquare = mOuterRadius * mOuterRadius;
        innerRadiusSquare = mInnerRadius * mInnerRadius;

        mSliceOval.left = mCenterX - mOuterRadius;
        mSliceOval.right = mCenterX + mOuterRadius;
        mSliceOval.top = mCenterY - mOuterRadius;
        mSliceOval.bottom = mCenterY + mOuterRadius;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        float currX = event.getX();
        float currY = event.getY();

        switch(event.getActionMasked()){
            case MotionEvent.ACTION_DOWN:
                mLatestDownX = currX;
                mLatestDownY = currY;

                mPressed = true;
                break;
            case MotionEvent.ACTION_MOVE:


                if(Math.abs(currX - mLatestDownX) > mTouchSlop || Math.abs(currY - mLatestDownY) > mTouchSlop) mPressed = false;
                break;
            case MotionEvent.ACTION_UP:

                if(mPressed){
                    int dx = (int) currX - mCenterX;
                    int dy = (int) currY - mCenterY;
                    int distanceSquare = dx * dx + dy * dy;

                    //if the distance between touchpoint and centerpoint is smaller than outerRadius and longer than innerRadius, then we're in the clickable area
                    if(distanceSquare > innerRadiusSquare && distanceSquare < outerRadiusSquare){

                        //get the angle to detect which slice is currently being click
                        double angle = Math.atan2(dy, dx);

                        if(angle >= -quarterCircle && angle < 0){
                            angle += quarterCircle;
                        }else if(angle >= -Math.PI && angle < -quarterCircle){
                            angle += Math.PI + Math.PI + quarterCircle;
                        }else if(angle >= 0 && angle < Math.PI){
                            angle += quarterCircle;
                        }

                        double rawSliceIndex = angle / (Math.PI * 2) * mSlices;

                        if(mOnSliceClickListener != null){
                            mOnSliceClickListener.onSlickClick((int) rawSliceIndex);
                        }

                    }
                }
                break;
        }

        return true;
    }

    @Override
    public void onDraw(Canvas canvas){
        int startAngle = quarterDegreeMinus;

        //draw slice
        for(int i = 0; i < mSlices; i++){
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(colors[i % colors.length]);
            canvas.drawArc(mSliceOval, startAngle, degreeStep, true, mPaint);

            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(Color.WHITE);
            canvas.drawArc(mSliceOval, startAngle, degreeStep, true, mPaint);

            startAngle += degreeStep;
        }

        //draw center circle
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLACK);
        canvas.drawCircle(mCenterX, mCenterY, mInnerRadius, mPaint);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.WHITE);
        canvas.drawCircle(mCenterX, mCenterY, mInnerRadius, mPaint);
    }
}

您可以使用drawArc和drawCircle绘制径向菜单,并使用触点和中心点之间的距离和角度来检测当前正在单击的切片。我为你写了一个样本:

public class RadioButtons extends View {

    //the number of slice
    private int mSlices = 6;

    //the angle of each slice
    private int degreeStep = 360 / mSlices;

    private int quarterDegreeMinus = -90;

    private float mOuterRadius;
    private float mInnerRadius;

    //using radius square to prevent square root calculation
    private float outerRadiusSquare;
    private float innerRadiusSquare;

    private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private RectF mSliceOval = new RectF();

    private static final double quarterCircle = Math.PI / 2;

    private float innerRadiusRatio = 0.3F;

    //color for your slice
    private int[] colors = new int[]{Color.GREEN, Color.GRAY, Color.BLUE, Color.CYAN, Color.DKGRAY, Color.RED};

    private int mCenterX;
    private int mCenterY;

    private OnSliceClickListener mOnSliceClickListener;
    private int mTouchSlop;

    private boolean mPressed;
    private float mLatestDownX;
    private float mLatestDownY;

    public interface OnSliceClickListener{
        void onSlickClick(int slicePosition);
    }

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

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

    public RadioButtons(Context context, AttributeSet attrs, int defStyle){
        super(context, attrs, defStyle);

        ViewConfiguration viewConfiguration = ViewConfiguration.get(context);
        mTouchSlop = viewConfiguration.getScaledTouchSlop();

        mPaint.setStrokeWidth(10);
    }

    public void setOnSliceClickListener(OnSliceClickListener onSliceClickListener){
        mOnSliceClickListener = onSliceClickListener;
    }

    @Override
    public void onSizeChanged(int w, int h, int oldw, int oldh){
        mCenterX = w / 2;
        mCenterY = h / 2;

        mOuterRadius = mCenterX > mCenterY ? mCenterY : mCenterX;
        mInnerRadius = mOuterRadius * innerRadiusRatio;

        outerRadiusSquare = mOuterRadius * mOuterRadius;
        innerRadiusSquare = mInnerRadius * mInnerRadius;

        mSliceOval.left = mCenterX - mOuterRadius;
        mSliceOval.right = mCenterX + mOuterRadius;
        mSliceOval.top = mCenterY - mOuterRadius;
        mSliceOval.bottom = mCenterY + mOuterRadius;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        float currX = event.getX();
        float currY = event.getY();

        switch(event.getActionMasked()){
            case MotionEvent.ACTION_DOWN:
                mLatestDownX = currX;
                mLatestDownY = currY;

                mPressed = true;
                break;
            case MotionEvent.ACTION_MOVE:


                if(Math.abs(currX - mLatestDownX) > mTouchSlop || Math.abs(currY - mLatestDownY) > mTouchSlop) mPressed = false;
                break;
            case MotionEvent.ACTION_UP:

                if(mPressed){
                    int dx = (int) currX - mCenterX;
                    int dy = (int) currY - mCenterY;
                    int distanceSquare = dx * dx + dy * dy;

                    //if the distance between touchpoint and centerpoint is smaller than outerRadius and longer than innerRadius, then we're in the clickable area
                    if(distanceSquare > innerRadiusSquare && distanceSquare < outerRadiusSquare){

                        //get the angle to detect which slice is currently being click
                        double angle = Math.atan2(dy, dx);

                        if(angle >= -quarterCircle && angle < 0){
                            angle += quarterCircle;
                        }else if(angle >= -Math.PI && angle < -quarterCircle){
                            angle += Math.PI + Math.PI + quarterCircle;
                        }else if(angle >= 0 && angle < Math.PI){
                            angle += quarterCircle;
                        }

                        double rawSliceIndex = angle / (Math.PI * 2) * mSlices;

                        if(mOnSliceClickListener != null){
                            mOnSliceClickListener.onSlickClick((int) rawSliceIndex);
                        }

                    }
                }
                break;
        }

        return true;
    }

    @Override
    public void onDraw(Canvas canvas){
        int startAngle = quarterDegreeMinus;

        //draw slice
        for(int i = 0; i < mSlices; i++){
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setColor(colors[i % colors.length]);
            canvas.drawArc(mSliceOval, startAngle, degreeStep, true, mPaint);

            mPaint.setStyle(Paint.Style.STROKE);
            mPaint.setColor(Color.WHITE);
            canvas.drawArc(mSliceOval, startAngle, degreeStep, true, mPaint);

            startAngle += degreeStep;
        }

        //draw center circle
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(Color.BLACK);
        canvas.drawCircle(mCenterX, mCenterY, mInnerRadius, mPaint);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(Color.WHITE);
        canvas.drawCircle(mCenterX, mCenterY, mInnerRadius, mPaint);
    }
}

也许因为我的馅饼片是不同的颜色,我可以使用onTouch检查被触摸的颜色?你为什么不谷歌android径向菜单?哇。谢谢@Bob。看起来棒极了。我不知道那是真的。但这似乎是一个独立的库,不是我必须下载的Android API的一部分。你觉得还有别的办法吗?该饼图实际上是一个可动画化的VectorDrawable,单击后我将对其进行动画制作。我相信您可以找到一个使用VectorDrawables或AnimatedVectorDrawables的库。对于与库相关的内容,它们是一段代码,通常免费提供完整的源代码,您可以根据自己的喜好进行修改。所以,不管你是谷歌还是直接跳转到安卓阿森纳,我相信你都能找到工作。谢谢你给我指明了正确的方向鲍勃。你为什么不写一个简短的答案,这样我就可以把它标记为一个答案?也许因为我的饼片是不同的颜色,我可以用onTouch来检查被触摸的颜色?你为什么不谷歌安卓径向菜单?哇。谢谢@Bob。看起来棒极了。我不知道那是真的。但这似乎是一个独立的库,不是我必须下载的Android API的一部分。你觉得还有别的办法吗?该饼图实际上是一个可动画化的VectorDrawable,单击后我将对其进行动画制作。我相信您可以找到一个使用VectorDrawables或AnimatedVectorDrawables的库。对于与库相关的内容,它们是一段代码,通常免费提供完整的源代码,您可以根据自己的喜好进行修改。所以,不管你是谷歌还是直接跳转到安卓阿森纳,我相信你都能找到工作。谢谢你给我指明了正确的方向鲍勃。你为什么不写一个简短的答案,这样我就可以把它标记为一个答案呢?非常感谢。我从来没有想过这个主意,你也不必为我做那么多。这足以为我指明正确的方向。但这是一段精彩的代码。我看到您在这里实现了一个侦听器。我最近也尝试了一些其他方法,让应用程序监听动画。在继续之前,isRunning是错误的,但它不起作用。我以后一定会引用这段代码。不客气,我一直喜欢写关于绘图的代码,而radial button是一个有趣的功能,需要实现。非常感谢。我从来没有想过这个主意,你也不必为我做那么多。这足以为我指明正确的方向。但这是一段精彩的代码。我看到您在这里实现了一个侦听器。我最近也尝试了一些其他方法,让应用程序监听动画。在继续之前,isRunning是错误的,但它不起作用。我以后一定会引用这段代码。不客气,我一直喜欢写关于绘图的代码,而radial button是一个有趣的功能。