Android 自定義感光器控件SolarProgressView,也可當做普通ProgressBar使用

Android 自定義感光器控件SolarProgressView,也可當做普通ProgressBar使用


本文出處: http://blog.csdn.net/qq_27512671/article/details/76020265
完整代碼獲取:https://github.com/miqt/SolarProgressView
實現效果:


實現思路:

①光線強度數據的獲取:Android光線傳感器
②光線強度的UI展示:自定義SolarProgressView
③光線數據源 --> UI展示需要數據的轉化: 數據梯度設置
④其他動畫效果的實現:光線強度增加的過度動畫

光線強度數據的獲取:Android光線傳感器

Android光線傳感器是Android獲取周圍環境光的感光器元件,通過註冊感光器的傳感器監聽,我們就可以通過傳感器傳過來的數值。

獲得光線傳感器實例:

manager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensor = manager.getDefaultSensor(Sensor.TYPE_LIGHT);

註冊監聽器開始監聽傳感器數據

 private SensorEventListener listener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {       
                        //取得數據
            Log.i("sensor_Data","\naccuracy : " + event.accuracy
            + "\ntimestamp : " + event.timestamp
            + "\nvalues : " + Arrays.toString(event.values));
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    };
@Override
protected void onResume() {
    super.onResume();
    manager.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_NORMAL);
}

在合適的時機取消註冊,節省資源佔用:

@Override
protected void onPause() {
    super.onPause();
    manager.unregisterListener(listener);
}

光線強度的UI展示:自定義SolarProgressView

在用來展示的數據都準備好了之後,我們就要考慮如何將這些被整理好的數據展示出來了,實際上我們使用一個ProgressBar來展示即可,但考慮到展示的美觀性和光線強弱變化的動畫交互,我們決定自定義一個類似於太陽花的自定義控件來展示數據。並且這個自定義控件具有同ProgressBar類似的屬性,比如max(最大值)、progress(當前值)等等。
有了明確的想法之後,開始開發:

package com.mqt.solarprogressview;

import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.support.annotation.ColorInt;
import android.support.annotation.FloatRange;
import android.support.annotation.IntRange;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.AnimationSet;
import android.view.animation.DecelerateInterpolator;


public class SolarView extends View {

    private int mColor = Color.RED;
    private int mMax = 9;
    private int mPregress = 0;

    private float mLolarScale;
    private int mLightRadius;
    private ObjectAnimator mScaleAnim;
    private int mLightRadiusAnim;
    private ObjectAnimator mColorAnim;

    public SolarView(Context context) {
        super(context);
        init(null, 0);
    }

