4

現在、充填率を調整できるメーターを作成しようとしています。私が抱えている問題は、数学がまったく得意ではないことです。「東」に0度の点を持つ通常の円弧(2番目の画像に示すように)とは対照的に、「北」に円弧を描き始めたい(最初の画像)。

画像1 画像2

画面に沿ってドラッグ/タッチすることで、画像1の青い領域のサイズ(角度)を大きくしたいと考えています。今、これらは私がある種の方法で今できることです。私が直面している本当の問題はこれです:

次のコードを使用して、青い領域を描画します。

mStart = -90;
int degree = (int)((theta + Math.PI) * 180 / Math.PI);
mSweep = degree;

RectF mOvals = new RectF(c.x - outerRadius + circleThickness, c.y - outerRadius + circleThickness, c.x + outerRadius - circleThickness, c.y + outerRadius - circleThickness );

mArcSetLevel = new Path();

if(mArcSetLevel != null ) {
    canvas.drawArc(mOvals, mStart, mSweep, true, arcPaint);
}

開始を -90 に設定すると、90 度早く開始されます。タッチの角度を追跡するには、次の式を使用しますが、ここで問題が発生します。

int py = (int)event.getY() - c.y;
int px = (int)event.getX() - c.x;

theta = (float) ((float)  Math.atan2(py, px) - (Math.PI / 2)); // - Math.PI / 2 to correct -90 start

正確に 270 度を超えると、青色の領域がリセットされ、北から西にはるかに小さい角度で描画されます (3 番目の画像に示すように、-90 の「誤った」開始のため)。私の数学のスキルは、これを解決するのに十分ではありませんが、なぜそれが起こっているのかを考えることができますが、解決策を見つけることができないようです.

画像3

私が作成したビュー全体の (非常に面倒な) コードは次のとおりです。

    private Canvas canvas;  

//Canvas width and height
private int h = -1;
private int w = -1;

//circle properties
private Paint paint;
private Paint arcPaint;
private Path circle;
private Point c;
private int outerRadius;
private int circleThickness = 20;

//point click in wheel
private float theta = 0;

private float mStart;
private float mSweep;
private Paint mBgPaints   = new Paint();
private Path mArcSetLevel;

int padding = 10;

OnMeterWheelChangeListener onMeterWheelChangeListener = null;

public MeterWheel(Context context){
    super(context);
    initCircleSeekBar();
}

public MeterWheel(Context context, AttributeSet attrs) {
    super(context, attrs);
    initCircleSeekBar();
}

private void initCircleSeekBar() {

    canvas = new Canvas();
    circle = new Path();
    paint = new Paint();
    arcPaint = new Paint();
    c = new Point();

    mBgPaints.setAntiAlias(true);
    mBgPaints.setStyle(Paint.Style.FILL);
    mBgPaints.setColor(0x88FF0000);
    mBgPaints.setStrokeWidth(0.5f);

    mArcSetLevel = new Path();

    this.draw(canvas);
}


@Override
protected void onSizeChanged(int width, int height, int oldw, int oldh) {
    // TODO Auto-generated method stub
    super.onSizeChanged(width, height, oldw, oldh);

    w = width;
    h = height;
    Log.i("POWERWHEEL", String.valueOf(w) + "   " + String.valueOf(h));
    c.set(w/2, h/2);
    drawCircle();
}

private void drawCircle() {
    outerRadius = Math.min(h,w)/2;
    circleThickness = (int) (outerRadius*0.15);

    circle.addArc(new RectF(c.x - outerRadius + circleThickness/2, c.y - outerRadius + circleThickness/2, c.x + outerRadius - circleThickness/2, c.y + outerRadius - circleThickness/2 ), 0, 360);
    circle.moveTo(c.x, c.y);
    //paint.setShader(new SweepGradient(w/2,h/2, colourarry, null));
    paint.setColor(Color.GRAY);
    paint.setStyle(Style.STROKE);
    paint.setStrokeWidth(circleThickness);
    paint.setAntiAlias(true);

    arcPaint.setColor(Color.BLUE);
    arcPaint.setStyle(Style.FILL);
    arcPaint.setStrokeWidth(circleThickness);
    arcPaint.setAntiAlias(true);
}


