您的位置:首页 > 移动开发 > Android开发

自定义模拟+数字时钟,整合AnalogDigitalView

2015-12-06 22:35 537 查看
一、主要原理可以采用两种方案:

(1)内置Handler每隔1s发送空消息,更新Calendar拿到当前最新时间,分解出日期、时、分秒,刷新对应视图。视图采用canvas绘制原理绘制,实时性较强,实现较轻量。

(2)使用系统时钟广播,每隔1min收到消息,更新Calendar拿到当前最新时间,刷新日、时、分、秒视图。实时性不强,需要捕获系统消息,并做过滤处理,部分机型有问题。

二、实现模拟+数字时钟效果截图



三、自定义AnalogDigtalClock 继承自View

/**
* @name 自定义模拟+数字时钟
* @author Fanjb
* @date 2015年11月21日
*/
public class AnalogDigtalClock extends View {

private Drawable mDial;
private Drawable mHourHand;
private Drawable mMinuteHand;
private Drawable mSecondHand;

private Paint mBoundPaint;
private Paint mDatePaint;

private boolean mChanged;
private Calendar mCalendar;

private Runnable mTicker;
private Handler mHandler;

public AnalogDigtalClock(Context context) {
this(context, null);
}

public AnalogDigtalClock(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}

public AnalogDigtalClock(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.AnalogDigtalClock,
defStyleAttr, 0);

// dial
mDial = a.getDrawable(R.styleable.AnalogDigtalClock_dialDrawable);
if (mDial == null) {
mDial = context.getDrawable(R.drawable.analog_dial);
}

// hour hand
mHourHand = a.getDrawable(R.styleable.AnalogDigtalClock_hourHandDrawable);
if (mHourHand == null) {
mHourHand = context.getDrawable(R.drawable.analog_hour_hand);
}

// minute hand
mMinuteHand = a.getDrawable(R.styleable.AnalogDigtalClock_minuteHandDrawable);
if (mMinuteHand == null) {
mMinuteHand = context.getDrawable(R.drawable.analog_minute_hand);
}

// second hand
mSecondHand = a.getDrawable(R.styleable.AnalogDigtalClock_secondHandDrawable);
if (mSecondHand == null) {
mSecondHand = context.getDrawable(R.drawable.analog_second_hand);
}

a.recycle();

mBoundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBoundPaint.setColor(Color.BLUE);
mBoundPaint.setStyle(Style.STROKE);
mBoundPaint.setStrokeWidth(1);

mDatePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDatePaint.setColor(Color.BLACK);
mDatePaint.setTextSize(15);
}

@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
// IntentFilter filter = new IntentFilter();
// filter.addAction(Intent.ACTION_TIME_TICK);
// filter.addAction(Intent.ACTION_TIME_CHANGED);
// filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
// filter.addAction(Intent.ACTION_DATE_CHANGED);
// getContext().registerReceiver(mTimeChangeReceiver, filter);

mHandler = new Handler();
mTicker = new Runnable() {
@Override
public void run() {
mCalendar = Calendar.getInstance();
mChanged = true;
invalidate();
long now = SystemClock.uptimeMillis();
long next = now + (1000 - now % 1000);
mHandler.postAtTime(mTicker, next);
}
};
mTicker.run();
}

@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
// getContext().unregisterReceiver(mTimeChangeReceiver);
}

/**
* time has changed request invalidate views
*/
// private BroadcastReceiver mTimeChangeReceiver = new BroadcastReceiver() {
// public void onReceive(Context context, Intent intent) {
// String action = intent.getAction();
// if (action.equals(Intent.ACTION_TIME_TICK) ||
// action.equals(Intent.ACTION_TIME_CHANGED)
// || action.equals(Intent.ACTION_TIMEZONE_CHANGED)
// || action.equals(Intent.ACTION_DATE_CHANGED)) {
// // mTicker.run();
// }
// }
// };

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (isInEditMode()) {
return;
}

int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);

float hScale = 1.0f;
float vScale = 1.0f;
int dialWidth = mDial.getIntrinsicWidth();
int dialHeight = mDial.getIntrinsicHeight();

if (widthMode != MeasureSpec.UNSPECIFIED && width < dialWidth) {
hScale = (float) width / (float) dialWidth;
}
if (heightMode != MeasureSpec.UNSPECIFIED && height < dialHeight) {
vScale = (float) height / (float) dialHeight;
}
float scale = Math.min(hScale, vScale);