    public SolarView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs, 0);
    }

    public SolarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(attrs, defStyle);
    }

    private void init(AttributeSet attrs, int defStyle) {
        // Load attributes
        final TypedArray a = getContext().obtainStyledAttributes(
                attrs, R.styleable.SolarView, defStyle, 0);

        mColor = a.getColor(R.styleable.SolarView_color, Color.RED);
        mMax = a.getInt(R.styleable.SolarView_max, 9);
        mPregress = a.getInt(R.styleable.SolarView_pregress, 0);
        mLolarScale = a.getFloat(R.styleable.SolarView_solarScale, 0.25F);
        mLightRadius = a.getDimensionPixelSize(R.styleable.SolarView_lightRadius, 10);
        mLightRadiusAnim = mLightRadius;
        a.recycle();
        initPaint();
        initAnim();
    }

    private void initAnim() {
        mScaleAnim = ObjectAnimator
                .ofInt(this, "mLightRadiusAnim", mLightRadius / 2, mLightRadius, mLightRadius * 2, mLightRadius);
        mScaleAnim.setDuration(1200);//設置動畫時間
        mScaleAnim.setInterpolator(new DecelerateInterpolator());//設置動畫插入器,減速
        mScaleAnim.setRepeatCount(0);//設置動畫重複次數,這裏-1代表無限
        mScaleAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mLightRadiusAnim = value;
                postInvalidate();
            }
        });

        mColorAnim = ObjectAnimator
                .ofArgb(this, "mColor", mColor, Color.rgb(250, 128, 10), mColor);
        mColorAnim.setDuration(1200);//設置動畫時間
        mColorAnim.setInterpolator(new DecelerateInterpolator());//設置動畫插入器,減速
        mColorAnim.setRepeatCount(0);//設置動畫重複次數,這裏-1代表無限
        mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mColor = value;
                postInvalidate();
            }
        });
    }

    private int radumColor() {
        return Color.rgb((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
    }

    Paint mPaint;

    private void initPaint() {
        mPaint = new Paint();
        mPaint.setColor(mColor);
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //實心圓,直徑爲控件的最小寬高的一半
        int min = Math.min(getWidth(), getHeight());
        float r = min * mLolarScale;
        mPaint.setColor(mColor);
        canvas.drawCircle(getWidth() / 2, getHeight() / 2, r, mPaint);
        for (int i = 0; i < mMax && i < mPregress; i++) {
            canvas.save();
            canvas.rotate((360f / mMax) * i, getWidth() >> 1, getHeight() >> 1);
            if (i == mPregress - 1) {
                canvas.drawCircle(getWidth() >> 1, getHeight() >> 3, mLightRadiusAnim, mPaint);
            } else {
                canvas.drawCircle(getWidth() >> 1, getHeight() >> 3, mLightRadius, mPaint);
            }
            canvas.restore();
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int minw = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
        int w = resolveSizeAndState(minw, widthMeasureSpec, 0);
        int minh = getPaddingLeft() + getPaddingRight() + getSuggestedMinimumWidth();
        int h = resolveSizeAndState(minh, heightMeasureSpec, 0);
        setMeasuredDimension(w, h);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
    }

    public int getColor() {
        return mColor;
    }

    public void setColor(@ColorInt int color) {
        this.mColor = color;
        postInvalidate();
    }

    public int getMax() {
        return mMax;
    }

    public void setMax(@IntRange(from = 0) int max) {
        this.mMax = max;
        postInvalidate();
    }

    public int getPregress() {
        return mPregress;
    }

    public void setPregress(@IntRange(from = 0) int pregress) {
        if (pregress > this.mPregress) {
            mColorAnim.cancel();
            mColorAnim.start();
            mScaleAnim.cancel();
            mScaleAnim.start();//啓動動畫
        }
        this.mPregress = pregress;

    }

    public float getLolarScale() {
        return mLolarScale;
    }

    public void setLolarScale(@FloatRange(from = 0, to = 1) float lolarScale) {
        this.mLolarScale = lolarScale;
        postInvalidate();
    }

    public int getLightRadius() {
        return mLightRadius;
    }

    public void setLightRadius(@IntRange(from = 0) int lightRadius) {
        this.mLightRadius = lightRadius;
        postInvalidate();
    }
}

自定義屬性XML:

<resources>
    <declare-styleable name="SolarView">
        <attr name="max" format="integer" />
        <attr name="pregress" format="integer" />
        <attr name="color" format="color" />
        <attr name="solarScale" format="float" />
        <attr name="lightRadius" format="dimension" />
    </declare-styleable>
</resources>

光線數據源 --> UI展示需要數據的轉化: 數據梯度設置

在我們獲取到傳感器給出的光照強度數據源後,我們無法直接使用,因爲光線給出的數據與UI展示需要的數據不同,因此我們需要下轉化,直接在傳感器監聽中添加代碼即可:

private SensorEventListener listener = new SensorEventListener() {
        @Override
        public void onSensorChanged(SensorEvent event) {
            float intensity = event.values[2];
            int size = (int) ((intensity * sv_light.getMax() / 120));
            sv_light.setPregress(size);
            textView.setText(
                    "\naccuracy : " + event.accuracy
                            + "\ntimestamp : " + event.timestamp
                            + "\nvalues : " + Arrays.toString(event.values)
            );
        }

        @Override
        public void onAccuracyChanged(Sensor sensor, int accuracy) {

        }
    };

其他動畫效果的實現:光線強度增加的過度動畫

光線傳感器數據獲取完成了,用於UI展示的自定義控件也寫好了,數據也匹配好了可以正常展示了,但我們可能還不會滿足,我們可能還希望給這個自定義控件在傳感器數據發生變化的時候,有一個動畫出來。這個動畫其實在上面粘貼的代碼中已經有了,這裏只是介紹一下給這個自定義view添加動畫的思路:
我們知道自定義view的所有的視圖都是在onDraw(Canvas canvas)方法中用Canvas畫出來的,而化成什麼樣子又是其中的各種參數控制的,例如我們在自定義控件中畫一個圓,我們只需要動態的改變這個圓的半徑,就能達到這個圓的放大縮小的目的,當我們有規律的並且足夠頻繁改變這個值,就可以達到平滑的動態效果了。例如實現控件中控件隨progress的變化顏色漸變:

  mColorAnim = ObjectAnimator
                .ofArgb(this, "mColor", mColor, Color.rgb(250, 128, 10), mColor);
        mColorAnim.setDuration(1200);//設置動畫時間
        mColorAnim.setInterpolator(new DecelerateInterpolator());//設置動畫插入器,減速
        mColorAnim.setRepeatCount(0);//設置動畫重複次數,這裏-1代表無限
        mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                int value = (int) animation.getAnimatedValue();
                mColor = value;
                postInvalidate();//重新繪製控件
            }
        });

這樣,我們的自定義感光器控件的“感光”、“數據展示”和一切其他的動畫效果,就達成了,乾杯!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章