以有限度数旋转表盘

Posted

技术标签:

【中文标题】以有限度数旋转表盘【英文标题】:rotate dial in limited degrees 【发布时间】:2012-12-13 14:35:36 【问题描述】:

所有 我想以特定角度旋转图像,如下图所示。我有旋转代码,但它旋转 360 度,但我只想要特定度数并获得选定的数字,它位于表盘的上方。

下面是我的代码。 我的自定义查看这项工作很好,但性能湖。

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector.OnGestureListener;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;

public class MyDialView extends View implements OnGestureListener
    private static Bitmap bimmap;
    private static Paint paint;
    private static Rect bounds;
    private int totalNicks = 100;
    private int currentNick = 0;
    private GestureDetector gestureDetector;
    private float dragStartDeg = Float.NaN;
    float dialerWidth = 0,dialerHeight = 0;

    private static Paint createDefaultPaint() 
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setFilterBitmap(true);
        return paint;
    
    private float xyToDegrees(float x, float y) 
        float distanceFromCenter = PointF.length((x - 0.5f), (y - 0.5f));
        if (distanceFromCenter < 0.1f
                || distanceFromCenter > 0.5f)  // ignore center and out of bounds events
            return Float.NaN;
         else 
            return (float) Math.toDegrees(Math.atan2(x - 0.5f, y - 0.5f));
        
    
    public final float getRotationInDegrees() 
        return (360.0f / totalNicks) * currentNick;
    

    public final void rotate(int nicks) 
        currentNick = (currentNick + nicks);
        if (currentNick >= totalNicks) 
            currentNick %= totalNicks;
         else if (currentNick < 0) 
            currentNick = (totalNicks + currentNick);
        
        Log.e("Current nick", String.valueOf(currentNick));
        if((currentNick > 80 || currentNick < 20))
            invalidate();
        
    
    public MyDialView(Context context, AttributeSet attrs) 
        super(context, attrs);
        bimmap = BitmapFactory.decodeResource(context.getResources(),R.drawable.out_round);
        paint = createDefaultPaint();
        gestureDetector = new GestureDetector(getContext(), this);
        dialerWidth = bimmap.getWidth() /2.0f;
        dialerHeight = bimmap.getHeight() / 2.0f;
        bounds = new Rect();
    



    @Override
    protected void onDraw(Canvas canvas) 
        canvas.getClipBounds(bounds);
            canvas.save(Canvas.MATRIX_SAVE_FLAG);
            //
                canvas.translate(bounds.left, bounds.top);

                float rotation = getRotationInDegrees();
                canvas.rotate(rotation, dialerWidth, dialerHeight);
                canvas.drawBitmap(bimmap, 0,0,null);
                //canvas.rotate(- rotation, dialerWidth, dialerHeight);
            //     
            canvas.restore();
    
    @Override
    public boolean onTouchEvent(MotionEvent event) 
        if (gestureDetector.onTouchEvent(event)) 
            return true;
         else 
            return super.onTouchEvent(event);
        
    
    //Gesture detector methods
    @Override
    public boolean onDown(MotionEvent e) 
        float x = e.getX() / ((float) getWidth());
        float y = e.getY() / ((float) getHeight());

        dragStartDeg = xyToDegrees(x, y);
        //Log.d("deg = " , ""+dragStartDeg);
        if (! Float.isNaN(dragStartDeg)) 
            return true;
         else 
            return false;
        
    

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
            float velocityY) 
        return false;
    

    @Override
    public void onLongPress(MotionEvent e) 

    

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX,
            float distanceY) 
        if (! Float.isNaN(dragStartDeg)) 
            float currentDeg = xyToDegrees(e2.getX() / getWidth(), 
                    e2.getY() / getHeight());

            if (! Float.isNaN(currentDeg)) 
                float degPerNick = 360.0f / totalNicks;
                float deltaDeg = dragStartDeg - currentDeg;

                final int nicks = (int) (Math.signum(deltaDeg) 
                        * Math.floor(Math.abs(deltaDeg) / degPerNick));

                if (nicks != 0) 
                    dragStartDeg = currentDeg;
                    rotate(nicks);
                 
             

            return true;
         else 
            return false;
        
    

    @Override
    public void onShowPress(MotionEvent e) 

    

    @Override
    public boolean onSingleTapUp(MotionEvent e) 
        return false;
    