@SuppressLint("DrawAllocation")
@Override
protected void onDraw(Canvas canvas) {
    // TODO Auto-generated method stub
    super.onDraw(canvas);

    if(circle != null){
    //draw circle
        canvas.drawPath(circle, paint);

        mStart = -90;

        int degree = (int)((theta + Math.PI) * 180 / Math.PI);
        Log.d("POWERWHEEL", "" + degree);
        mSweep = degree;

        RectF mOvals = new RectF(c.x - outerRadius + circleThickness, c.y - outerRadius + circleThickness, c.x + outerRadius - circleThickness, c.y + outerRadius - circleThickness );

        mArcSetLevel = new Path();

        if(mArcSetLevel != null ) {
            canvas.drawArc(mOvals, mStart, mSweep, true, arcPaint);
        }
    }

}


@Override
public boolean onTouchEvent(MotionEvent event) {

    if (!isEnabled()) {
        return false;
    }

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            setPressed(true);
            onStartTrackingTouch(event);
            trackTouchEvent(event);
            break;

        case MotionEvent.ACTION_MOVE:
            trackTouchEvent(event);
            break;

        case MotionEvent.ACTION_UP:
            trackTouchEvent(event);
            onStopTrackingTouch();
            setPressed(false);
            invalidate();
            break;

        case MotionEvent.ACTION_CANCEL:
            onStopTrackingTouch();
            setPressed(false);
            invalidate();
            break;
    }

    return true;
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);

    setMeasuredDimension(width,height);
}

private void onStartTrackingTouch(MotionEvent event) {

}

private void onStopTrackingTouch() {

}

private void trackTouchEvent(MotionEvent event) {

    int py = (int)event.getY() - c.y;
    int px = (int)event.getX() - c.x;

    theta = (float) ((float)  Math.atan2(py, px) - (Math.PI / 2));
    Log.d("POWERWHEEL", "theta: " + theta);

    this.invalidate();
}



public void setSize(int x, int y){
    h = y;
    w = x;
}

public void setCirleThickness(int t){
    circleThickness = t;
}


public void setOnMeterWheelChangeListener (OnMeterWheelChangeListener listener) {
    onMeterWheelChangeListener = listener;
}

public interface OnMeterWheelChangeListener{
    public void onStartTrackingTouch (MeterWheel colourWheel);
    public void onStopTrackingTouch (MeterWheel colourWheel);
}

100万前もってありがとう!

4

1 に答える 1

1

theta を計算するときは、角度を +/- pi で返す atan2 を使用します。したがって、左上象限にある場合、-pi/2 から -pi の範囲の値を返します (y が下向きに正であり、x が右向きに正であると仮定します)。pi/2 を直接減算すると、-pi から -3pi/2 の範囲が得られます。onDraw では、この象限のスイープの 0 から -pi/2 の範囲を与える pi を再度追加します (紛らわしい)。これは、上部の開始位置から反時計回りに 0 から pi/2 (または 0 から 90 度) の弧を描くことを意味します。スイープが常に 0 から pi の範囲内にあることを確認する必要があります。最も良い解決策は、座標を -pi/2 だけシフトして、Math.atan2(py, px) の代わりに Math.atan2(px, -py) を実行し、theta が負の場合は 2*pi を追加することです。のようなもの(私はアンドロイドを書いていません)

theta = (float)  Math.atan2(px, -py);
if (theta < 0) theta += 2 * Math.PI;

そしてonDrawで

int degree = (int)(theta * 180 / Math.PI);
Log.d("POWERWHEEL", "" + degree);
mSweep = degree;

それでも問題が発生する場合は、mSweep が常に 0 から 360 度の範囲内にあることを確認してください。

于 2012-11-28T17:07:30.830 に答える