一个简单的自定义Compass

来源:互联网 发布:ubuntu snmpwalk 安装 编辑:程序博客网 时间:2024/06/14 20:00

Android提供了sensor(传感器)sensorManager(传感器管理器)类,我们可以对相关服务的注册来进行传感器(加速度,磁场,方向等)的开发。

现在利用传感器写一个简单的指南针功能demo。

首先自定义一个旋转的imageview(存放根据水平度数变化的图片),

代码如下:

CompassView.java;

public class CompassView extends ImageView{

    private Drawable compass; //旋转的图片资源

    private float mDirection ; //方向旋转浮点数   

public CompassView(Context context) {

        super(context);

        mDirection = 0f;    //默认不旋转

        compass=null;

    }  

  public CompassView(Context context, @Nullable AttributeSet attrs) {

        super(context, attrs);

        mDirection = 0f;

        compass=null;

    }

    public CompassView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);

        mDirection=0f;

        compass=null;

    }

  @Override

    protected void onDraw(Canvas canvas) {

        super.onDraw(canvas);

        if(compass==null){

            compass=getDrawable();  //获取当前view资源

            compass.setBounds(0,0,getHeight(),getWidth());      //充满当前view

        }

        canvas.save();

        canvas.rotate(mDirection ,getHeight() / 2,getWidth() / 2);   //图片绕中心旋转

        compass.draw(canvas);  //将旋转之后的图片画出来,保持旋转之后的样子

        canvas.restore();    //保存一下

    }

    /**

     * 自定义旋转方法

     * @param direction

      *

      * */

    public void updateDirection(float direction){

        mDirection=direction;

        invalidate();   //旋转之后重新刷新

    }

然后就是具体功能的实现了,也是重点。界面很简单,直接在MainActivity内实现所有功能包括引导动画,传感器的应用,主界面ui更新等

代码如下:

MainActivity.java;

public class MainActivity extends Activity(){

 private static final int EXIT_TIME = 2000;// 两次按返回键的间隔判断
    private final float MAX_ROATE_DEGREE = 1.0f;// 最多旋转一周,即360°
    private SensorManager mSensorManager;// 传感器管理对象
    private Sensor mOrientationSensor;// 传感器对象
    private float mDirection;// 当前浮点方向
    private float mTargetDirection;// 目标浮点方向
    private AccelerateInterpolator mInterpolator;// 动画从开始到结束,变化率是一个加速的过程,就是一个动画速率
    protected final Handler mHandler = new Handler();
    private boolean mStopDrawing;// 是否停止指南针旋转的标志位
    private long firstExitTime = 0L;// 用来保存第一次按返回键的时间

    private final float MAX_ROATE_DEGREE = 1.0f;// 最多旋转一周,即360°

    private SensorManager mSensorManager;// 传感器管理对象

    private Sensor mOrientationSensor;// 传感器对象

    private float mDirection;// 当前浮点方向

    private float mTargetDirection;// 目标浮点方向

    private AccelerateInterpolator mInterpolator;// 动画从开始到结束,变化率是一个加速的过程,就是一个动画速率

    protected final Handler mHandler = new Handler();

    private boolean mStopDrawing;// 是否停止指南针旋转的标志位

    private long firstExitTime = 0L;// 用来保存第一次按返回键的时间

 View mCompassView;  // 实际上是一个LinearLayout,装指南针ImageView和位置TextView

    CompassView mPointer;// 自定义的指南针view

    LinearLayout mDirectionLayout;// 顶部显示方向名称(东南西北)的LinearLayout

    LinearLayout mAngleLayout;  // 顶部显示方向具体度数的LinearLayout

    View mViewGuide;        //引导页

    ImageView mGuideAnimation;  //引导动画图片

protected Handler invisiableHandler = new Handler() {

        public void handleMessage(Message msg) {

            mViewGuide.setVisibility(View.GONE);

        }

    };

 @Override

    protected void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        initViews();// 初始化view