我想要根据用户选择 0-9 并且还允许用户旋转到 0-9 而不是更多的旋转。

我还检查了下面的另一个代码。

dialer = (ImageView) findViewById(R.id.imageView_ring);
        dialer.setOnTouchListener(new MyOnTouchListener());
        dialer.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() 

            @Override
            public void onGlobalLayout() 
                // method called more than once, but the values only need to be initialized one time
                if (dialerHeight == 0 || dialerWidth == 0) 
                    dialerHeight = dialer.getHeight();
                    dialerWidth = dialer.getWidth();

                    // resize
                    Matrix resize = new Matrix();
                    resize.postScale((float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getWidth(), (float)Math.min(dialerWidth, dialerHeight) / (float)imageOriginal.getHeight());
                    imageScaled = Bitmap.createBitmap(imageOriginal, 0, 0, imageOriginal.getWidth(), imageOriginal.getHeight(), resize, false);

                    // translate to the image view's center
                    float translateX = dialerWidth / 2 - imageScaled.getWidth() / 2;
                    float translateY = dialerHeight / 2 - imageScaled.getHeight() / 2;
                    matrix.postTranslate(translateX, translateY);

                    dialer.setImageBitmap(imageScaled);
                    dialer.setImageMatrix(matrix);
                    Log.e("Rotation degree :"+rotationDegrees, String.valueOf(tickNumber));
                
            
        );

int tickNumber = 0;
    private void rotateDialer(float degrees) 

        //System.out.println("Rotation Done :: "+rotationDone);

       // if(!rotationDone) 

            this.rotationDegrees += degrees;
            this.rotationDegrees = this.rotationDegrees % 360;

            tickNumber = (int)this.rotationDegrees*100/360;
            // It could be negative
            if (tickNumber > 0) tickNumber = 100 - tickNumber;


            //this.rotationDegrees  = Math.abs(rotationDegrees);
            this.tickNumber = Math.abs(tickNumber);

           if(tickNumber  < 20 || tickNumber > 80)
               Log.e("Rotation degree :"+rotationDegrees, String.valueOf(tickNumber));
               matrix.postRotate(degrees, dialerWidth / 2, dialerHeight / 2);
               dialer.setImageMatrix(matrix);
           

       // 
    
    /**
     * @return The angle of the unit circle with the image view's center
     */
    private double getAngle(double xTouch, double yTouch) 

        double delta_x = xTouch - (dialerWidth) /2;
        double delta_y = (dialerHeight) /2 - yTouch;
        double radians = Math.atan2(delta_y, delta_x);

        double dx = xTouch - dWidth;
        double dy = (dHeight - ((dialerHeight) /2)) -  yTouch;
        double dRadi = Math.atan2(dy, dx);
        //Log.e("MY degree", String.valueOf( Math.toDegrees(dRadi)));
        //return Math.toDegrees(dRadi);
        return Math.toDegrees(radians);
    



    /**
     * Simple implementation of an @link OnTouchListener for registering the dialer's touch events. 
     */
    private class MyOnTouchListener implements OnTouchListener 

        private double startAngle;

        @Override
        public boolean onTouch(View v, MotionEvent event) 

            switch (event.getAction()) 

                case MotionEvent.ACTION_DOWN:

                    // reset the touched quadrants
                    /*for (int i = 0; i < quadrantTouched.length; i++) 
                        quadrantTouched[i] = false;
                    */

                    //allowRotating = false;

                    startAngle = getAngle(event.getX(), event.getY());
                    break;

                case MotionEvent.ACTION_MOVE:
                    /*double rotationAngleRadians = Math.atan2(event.getX() - (dialer.getWidth() / 2 ),     ( (dialer.getHeight() / 2 ) - event.getY()));
                    double angle = (int) Math.toDegrees(rotationAngleRadians);
                    Log.i("gg", "rotaion angle"+angle);*/

                    double currentAngle = getAngle(event.getX(), event.getY());
                    //if(currentAngle < 130 || currentAngle < 110)
                        //Log.e("Start angle :"+startAngle, "Current angle:"+currentAngle);
                        rotateDialer((float) (startAngle - currentAngle));
                        startAngle = currentAngle;
                    //


                    //Log.e("MOVE start Degree:"+startAngle, "Current Degree :"+currentAngle);
                    break;

                case MotionEvent.ACTION_UP:
                    //allowRotating = true;
                    break;
            

            // set the touched quadrant to true
            //quadrantTouched[getQuadrant(event.getX() - (dialerWidth / 2), dialerHeight - event.getY() - (dialerHeight / 2))] = true;

            //detector.onTouchEvent(event);

            return true;
        
    

