Android 自定義感光器控件SolarProgressView,也可當(dāng)做普通ProgressBar使用
本文出處: http://blog.csdn.net/qq_27512671/article/details/76020265
完整代碼獲让邸:https://github.com/miqt/SolarProgressView
實現(xiàn)效果:
實現(xiàn)思路:
①光線強度數(shù)據(jù)的獲劝蚶骸:Android光線傳感器
②光線強度的UI展示:自定義SolarProgressView
③光線數(shù)據(jù)源 --> UI展示需要數(shù)據(jù)的轉(zhuǎn)化: 數(shù)據(jù)梯度設(shè)置
④其他動畫效果的實現(xiàn):光線強度增加的過度動畫
光線強度數(shù)據(jù)的獲取:Android光線傳感器
Android光線傳感器是Android獲取周圍環(huán)境光的感光器元件誓竿,通過注冊感光器的傳感器監(jiān)聽,我們就可以通過傳感器傳過來的數(shù)值筷屡。
獲得光線傳感器實例:
manager = (SensorManager) getSystemService(SENSOR_SERVICE);
sensor = manager.getDefaultSensor(Sensor.TYPE_LIGHT);
注冊監(jiān)聽器開始監(jiān)聽傳感器數(shù)據(jù)
private SensorEventListener listener = new SensorEventListener() {
@Override
public void onSensorChanged(SensorEvent event) {
//取得數(shù)據(jù)
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);
}
在合適的時機取消注冊,節(jié)省資源占用:
@Override
protected void onPause() {
super.onPause();
manager.unregisterListener(listener);
}
光線強度的UI展示:自定義SolarProgressView
在用來展示的數(shù)據(jù)都準(zhǔn)備好了之后毙死,我們就要考慮如何將這些被整理好的數(shù)據(jù)展示出來了,實際上我們使用一個ProgressBar來展示即可规哲,但考慮到展示的美觀性和光線強弱變化的動畫交互诽表,我們決定自定義一個類似于太陽花的自定義控件來展示數(shù)據(jù)。并且這個自定義控件具有同ProgressBar類似的屬性竿奏,比如max(最大值)、progress(當(dāng)前值)等等泛啸。
有了明確的想法之后,開始開發(fā):
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);//設(shè)置動畫時間
mScaleAnim.setInterpolator(new DecelerateInterpolator());//設(shè)置動畫插入器吕粹,減速
mScaleAnim.setRepeatCount(0);//設(shè)置動畫重復(fù)次數(shù),這里-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);//設(shè)置動畫時間
mColorAnim.setInterpolator(new DecelerateInterpolator());//設(shè)置動畫插入器匹耕,減速
mColorAnim.setRepeatCount(0);//設(shè)置動畫重復(fù)次數(shù),這里-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>
光線數(shù)據(jù)源 --> UI展示需要數(shù)據(jù)的轉(zhuǎn)化: 數(shù)據(jù)梯度設(shè)置
在我們獲取到傳感器給出的光照強度數(shù)據(jù)源后煤傍,我們無法直接使用,因為光線給出的數(shù)據(jù)與UI展示需要的數(shù)據(jù)不同嘱蛋,因此我們需要下轉(zhuǎn)化,直接在傳感器監(jiān)聽中添加代碼即可:
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) {
}
};
其他動畫效果的實現(xiàn):光線強度增加的過度動畫
光線傳感器數(shù)據(jù)獲取完成了蒋失,用于UI展示的自定義控件也寫好了,數(shù)據(jù)也匹配好了可以正常展示了桐玻,但我們可能還不會滿足,我們可能還希望給這個自定義控件在傳感器數(shù)據(jù)發(fā)生變化的時候镊靴,有一個動畫出來。這個動畫其實在上面粘貼的代碼中已經(jīng)有了偏竟,這里只是介紹一下給這個自定義view添加動畫的思路:
我們知道自定義view的所有的視圖都是在onDraw(Canvas canvas)方法中用Canvas畫出來的,而化成什么樣子又是其中的各種參數(shù)控制的踊谋,例如我們在自定義控件中畫一個圓,我們只需要動態(tài)的改變這個圓的半徑殖蚕,就能達到這個圓的放大縮小的目的,當(dāng)我們有規(guī)律的并且足夠頻繁改變這個值睦疫,就可以達到平滑的動態(tài)效果了害驹。例如實現(xiàn)控件中控件隨progress的變化顏色漸變:
mColorAnim = ObjectAnimator
.ofArgb(this, "mColor", mColor, Color.rgb(250, 128, 10), mColor);
mColorAnim.setDuration(1200);//設(shè)置動畫時間
mColorAnim.setInterpolator(new DecelerateInterpolator());//設(shè)置動畫插入器宛官,減速
mColorAnim.setRepeatCount(0);//設(shè)置動畫重復(fù)次數(shù)葫松,這里-1代表無限
mColorAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int value = (int) animation.getAnimatedValue();
mColor = value;
postInvalidate();//重新繪制控件
}
});
這樣底洗,我們的自定義感光器控件的“感光”、“數(shù)據(jù)展示”和一切其他的動畫效果亥揖,就達成了,干杯P炜椤!