// size: 0 - scale * dialSize ( scale : 0 - 1)
setMeasuredDimension(resolveSizeAndState((int) (scale * dialWidth), widthMeasureSpec, 0),
resolveSizeAndState((int) (scale * dialWidth), widthMeasureSpec, 0));
}

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

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0, 0, getWidth(), getHeight(), mBoundPaint);

boolean changed = mChanged;
if (changed) {
mChanged = false;
}

int x = getWidth() / 2;
int y = getHeight() / 2;

// scale canvas
boolean scaled = false;
if (getWidth() < mDial.getIntrinsicWidth() || getHeight() < mDial.getIntrinsicHeight()) {
float scale = Math.min((float) getWidth() / (float) mDial.getIntrinsicWidth(),
(float) getHeight() / (float) mDial.getIntrinsicHeight());
canvas.save();
canvas.scale(scale, scale, x, y);
scaled = true;
}

// dial
if (changed) {
mDial.setBounds(x - mDial.getIntrinsicWidth() / 2, y - mDial.getIntrinsicHeight() / 2,
x + mDial.getIntrinsicWidth() / 2, y + mDial.getIntrinsicHeight() / 2);
}
mDial.draw(canvas);

// datetime
String date = mCalendar.get(Calendar.YEAR) + "-" + (mCalendar.get(Calendar.MONTH) + 1) + "-"
+ mCalendar.get(Calendar.DAY_OF_MONTH) + " " + mCalendar.get(Calendar.HOUR_OF_DAY)
+ ":" + mCalendar.get(Calendar.MINUTE) + ":" + mCalendar.get(Calendar.SECOND);
int dateX = (int) (x - mDatePaint.measureText(date) / 2);
int dateY = y - mDial.getIntrinsicHeight() / 2 - 20;
canvas.drawText(date, dateX, dateY, mDatePaint);

// hour hand
canvas.save();
float hour = mCalendar.get(Calendar.HOUR) + mCalendar.get(Calendar.MINUTE) / 60.0f;
canvas.rotate(hour / 12.0f * 360, x, y);
if (changed) {
mHourHand.setBounds(x - mHourHand.getIntrinsicWidth() / 2,
y - mHourHand.getIntrinsicHeight(), x + mHourHand.getIntrinsicWidth() / 2, y);
}
mHourHand.draw(canvas);
canvas.restore();

// minute hand
canvas.save();
canvas.rotate(mCalendar.get(Calendar.MINUTE) / 60.0f * 360, x, y);
if (changed) {
mMinuteHand.setBounds(x - mMinuteHand.getIntrinsicWidth() / 2,
y - mMinuteHand.getIntrinsicHeight(), x + mMinuteHand.getIntrinsicWidth() / 2,
y);
}
mMinuteHand.draw(canvas);
canvas.restore();

// second hand
canvas.save();
canvas.rotate(mCalendar.get(Calendar.SECOND) / 60.0f * 360, x, y);
if (changed) {
mSecondHand.setBounds(x - mSecondHand.getIntrinsicWidth() / 2,
y - mSecondHand.getIntrinsicHeight(), x + mSecondHand.getIntrinsicWidth() / 2,
y);
}
mSecondHand.draw(canvas);
canvas.restore();

if (scaled) {
canvas.restore();
}
}

}

四、xml 布局中加入自定义属性
(1) 样式表约束

<!-- 模拟时钟 -->
<declare-styleable name="AnalogDigtalClock">
<attr name="dialDrawable" format="reference" />
<attr name="hourHandDrawable" format="reference"/>
<attr name="minuteHandDrawable" format="reference"/>
<attr name="secondHandDrawable" format="reference" />
</declare-styleable>

(2)控件声明

<com.freedoman.widgets.calendar.AnalogDigtalClock
android:id="@+id/analog_digtal_clock"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="5dp"
app:dialDrawable="@drawable/analog_dial"
app:hourHandDrawable="@drawable/analog_hour_hand"
app:minuteHandDrawable="@drawable/analog_minute_hand"
app:secondHandDrawable="@drawable/analog_second_hand" />

五、在Activity中做一个简单的测试
public class AnalogDigtalClockActivity extends Activity {

private AnalogDigtalClock mAnalogDigtalClock;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_clock);

// 自定义模拟数字时钟
if (mAnalogDigtalClock == null) {
mAnalogDigtalClock = (AnalogDigtalClock) findViewById(R.id.analog_digtal_clock);
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息