【问题讨论】:

问题解决了吗?你能分享你的解决方案吗?谢谢。 【参考方案1】:

我不明白你的问题。下面的代码将图像旋转 48 度。

ImageView dialer = (ImageView) findViewById(R.id.imageView_ring);

int degrees = 48;
Matrix matrix = new Matrix();
matrix.setRotate(degrees);
Bitmap bmpBowRotated = Bitmap.createBitmap(imageOrginal, 0, 0, imageOrginal.getWidth(),imageOrginal.getHeight(), matrix, false);

dialer.setImageBitmap(bmpBowRotated);

【讨论】:

是的,我知道,但我的代码工作正常,但它可以 360 度旋转图像,但我只想根据用户旋转特定度数移动图像。在我的图像中,仅将图像旋转 1 到 10。【参考方案2】:

嗨 Girish 有一个名为 RotateAnimation 的类,通过使用这个类你可以轻松做到

     look Example like

      RotateAnimation r = new RotateAnimation(0f, -90f,200,200); // HERE 
      r.setStartOffset(1000);
      r.setDuration(1000);
      r.setFillAfter(true); //HERE
      animationSet.addAnimation(r);

【讨论】:

【参考方案3】:

我想先了解部署的内容是什么?它允许操纵事件吗?如果是,那么您将处理 ManipulationStatring 和 ManipulationDelta 事件来旋转元素。 如果 Manipulation 不可用的情况并非如此,那么您可以尝试 RenderTransformation 属性和元素的 RorateTransform 是否正在使用 WPf。

【讨论】:

【参考方案4】:

我可以通过对您的代码进行以下一些调整来实现这一点

    让用户准确点击箭头总是以获得放置箭头的初始角度,在您的情况下为 90 度,否则返回 false

    还保存用户移开手指的角度,并将该角度用作下一次触摸的初始值,例如,如果他将箭头置于 100 度,则表示他的初始触摸再次激活旋转的位置

    现在检查他的答案,取你的数字 0 到 9 的角度,我猜你的值从 0 到 9 取 120 度,将该角度除以 10,你可以很容易地找到找出什么角度代表什么价值,得到你的结果

同样以 90 度准确触摸开始旋转非常烦人,因此请始终检查 bw 90+4 和 90-4 的值以开始旋转,但始终使用 90 作为开始角度

【讨论】:

以上是关于以有限度数旋转表盘的主要内容,如果未能解决你的问题,请参考以下文章

想问一个关于数学旋转的问题,就是在直角坐标系中,某个点绕原点旋转某个度数,应该怎么求出旋转后的度数

在 WPF 中向鼠标旋转图形(如模拟表盘)

在 JavaScript 中旋转人脸 (Node.js 10)

变换上的 CSS3 动画:旋转。获取旋转元素当前度数的方法?

使用具有特定度数的jQuery旋转DIV? [复制]

div旋转做仪表盘和进度——Mask