Custom Knob View for controlling Volume?

2019-04-10 08:12发布

问题:

I want to show progress bar around knob. After following this tutorial I created this knob it is working fine.

But How could i modify the above knob to look like the second image

Running code for the first knob is written below.

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.GestureDetector;
import android.view.GestureDetector.OnGestureListener;
import android.view.View.MeasureSpec;
import android.view.MotionEvent;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.RelativeLayout;

public class RoundKnobButton extends RelativeLayout implements
        OnGestureListener 
{
    public int eventValue=10;

    //doctory starts 
    Paint p1,p2,p3;

    RectF oval;

    int width;
    //doctory ends
    private GestureDetector gestureDetector;

    private float mAngleDown, mAngleUp;
    private ImageView ivRotor;
    private Bitmap bmpRotorOn, bmpRotorOff;
    private boolean mState = false;
    private int m_nWidth = 0, m_nHeight = 0;

    public interface RoundKnobButtonListener 
    {
        public void onStateChange(boolean newstate);

        public void onRotate(int percentage);
    }


    private RoundKnobButtonListener m_listener;

    public void SetListener(RoundKnobButtonListener l) 
    {
        m_listener = l;
    }

    public void SetState(boolean state)
    {
        mState = state;

        ivRotor.setImageBitmap(state ? bmpRotorOn : bmpRotorOff);
    }

    public RoundKnobButton(Context context, int back, int rotoron,
            int rotoroff, final int w, final int h) {
        super(context);

        //doctory starts 

        width = w;

        p1 = new Paint(1);
        p1.setColor(Color.rgb(86, 86, 86));  
        p1.setStyle(android.graphics.Paint.Style.FILL);



         p2 = new Paint(1);
         p2.setColor(Color.rgb(245, 109, 89));
         p2.setStyle(android.graphics.Paint.Style.FILL);

         p3 = new Paint(1);
         p3.setColor(Color.GREEN);
         p3.setStyle(android.graphics.Paint.Style.STROKE);


        oval = new RectF();

        //doctory ends...

        // we won't wait for our size to be calculated, we'll just store out
        // fixed size
        m_nWidth = w;
        m_nHeight = h;

        // create stator
        ImageView ivBack = new ImageView(context);
        ivBack.setImageResource(back);

        RelativeLayout.LayoutParams lp_ivBack = new RelativeLayout.LayoutParams(
                w, h);
        lp_ivBack.addRule(RelativeLayout.CENTER_IN_PARENT);
        addView(ivBack, lp_ivBack);
        // load rotor images
        Bitmap srcon = BitmapFactory.decodeResource(context.getResources(),
                rotoron);
        Bitmap srcoff = BitmapFactory.decodeResource(context.getResources(),
                rotoroff);
        float scaleWidth = ((float) w) / srcon.getWidth();
        float scaleHeight = ((float) h) / srcon.getHeight();
        Matrix matrix = new Matrix();
        matrix.postScale(scaleWidth, scaleHeight);

        bmpRotorOn = Bitmap.createBitmap(srcon, 0, 0, srcon.getWidth(),
                srcon.getHeight(), matrix, true);
        bmpRotorOff = Bitmap.createBitmap(srcoff, 0, 0, srcoff.getWidth(),
                srcoff.getHeight(), matrix, true);
        // create rotor
        ivRotor = new ImageView(context);
        ivRotor.setImageBitmap(bmpRotorOn);
        RelativeLayout.LayoutParams lp_ivKnob = new RelativeLayout.LayoutParams(
                w, h);// LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        lp_ivKnob.addRule(RelativeLayout.CENTER_IN_PARENT);
        addView(ivRotor, lp_ivKnob);
        // set initial state
        SetState(mState);
        // enable gesture detector
        gestureDetector = new GestureDetector(getContext(), this);
    }

    /**
     * math..
     * 
     * @param x
     * @param y
     * @return
     */
    private float cartesianToPolar(float x, float y) {
        return (float) -Math.toDegrees(Math.atan2(x - 0.5f, y - 0.5f));
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        if (gestureDetector.onTouchEvent(event))
            return true;
        else
            return super.onTouchEvent(event);
    }

    public boolean onDown(MotionEvent event) 
    {
        float x = event.getX() / ((float) getWidth());
        float y = event.getY() / ((float) getHeight());
        mAngleDown = cartesianToPolar(1 - x, 1 - y);// 1- to correct our custom
                                                    // axis direction
        return true;
    }

    public boolean onSingleTapUp(MotionEvent e)
    {
        float x = e.getX() / ((float) getWidth());
        float y = e.getY() / ((float) getHeight());
        mAngleUp = cartesianToPolar(1 - x, 1 - y);// 1- to correct our custom
                                                    // axis direction

        // if we click up the same place where we clicked down, it's just a
        // button press
        if (!Float.isNaN(mAngleDown) && !Float.isNaN(mAngleUp)
                && Math.abs(mAngleUp - mAngleDown) < 10) {
            SetState(!mState);
            if (m_listener != null)
                m_listener.onStateChange(mState);
        }
        return true;
    }

    public void setRotorPosAngle(float deg)
    {
        if (deg >= 210 || deg <= 150) 
        {
            if (deg > 180)
                deg = deg - 360;

            Matrix matrix = new Matrix();
            ivRotor.setScaleType(ScaleType.MATRIX);
        //  matrix.postRotate((float) deg, 210 / 2, 210 / 2);// getWidth()/2,
                                                                // getHeight()/2);

            matrix.postRotate((float) deg, m_nWidth/2, m_nHeight/2);//getWidth()/2, getHeight()/2);
            ivRotor.setImageMatrix(matrix);
        }
    }

    public void setRotorPercentage(int percentage)
    {
        int posDegree = percentage * 3 - 150;

        if (posDegree < 0)
            posDegree = 360 + posDegree;

        setRotorPosAngle(posDegree);
    }

    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
            float distanceY) {
        float x = e2.getX() / ((float) getWidth());
        float y = e2.getY() / ((float) getHeight());
        float rotDegrees = cartesianToPolar(1 - x, 1 - y);// 1- to correct our
                                                            // custom axis
                                                            // direction

        if (!Float.isNaN(rotDegrees)) {
            // instead of getting 0-> 180, -180 0 , we go for 0 -> 360
            float posDegrees = rotDegrees;
            if (rotDegrees < 0)
                posDegrees = 360 + rotDegrees;

            // deny full rotation, start start and stop point, and get a linear
            // scale
            if (posDegrees > 210 || posDegrees < 150) {
                // rotate our imageview
                setRotorPosAngle(posDegrees);
                // get a linear scale
                float scaleDegrees = rotDegrees + 150; // given the current
                                                        // parameters, we go
                                                        // from 0 to 300
                // get position percent
                int percent = (int) (scaleDegrees / 3);
                if (m_listener != null)
                    m_listener.onRotate(percent);
                return true; // consumed
            } else
                return false;
        } else
            return false; // not consumed
    }

    public void onShowPress(MotionEvent e) {
        // TODO Auto-generated method stub

    }

    public boolean onFling(MotionEvent arg0, MotionEvent arg1, float arg2,
            float arg3) {
        return false;
    }

    public void onLongPress(MotionEvent e) {
    }

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

        int i = width / 4;

        oval.set(i - i / 2, i / 2, i * 3 + i / 2, i * 3 + i / 2);

        canvas.drawOval(oval, p1);

        canvas.drawArc(oval, -90F, (int)Math.round((360D * Double.valueOf(eventValue).doubleValue()) / 100D), true, p2);

        canvas.drawLine(20, 30, 120, 200, p2);


    }*/

    /*@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        int desiredWidth  = width;

        int desiredHeight = width;

        int widthMode  = android.view.View.MeasureSpec.getMode(widthMeasureSpec);

        int widthSize = android.view.View.MeasureSpec.getSize(widthMeasureSpec);

        int heightMode = android.view.View.MeasureSpec.getMode(heightMeasureSpec);

        int heightSize = android.view.View.MeasureSpec.getSize(heightMeasureSpec);

        int measuredWidth;

        int measuredHeight;


        if (widthMode == MeasureSpec.EXACTLY)
        {
            measuredWidth = widthSize ;
        }
        else if (widthMode == MeasureSpec.AT_MOST)
        {
            measuredWidth = Math.min(desiredWidth , widthSize);
        } 
        else
        {
            measuredWidth = desiredWidth;
        }


        if (heightMode == MeasureSpec.EXACTLY)
        {
            measuredHeight = heightSize ;
        }
        else if (heightMode == MeasureSpec.AT_MOST)
        {
            measuredHeight = Math.min(desiredHeight, heightSize );
        }
        else
        {
            measuredHeight = desiredHeight;
        }

        setMeasuredDimension(measuredWidth, measuredHeight);
    }
*/
}

I have created onDraw and onMeasure function function and try to draw outer progress, or how could i modify it.

Edit

Is it possible to change the rotation percentage to 20 . I mean it shows progress form 0 to 99. is it possible to convert it to 0 to 12.

回答1:

Use two XML files.

circular_progress_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromDegrees="270"
    android:toDegrees="270">
    <shape
        android:innerRadiusRatio="2.5"
        android:shape="ring"
        android:thickness="1dp">
        <gradient
            android:angle="0"
            android:endColor="#22FA05"
            android:startColor="#22FA05"
            android:type="sweep"
            android:useLevel="false" />
    </shape>
</rotate>

and background_circle.xml

<?xml version="1.0" encoding="utf-8"?>
<shape
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="ring"
    android:innerRadiusRatio="2.5"
    android:thickness="1dp"
    android:useLevel="false">

    <solid android:color="#000000" />

</shape>

and finally

<ProgressBar
        android:id="@+id/progressBar"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:indeterminate="false"
        android:progressDrawable="@drawable/circular_progress_drawable"
        android:background="@drawable/background_circle"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:progress="25" />

Note that set width and height based on the image position you have