        initServices();// 初始化传感器和位置服务

    }


    public void onWindowFocusChanged(boolean hasFocus) {

        AnimationDrawable anim = (AnimationDrawable) mGuideAnimation

                .getDrawable();

        anim.start();

    }

 

  /**

     * 更新指南针旋转的线程,每20毫秒检测方向变化值,对应更新指南针旋转

     * */

  protected Runnable mCompassViewUpdater = new Runnable() {

      @Override

        public void run() {

            if (mPointer != null && !mStopDrawing) {

                if (mDirection != mTargetDirection) {

 // 计算短程序

                    float to = mTargetDirection;

                    if (to - mDirection > 180) {

                        to -= 360;

                    } else if (to - mDirection < -180) {

                        to += 360;

                    }

      float distance = to - mDirection;

                    if (Math.abs(distance) > MAX_ROATE_DEGREE) {

                        distance = distance > 0 ? MAX_ROATE_DEGREE

                                : (-1.0f * MAX_ROATE_DEGREE);

                    }

                    // 加速动画去旋转图片,细节处理

                    mDirection = normalizeDegree(mDirection

                            + ((to - mDirection) * mInterpolator

                            .getInterpolation(Math.abs(distance) > MAX_ROATE_DEGREE ? 0.4f

                                    : 0.3f)));

                    mPointer.updateDirection(mDirection);// 更新指南针旋转

                }

updateDirection();// 更新方向值

                mHandler.postDelayed(mCompassViewUpdater, 20);// 20毫米后重新执行自己

            }

        }

    };

/**

     * 初始化view

    * */

    private void initViews() {

        mViewGuide = findViewById(R.id.view_guide);

        mViewGuide.setVisibility(View.VISIBLE);

        invisiableHandler.sendMessageDelayed(new Message(), 3000);

        mGuideAnimation = (ImageView) findViewById(R.id.guide_animation);

        mDirection = 0.0f;// 初始化起始方向

        mTargetDirection = 0.0f;// 初始化目标方向

        mInterpolator = new AccelerateInterpolator();// 实例化加速动画对象

        mStopDrawing = true;

        mCompassView = findViewById(R.id.view_compass);

        mPointer = (CompassView) findViewById(R.id.compass_pointer);

        mDirectionLayout = (LinearLayout) findViewById(R.id.layout_direction);

        mAngleLayout = (LinearLayout) findViewById(R.id.layout_angle);

    }

  // 初始化传感器

    private void initServices() {

        mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);

        mOrientationSensor = mSensorManager.getSensorList(

                Sensor.TYPE_ORIENTATION).get(0);

    }

/**

     * 监听返回键

      * */

  @Override

    public void onBackPressed() {// 覆盖返回键

        long curTime = System.currentTimeMillis();

        if (curTime - firstExitTime < EXIT_TIME) {// 两次按返回键的时间小于2秒就退出应用

            finish();

        } else {

            Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show();

            firstExitTime = curTime;

        }

    }

 /**

     *  在恢复的生命周期里判断、启动传感器服务

     * */

    @Override

    protected void onResume() {

        super.onResume();

        if (mOrientationSensor != null) {

            mSensorManager.registerListener(mOrientationSensorEventListener,

                    mOrientationSensor, SensorManager.SENSOR_DELAY_GAME);

        }

        mStopDrawing = false;

        mHandler.postDelayed(mCompassViewUpdater, 20);// 20毫秒执行一次更新指南针图片旋转

    }

/**

    * 在暂停的生命周期里注销传感器服务

     * */

    @Override

    protected void onPause() {

        super.onPause();

        mStopDrawing = true;

        if (mOrientationSensor != null) {

            mSensorManager.unregisterListener(mOrientationSensorEventListener);

        }

    }

