7

我已经编写了一个应用程序,其中我有speedometera needle,并且我试图center随着速度的变化围绕它旋转针。

我从那个角度计算来自互联网的下载速度的速度值也测量,

我的问题是如何将指针一端固定在车速表的中心?

如何随着角度值的变化旋转针?

我被期望这样

在此处输入图像描述

这里的针不固定中心位置如何像第一个图像中心一样固定图像的针中心

在此处输入图像描述

4

2 回答 2

16

这是我写的自定义 GaugeView:GaugeView.java

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.RotateAnimation;
import android.view.animation.Transformation;
import android.widget.AbsoluteLayout;
import android.widget.LinearLayout;
@SuppressWarnings("deprecation")

public class GaugeView extends LinearLayout{

private int gaugeDiameter;      //Gauge radius
private int gaugeBackground;    //Gauge background resource
private int needleBackground;   //Needle background resource
private int needleWidth;        //Needle width
private int needleHeight;       //Needle height
private int needleX;            //Needle X position
private int needleY;            //Needle Y position
private int needleDeltaX;       //Needle's X position from the centre of gauge
private int needleDeltaY;       //Needle's Y position from the centre of gauge
private int deflectTime;        //Animation time when needle deflects to a higher angle
private int releaseTime;        //Animation time when needle deflects to a lower angle
private int pivotX;             //Needles X Axis of rotation
private int pivotY;             //Needles Y Axis of rotation
private int deltaXAxis;         //Needles new X Axis of rotation
private int deltaYAxis;         //Needles new Y Axis of rotation
private float currentValue;     //Current needle value
private float minValue;         //Minimum needle value
private float maxValue;         //Maximum needle value
private float currentAngle;     //Current angular position of needle(Used in rotate animation)
private float previousAngle;    //To store last known angular position of needle(Used in rotate animation)
private float minAngle;         //Minimum angle of needle
private float maxAngle;         //Maximum angle of needle
private float currentDegrees;   //Current angular position of needle

private boolean animateDeflect; //Enable/Disable rotate animation

NeedleDeflectListener NDL;
public interface NeedleDeflectListener { 
    /**Called when needle value or angle is changed*/
    public void onDeflect(float angle,float value);

    }

/**Register a callback to be invoked when the needle value/angle is changed.*/
public void setOnNeedleDeflectListener(NeedleDeflectListener eventListener) {
    NDL=eventListener;
    }

private AbsoluteLayout guageBack;
private LinearLayout gaugeNeedle;

/**Custom view used for creating analog gauges like speedometer*/
public GaugeView(Context context, AttributeSet attrs) {
    super(context, attrs);
    if(!isInEditMode()){
        LayoutInflater layoutInflater = (LayoutInflater)context
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        layoutInflater.inflate(R.layout.gauge_layout,this);
        initView(); 
    }

}


private void initView() // Initializes the view
 {
    this.gaugeBackground=R.drawable.gauge_gradient;
    this.needleBackground=R.drawable.needle_gradient;
    this.gaugeDiameter=0;
    this.needleWidth=0;
    this.needleHeight=0;
    this.needleX=0;
    this.needleY=0;
    this.needleDeltaX=0;
    this.needleDeltaY=0;
    this.currentValue=0;
    this.minValue=0;
    this.maxValue=100;
    this.currentAngle=0;
    this.minAngle=0;
    this.maxAngle=360;
    this.deflectTime=0;
    this.releaseTime=0;
    this.pivotX=0;
    this.pivotY=0;
    this.previousAngle=0;
    this.deltaXAxis=0;
    this.deltaYAxis=0;
    this.currentDegrees=0;
    this.animateDeflect=true;
    this.gaugeNeedle=(LinearLayout)findViewById(R.id.gaugeNeedleLay);
    this.guageBack=(AbsoluteLayout) findViewById(R.id.gaugeFrame);
    this.guageBack.setBackgroundResource(gaugeBackground);
    this.gaugeNeedle.setBackgroundResource(needleBackground);
    this.gaugeNeedle.bringToFront();
 }


/**Sets a background resource for the gauge*/
public void setGaugeBackgroundResource(int resID)
 {
    gaugeBackground=resID;
    guageBack.setBackgroundResource(0);
    guageBack.setBackgroundResource(gaugeBackground);
    guageBack.refreshDrawableState();
 }


/**Sets the Diameter of the gauge*/
public void setDiameter(int diameter)
 {
     gaugeDiameter=diameter;
     guageBack.setLayoutParams(new android.widget.LinearLayout.LayoutParams(gaugeDiameter,gaugeDiameter));  
 }


/**Sets a background resource for the needle*/
public void setNeedleBackgroundResource(int resID)
{
    needleBackground=resID;
    gaugeNeedle.setBackgroundResource(needleBackground);
}


/**Creates a needle at the centre of the gauge.
<br> <b>deltaX</b>: Adjusts needle's X position from the centre of gauge
<br> <b>deltaY</b>: Adjusts needle's Y position from the centre of gauge*/
public void createNeedle(int width,int height,int deltaX,int deltaY)
{
    this.needleWidth=width;
    this.needleHeight=height;
    this.needleDeltaX=deltaX;
    this.needleDeltaY=deltaY;

    this.needleX=guageBack.getLeft()+(gaugeDiameter/2)+needleDeltaX-needleWidth/2;
    this.needleY=guageBack.getTop()+(gaugeDiameter/2)+needleDeltaY;

    this.pivotX=needleWidth/2;
    this.pivotY=Math.abs(needleDeltaY);

    AbsoluteLayout.LayoutParams params = new AbsoluteLayout.LayoutParams(this.needleWidth,this.needleHeight,this.needleX,this.needleY);
    gaugeNeedle.setLayoutParams(params);
}

/**Sets a reference background for the gauge*/
public void setReferenceBackground()
{
    guageBack.setBackgroundResource(R.drawable.degrees);
}

/**Removes the reference background of the gauge*/
public void removeReferenceBackground()
{
    guageBack.setBackgroundResource(this.gaugeBackground);
}

/**Sets the current needle value*/
public void setCurrentValue(float value)
{

    if(value>maxValue)
        this.currentValue=maxValue;
    else if(value<minValue)
        this.currentValue=minValue;
    else
        this.currentValue=value;

    this.currentAngle=(((this.currentValue-this.minValue)*(this.maxAngle-this.minAngle))
                                            /(this.maxValue-this.minValue))+this.minAngle;
    setCurrentAngle(this.currentAngle);

}

/**Sets the needle value range*/
public void setValueRange(float min_Value,float max_Value)
{
    this.minValue=min_Value;
    this.maxValue=max_Value;

}

/**Sets the needle angle range (0-360)*/
public void setAngleRange(float min_Angle,float max_Angle)
{
    if(min_Angle<0)
        min_Angle=0;
    if(max_Angle>360)
        max_Angle=360;
    this.minAngle=min_Angle;
    this.maxAngle=max_Angle;
}

/**Sets the current needle angle*/
public void setCurrentAngle(float angle)
{
    if(angle>maxAngle)
        this.currentAngle=maxAngle;
    else if(angle<minAngle)
        this.currentAngle=minAngle;
    else
        this.currentAngle=angle;


    RotateAnimation needleDeflection=new RotateAnimation(this.previousAngle, this.currentAngle,this.pivotX,this.pivotY){
         protected void applyTransformation(float interpolatedTime,Transformation t) {
             currentDegrees=previousAngle+(currentAngle-previousAngle)*interpolatedTime;
             currentValue=(((currentDegrees-minAngle)*(maxValue-minValue))/(maxAngle-minAngle))+minValue;
             if(NDL!=null) 
                 NDL.onDeflect(currentDegrees,currentValue);
             super.applyTransformation(interpolatedTime, t);
         }

    };

    needleDeflection.setAnimationListener(new AnimationListener() {@Override
        public void onAnimationStart(Animation arg0) {}@Override
        public void onAnimationRepeat(Animation arg0) {}@Override
        public void onAnimationEnd(Animation arg0) {previousAngle=currentAngle;}});


    if(currentAngle>this.previousAngle)
        needleDeflection.setDuration(this.deflectTime); 
    else
        needleDeflection.setDuration(this.releaseTime);

    if(!animateDeflect)
        needleDeflection.setDuration(0);

    needleDeflection.setFillAfter(true);
    this.gaugeNeedle.startAnimation(needleDeflection);
    this.gaugeNeedle.refreshDrawableState();
}

/**Sets the needle's animation time
<br> <b>deflectTime</b>: Time taken by the needle to deflect to a higher value/angle
<br> <b>releaseTime</b>: Time taken by the needle to deflect to a lower value/angle*/
public void setAnimationTime(int deflectTime,int releaseTime)
{
    this.releaseTime=releaseTime;
    this.deflectTime=deflectTime;
}

/**Sets the axis of needle rotation with respect to the centre of gauge*/
public void setDeltaAxis(int deltaX,int deltaY)
{
    this.deltaXAxis=deltaX;
    this.deltaYAxis=deltaY;
    this.pivotX=(needleWidth/2)+deltaXAxis;
    this.pivotY=deltaYAxis;
}

/**Returns the current needle angle*/
public float getCurrentAngle()
{
    return this.currentDegrees;
}

/**Returns the Background resource ID of the gauge*/
public int getGaugeBackgroundResource()
{
    return this.gaugeBackground;
}

/**Returns the Diameter of the gauge*/
public int getDiameter()
{
    return this.gaugeDiameter;
}

/**Returns the Background resource ID of the needle*/
public int getNeedleBackgroundResource()
{
    return this.needleBackground;
}

/**Returns the current needle value*/
public float getCurrentValue()
{
    return this.currentValue;
}

/**Returns the needle width*/
public int getNeedleWidth()
{
    return this.needleWidth;
}

/**Returns the needle height*/
public int getNeedleHeight()
{
    return this.needleHeight;
}

/**Returns the X position of needle*/
public int getNeedlePositionX()
{
    return this.needleX;
}

/**Returns the Y position of needle*/
public int getNeedlePositionY()
{
    return this.needleY;
}

/**Returns the X axis of rotation of needle*/
public int getNeedleAxisX()
{
    return this.pivotX;
}

/**Returns the X axis of rotation of needle*/
public int getNeedleAxisY()
{
    return this.pivotY;
}

/**Returns the minimum needle value*/
public float getMinValue()
{
    return this.minValue;
}

/**Returns the maximum needle value*/
public float getMaxValue()
{
    return this.maxValue;
}

/**Returns the minimum needle angle*/
public float getMinAngle()
{
    return this.minAngle;
}

/**Returns the maximum needle angle*/
public float getMaxAngle()
{
    return this.maxAngle;
}

/**Returns the needle deflect time*/
public int getDeflectTime()
{
    return this.deflectTime;
}

/**Returns the needle release time*/
public int getReleaseTime()
{
    return this.releaseTime;
}

/**Enable/disable needle animation*/
public void setNeedleAnimation(boolean EnableAnimation)
{
    this.animateDeflect=EnableAnimation;

}

/**Returns needle animation state*/
public boolean getNeedletAnimation()
{
    return this.animateDeflect;

}

}

gauge_layout.xml布局xml文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<AbsoluteLayout
    android:id="@+id/gaugeFrame"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" 
    android:scaleType="fitXY" >
    <LinearLayout
        android:id="@+id/gaugeNeedleLay"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:orientation="vertical"
        android:scaleType="fitXY" >
    </LinearLayout>
</AbsoluteLayout></LinearLayout>

实现示例:

final GaugeView gv=(GaugeView)findViewById(R.id.gaugeView1);
gv.setBackgroundResource(R.drawable.gaugeback_image);
gv.setNeedleBackgroundResource(R.drawable.needle_image);
gv.setDiameter(460);
gv.createNeedle(50,230,0,-20);
gv.setAngleRange(45,315);

更新:

AbsoluteLayout已弃用。您可以将其替换为RelativeLayout和一些代码更改。

于 2013-01-09T06:03:11.787 回答
5

有很多方法可以做到这一点。一些可能对您有帮助的线程:

Android:将imageview中的图像旋转一个角度

安卓车速表(针规)

Android仪表动画问题

这是我发现的一个很好的教程,它使用类似的动画制作老式温度计:

http://mindtherobot.com/blog/272/android-custom-ui-making-a-vintage-thermometer/

于 2013-01-09T05:34:20.387 回答