├── images ├── 0.jpg └── 1.jpg ├── ic_launcher-web.png ├── libs └── android-support-v4.jar ├── res ├── drawable-xhdpi │ ├── btn_clock.png │ ├── ic_launcher.png │ ├── clock_hand_hour.png │ ├── clock_hand_minute.png │ └── clock_hand_second.png ├── drawable-hdpi │ └── ic_launcher.png ├── drawable-mdpi │ └── ic_launcher.png ├── drawable-xxhdpi │ └── ic_launcher.png ├── values-sw600dp │ └── dimens.xml ├── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── menu │ └── main.xml ├── values-sw720dp-land │ └── dimens.xml ├── values-v11 │ └── styles.xml ├── values-v14 │ └── styles.xml └── layout │ └── activity_main.xml ├── .settings └── org.eclipse.jdt.core.prefs ├── README.md ├── src └── com │ └── example │ ├── android_clock │ └── MainActivity.java │ └── widget │ └── AnalogClock.java ├── .classpath ├── project.properties ├── proguard-project.txt ├── .project └── AndroidManifest.xml /images/0.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/images/0.jpg -------------------------------------------------------------------------------- /images/1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/images/1.jpg -------------------------------------------------------------------------------- /ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/ic_launcher-web.png -------------------------------------------------------------------------------- /libs/android-support-v4.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/libs/android-support-v4.jar -------------------------------------------------------------------------------- /res/drawable-xhdpi/btn_clock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/res/drawable-xhdpi/btn_clock.png -------------------------------------------------------------------------------- /res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/clock_hand_hour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/res/drawable-xhdpi/clock_hand_hour.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/clock_hand_minute.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/res/drawable-xhdpi/clock_hand_minute.png -------------------------------------------------------------------------------- /res/drawable-xhdpi/clock_hand_second.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuningjack/CustomClock/HEAD/res/drawable-xhdpi/clock_hand_second.png -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 3 | org.eclipse.jdt.core.compiler.compliance=1.6 4 | org.eclipse.jdt.core.compiler.source=1.6 5 | -------------------------------------------------------------------------------- /res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 8 | -------------------------------------------------------------------------------- /res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16dp 5 | 16dp 6 | 7 | -------------------------------------------------------------------------------- /res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Android_clock 5 | Settings 6 | Hello world! 7 | 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CustomClock 2 | The clock is painted by canvas, with a hour,minute and second hand (手画的时钟,带有时、分、秒针) 3 | 效果图: 4 | ![Alt text](https://github.com/xuningjack/CustomClock/raw/master/images/0.jpg) 5 | 6 | ![Alt text](https://github.com/xuningjack/CustomClock/raw/master/images/1.jpg) 7 | -------------------------------------------------------------------------------- /res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 128dp 8 | 9 | -------------------------------------------------------------------------------- /res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /src/com/example/android_clock/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.android_clock; 2 | 3 | import android.os.Bundle; 4 | import android.app.Activity; 5 | import android.view.Menu; 6 | 7 | public class MainActivity extends Activity { 8 | 9 | @Override 10 | protected void onCreate(Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_main); 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-19 15 | -------------------------------------------------------------------------------- /res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | -------------------------------------------------------------------------------- /proguard-project.txt: -------------------------------------------------------------------------------- 1 | # To enable ProGuard in your project, edit project.properties 2 | # to define the proguard.config property as described in that file. 3 | # 4 | # Add project specific ProGuard rules here. 5 | # By default, the flags in this file are appended to flags specified 6 | # in ${sdk.dir}/tools/proguard/proguard-android.txt 7 | # You can edit the include path and order by changing the ProGuard 8 | # include property in project.properties. 9 | # 10 | # For more details, see 11 | # http://developer.android.com/guide/developing/tools/proguard.html 12 | 13 | # Add any project specific keep options here: 14 | 15 | # If your project uses WebView with JS, uncomment the following 16 | # and specify the fully qualified class name to the JavaScript interface 17 | # class: 18 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 19 | # public *; 20 | #} 21 | -------------------------------------------------------------------------------- /res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 18 | 19 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | Android_clock 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /src/com/example/widget/AnalogClock.java: -------------------------------------------------------------------------------- 1 | package com.example.widget; 2 | 3 | import java.util.TimeZone; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.content.res.Resources; 9 | import android.graphics.Canvas; 10 | import android.graphics.drawable.Drawable; 11 | import android.os.Handler; 12 | import android.text.format.DateUtils; 13 | import android.text.format.Time; 14 | import android.util.AttributeSet; 15 | import android.view.View; 16 | import android.widget.RemoteViews.RemoteView; 17 | import com.example.android_clock.R; 18 | 19 | /** 20 | * 有时针、分针、秒针的显示时钟 21 | */ 22 | @RemoteView 23 | public class AnalogClock extends View { 24 | 25 | private Context mContext; 26 | private Time mCalendar; 27 | private Drawable mHourHand; 28 | private Drawable mMinuteHand; 29 | private Drawable mSecondHand; 30 | /** 时钟面板 */ 31 | private Drawable mDial; 32 | private int mDialWidth; 33 | private int mDialHeight; 34 | private boolean mAttached; 35 | private final Handler mHandler = new Handler(); 36 | private float mMinutes; 37 | private float mHour; 38 | private float mSecond; 39 | private boolean mChanged; 40 | /** 定时刷新 时钟界面的Handler */ 41 | private Handler tickHandler; 42 | 43 | public AnalogClock(Context context) { 44 | this(context, null); 45 | } 46 | 47 | public AnalogClock(Context context, AttributeSet attrs) { 48 | this(context, attrs, 0); 49 | } 50 | 51 | public AnalogClock(Context context, AttributeSet attrs, int defStyle) { 52 | super(context, attrs, defStyle); 53 | mContext = context; 54 | mCalendar = new Time(); 55 | Resources resource = mContext.getResources(); 56 | mDial = resource.getDrawable(R.drawable.btn_clock); 57 | mHourHand = resource.getDrawable(R.drawable.clock_hand_hour); 58 | mMinuteHand = resource.getDrawable(R.drawable.clock_hand_minute); 59 | mSecondHand = resource.getDrawable(R.drawable.clock_hand_second); 60 | mDialWidth = mDial.getIntrinsicWidth(); 61 | mDialHeight = mDial.getIntrinsicHeight(); 62 | prepareRefresh(); 63 | } 64 | 65 | /** 66 | * 准备刷新时钟面板 67 | */ 68 | public void prepareRefresh() { 69 | tickHandler = new Handler(); 70 | tickHandler.post(tickRunnable); 71 | } 72 | 73 | /** 74 | * 更新时钟的线程 75 | */ 76 | private Runnable tickRunnable = new Runnable() { 77 | public void run() { 78 | onTimeChanged(); 79 | postInvalidate(); 80 | tickHandler.postDelayed(tickRunnable, 1000); // 1s中更改1次时间 81 | } 82 | }; 83 | 84 | @Override 85 | protected void onAttachedToWindow() { 86 | super.onAttachedToWindow(); 87 | if (!mAttached) { 88 | mAttached = true; 89 | IntentFilter filter = new IntentFilter(); 90 | filter.addAction(Intent.ACTION_TIME_TICK); 91 | filter.addAction(Intent.ACTION_TIMEZONE_CHANGED); 92 | getContext().registerReceiver(mIntentReceiver, filter, null, 93 | mHandler); 94 | } 95 | mCalendar = new Time(); 96 | onTimeChanged(); 97 | } 98 | 99 | @Override 100 | protected void onDetachedFromWindow() { 101 | super.onDetachedFromWindow(); 102 | if (mAttached) { 103 | getContext().unregisterReceiver(mIntentReceiver); 104 | mAttached = false; 105 | } 106 | } 107 | 108 | @Override 109 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 110 | // 设置控件大小为表盘的大小 111 | setMeasuredDimension(mDialWidth, mDialHeight); 112 | } 113 | 114 | @Override 115 | protected void onSizeChanged(int w, int h, int oldw, int oldh) { 116 | super.onSizeChanged(w, h, oldw, oldh); 117 | mChanged = true; 118 | } 119 | 120 | @Override 121 | protected void onDraw(Canvas canvas) { 122 | super.onDraw(canvas); 123 | boolean changed = mChanged; 124 | if (changed) { 125 | 126 | mChanged = false; 127 | } 128 | 129 | int availableWidth = getRight() - getLeft(); 130 | System.out.println("availableWidth----" + availableWidth); // 76 131 | int availableHeight = getBottom() - getTop(); 132 | System.out.println("availableWidth----" + availableHeight); // 86 133 | 134 | int x = availableWidth / 2; 135 | int y = availableHeight / 2; 136 | System.out.println("x----" + x); // 38 137 | System.out.println("y----" + y); // 43 138 | 139 | final Drawable dial = mDial; 140 | int w = dial.getIntrinsicWidth(); 141 | System.out.println("w----" + w); // 76 142 | int h = dial.getIntrinsicHeight(); 143 | System.out.println("h----" + h); // 86 144 | 145 | boolean scaled = false; 146 | 147 | // 开始画面板 148 | if (availableWidth < w || availableHeight < h) { 149 | 150 | scaled = true; 151 | float scale = Math.min((float) availableWidth / (float) w, 152 | (float) availableHeight / (float) h); 153 | canvas.save(); 154 | canvas.scale(scale, scale, x, y); 155 | } 156 | if (changed) { 157 | 158 | dial.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y + (h / 2)); 159 | } 160 | dial.draw(canvas); 161 | 162 | // 开始画时针 163 | canvas.save(); 164 | canvas.rotate(mHour / 12.0f * 360.0f, x, y); 165 | final Drawable hourHand = mHourHand; 166 | if (changed) { 167 | 168 | w = hourHand.getIntrinsicWidth(); 169 | h = hourHand.getIntrinsicHeight(); 170 | hourHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y 171 | + (h / 2)); 172 | } 173 | hourHand.draw(canvas); 174 | 175 | // 开始画分针 176 | canvas.restore(); 177 | canvas.save(); 178 | canvas.rotate(mMinutes / 60.0f * 360.0f, x, y); 179 | final Drawable minuteHand = mMinuteHand; 180 | if (changed) { 181 | 182 | w = minuteHand.getIntrinsicWidth(); 183 | h = minuteHand.getIntrinsicHeight(); 184 | minuteHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y 185 | + (h / 2)); 186 | } 187 | minuteHand.draw(canvas); 188 | 189 | // 开始画秒针 190 | canvas.restore(); 191 | canvas.save(); 192 | canvas.rotate(mSecond / 60.0f * 360.0f, x, y); 193 | final Drawable secondHand = mSecondHand; 194 | if (changed) { 195 | 196 | w = secondHand.getIntrinsicWidth(); 197 | h = secondHand.getIntrinsicHeight(); 198 | secondHand.setBounds(x - (w / 2), y - (h / 2), x + (w / 2), y 199 | + (h / 2)); 200 | } 201 | secondHand.draw(canvas); 202 | canvas.restore(); 203 | if (scaled) { 204 | canvas.restore(); 205 | } 206 | } 207 | 208 | /** 209 | * 更新时间到当前时间 210 | */ 211 | private void onTimeChanged() { 212 | mCalendar.setToNow(); 213 | int hour = mCalendar.hour; 214 | int minute = mCalendar.minute; 215 | int second = mCalendar.second; 216 | 217 | mMinutes = minute + second / 60.0f; 218 | mHour = hour + mMinutes / 60.0f; 219 | mSecond = second; 220 | mChanged = true; 221 | updateContentDescription(mCalendar); 222 | } 223 | 224 | @SuppressWarnings("deprecation") 225 | private void updateContentDescription(Time time) { 226 | final int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_24HOUR; 227 | String contentDescription = DateUtils.formatDateTime(mContext, 228 | time.toMillis(false), flags); 229 | setContentDescription(contentDescription); 230 | } 231 | 232 | private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() { 233 | @Override 234 | public void onReceive(Context context, Intent intent) { 235 | if (intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED)) { 236 | String tz = intent.getStringExtra("time-zone"); 237 | mCalendar = new Time(TimeZone.getTimeZone(tz).getID()); 238 | } 239 | onTimeChanged(); 240 | invalidate(); // 使UI无效 241 | } 242 | }; 243 | } --------------------------------------------------------------------------------