// 更新顶部方向显示的方法

    private void updateDirection() {

        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,

                LayoutParams.WRAP_CONTENT);

        // 先移除layout中所有的view

        mDirectionLayout.removeAllViews();

        mAngleLayout.removeAllViews();

        // 根据mTargetDirection,作方向名称图片的处理

        ImageView east = null;

        ImageView west = null;

        ImageView south = null;

        ImageView north = null;

        float direction = normalizeDegree(mTargetDirection * -1.0f);

  if (direction > 22.5f && direction < 157.5f) {

            // east

            east = new ImageView(this);

            east.setImageResource( R.drawable.e);

            east.setLayoutParams(lp);

        } else if (direction > 202.5f && direction < 337.5f) {

            // west

            west = new ImageView(this);

            west.setImageResource(R.drawable.w);

            west.setLayoutParams(lp);

        }

if (direction > 112.5f && direction < 247.5f) {

            // south

            south = new ImageView(this);

            south.setImageResource(R.drawable.s);

            south.setLayoutParams(lp);

        } else if (direction < 67.5 || direction > 292.5f) {

            // north

            north = new ImageView(this);

            north.setImageResource(R.drawable.n);

            north.setLayoutParams(lp);

        }

if (south != null) {

                mDirectionLayout.addView(south);

}

            if (north != null) {

                mDirectionLayout.addView(north);

            }

            if (east != null) {

                mDirectionLayout.addView(east);

            }

            if (west != null) {

              mDirectionLayout.addView(west);

            }

// 下面是根据方向度数显示度数图片数字

        int direction2 = (int) direction;

        boolean show = false;

        if (direction2 >= 100) {

            mAngleLayout.addView(getNumberImage(direction2 / 100));

          direction2 %= 100;

            show = true;

        }

        if (direction2 >= 10 || show) {

            mAngleLayout.addView(getNumberImage(direction2 / 10));

            direction2 %= 10;

        }

        mAngleLayout.addView(getNumberImage(direction2));

        // 下面是增加一个°的图片

        ImageView degreeImageView = new ImageView(this);

        degreeImageView.setImageResource(R.drawable.degree);

        degreeImageView.setLayoutParams(lp);

        mAngleLayout.addView(degreeImageView);

    }

/**

    * 获取方向度数对应的图片,返回ImageView

    * */

    private ImageView getNumberImage(int number) {

        ImageView image = new ImageView(this);

        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,

                LayoutParams.WRAP_CONTENT);

        switch (number) {

switch (number) {

            case 0:

                image.setImageResource(R.drawable.number_0);

                break;

            case 1:

                image.setImageResource(R.drawable.number_1);

                break;

            case 2:

                image.setImageResource(R.drawable.number_2);

                break;

            case 3:

                image.setImageResource(R.drawable.number_3);

                break;

            case 4:

                image.setImageResource(R.drawable.number_4);

                break;

            case 5:

                image.setImageResource(R.drawable.number_5);

                break;

            case 6:

                image.setImageResource(R.drawable.number_6);

                break;

case 7:

                image.setImageResource(R.drawable.number_7);

                break;

            case 8:

                image.setImageResource(R.drawable.number_8);

                break;

            case 9:

                image.setImageResource(R.drawable.number_9);

                break;

        }

        image.setLayoutParams(lp);

        return image;

    }

 /**

     * 方向传感器变化监听

     * */

    private SensorEventListener mOrientationSensorEventListener = new SensorEventListener() {

        @Override

        public void onSensorChanged(SensorEvent event) {

            float direction = event.values[mSensorManager.DATA_X] * -1.0f;

            mTargetDirection = normalizeDegree(direction);// 赋值给全局变量,让指南针旋转

        }

        @Override

        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }

    };

/**

     * 调整方向传感器获取的值

     */

    private float normalizeDegree(float degree) {

        return (degree + 720) % 360;

    }

}

功能代码就这些,主界面上方是一个显示方向的LinearLayout,中间用来存放罗盘图片的自定义compassview,图片上是动态改变显示度数的LinearLayout,资源文件就不贴了。

效果图: