Clock analyze
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = this;
mActionBar = getActionBar();
mActionBar.setAlternativeTabStyle(true);
int tabHeight = (int) getResources().getDimensionPixelSize(R.dimen.uui_tab_height);
mActionBar.setTabHeight(tabHeight);
mActionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
setContentView(R.layout.clock_activity_main);
createFragmentAndTab(); --->1.1
}
------->1.1
private void createFragmentAndTab() {
mFragmentsList = new ArrayList<Fragment>();
mViewPager = (ViewPager) findViewById(R.id.tab_pager);
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment alarmFragment = new AlarmFragment();
Fragment clockFragment = new WorldClockFragment();
Fragment stopWatchFragment = new StopWatchFragment();
Fragment timerFragment = new TimerFragment(); //对 TimerFragment 分析
//省略下面的分析
mFragmentsList.add(alarmFragment);
mFragmentsList.add(clockFragment);
mFragmentsList.add(stopWatchFragment);
mFragmentsList.add(timerFragment);
addCustomTab(TabState.TAB_ALARM_INDEX, R.drawable.ic_tab_alarm_newui,
R.string.alarm_tab_label, new TabListener());
addCustomTab(TabState.TAB_CLOCK_INDEX, R.drawable.ic_tab_worldclock_newui,
R.string.worldclock_tab_label, new TabListener());
addCustomTab(TabState.TAB_STOPWATCH_INDEX, R.drawable.ic_tab_stopwatch_newui,
R.string.stopwatch_tab_label, new TabListener());
addCustomTab(TabState.TAB_TIMER_INDEX, R.drawable.ic_tab_timer_newui,
R.string.timer_tab_label, new TabListener());
mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
Intent i = getIntent();
if (i != null) {
int tab = i.getIntExtra(CURRENT_TAB, -1);
if (tab != -1) {
mCurrentTab = tab;
} else {
mCurrentTab = mPrefs.getInt(CURRENT_TAB, TabState.TAB_ALARM_INDEX);
}
} else {
mCurrentTab = mPrefs.getInt(CURRENT_TAB, TabState.TAB_ALARM_INDEX);
}
Log.d(TAG, "mCurrentTab = " + mCurrentTab);
mViewPager.setOffscreenPageLimit(mTabTitle.length);
mViewPager.setAdapter(new TabFragmentPagerAdapter(fragmentManager, mFragmentsList));
setCurrentTab(mCurrentTab);
mViewPager.setCurrentItem(mCurrentTab, true);
mViewPager.setOnPageChangeListener(mPagerListener);
}
TimerFragment.java
public class TimerFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View fragView = inflater.inflate(R.layout.fragment_timing, container, false); --->2.1
mStartBut = (Button) fragView.findViewById(R.id.start);
mStopBut = (Button) fragView.findViewById(R.id.stop);
mCancelBut = (Button) fragView.findViewById(R.id.cancel);
mRestartBut = (Button) fragView.findViewById(R.id.restart);
mTimePickerLayout = fragView.findViewById(R.id.fragment_timing_time_picker_layout);
mShow = (LinearLayout) fragView.findViewById(R.id.time_picker);
mShowTiming = (TextView) fragView.findViewById(R.id.show_timing);
mHours = (WheelView) fragView.findViewById(R.id.hour);
mHours.setViewAdapter(new NumericWheelAdapter(getActivity(), 0, 99)); -->important
mHours.setCyclic(true);
mMins = (WheelView) fragView.findViewById(R.id.mins);
mMins.setViewAdapter(new NumericWheelAdapter(getActivity(), 0, 59, "%02d"));
mMins.setCyclic(true);
mSecs = (WheelView) fragView.findViewById(R.id.secs);
mSecs.setViewAdapter(new NumericWheelAdapter(getActivity(), 0, 59, "%02d"));
mSecs.setCyclic(true);
mHours.setCurrentItem(0);
mMins.setCurrentItem(1);
mSecs.setCurrentItem(0);
/*if ((mHours.getCurrentItem() != 0) && (mMins.getCurrentItem() != 0) && (mSecs.getCurrentItem() != 0)) {
mStartBut.setEnabled(false);
} else {
mStartBut.setEnabled(true);
}
OnWheelChangedListener wheelListener = new OnWheelChangedListener() {
public void onChanged(WheelView wheel, int oldValue, int newValue) {
if (!mTimeScrolled) {
mSecnum = mHours.getCurrentItem() * 60 * 60 * 1000 + mMins.getCurrentItem() * 60 * 1000
+ mSecs.getCurrentItem() * 1000;
}
}
};
mHours.addChangingListener(wheelListener);
mMins.addChangingListener(wheelListener);
mSecs.addChangingListener(wheelListener);
OnWheelClickedListener click = new OnWheelClickedListener() {
public void onItemClicked(WheelView wheel, int itemIndex) {
wheel.setCurrentItem(itemIndex, true);
}
};
mHours.addClickingListener(click);
mMins.addClickingListener(click);
mSecs.addClickingListener(click);
OnWheelScrollListener scrollListener = new OnWheelScrollListener() {
public void onScrollingStarted(WheelView wheel) {
mTimeScrolled = true;
}
public void onScrollingFinished(WheelView wheel) {
if ((mHours.getCurrentItem() * 60 * 60 == 0) && (mMins.getCurrentItem() * 60 == 0)
&& (mSecs.getCurrentItem() == 0)) {
mStartBut.setEnabled(false);
} else {
mStartBut.setEnabled(true);
}
mTimeScrolled = true;
mSecnum = mHours.getCurrentItem() * 60 * 60 * 1000
+ mMins.getCurrentItem() * 60 * 1000
+ mSecs.getCurrentItem() * 1000;
}
};
mHours.addScrollingListener(scrollListener);
mMins.addScrollingListener(scrollListener);
mSecs.addScrollingListener(scrollListener);
*/ 注释部分省略
mStartBut.setOnClickListener(new OnClickListener() { //分析倒计时计时过程
@Override
public void onClick(View v) {
mIsRunning = true;
showPage(Utils.PAGE_COUNTDOWN_TIMER); ---> 3.6 倒计时进入的界面
mSecnum = mHours.getCurrentItem() * 60 * 60 * 1000 + mMins.getCurrentItem() * 60 * 1000
+ mSecs.getCurrentItem() * 1000;
startCountTime(mSecnum); --->3.1 计时
}
});
mStopBut.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (mSecnum >= 1 * 1000) {
mIsRunning = false;
mStopBut.setVisibility(View.GONE);
mRestartBut.setVisibility(View.VISIBLE);
if (mCountTimer != null) {
mCountTimer.cancel();
}
if (mPendingIntent != null) {
mAlarmManager.cancel(mPendingIntent);
}
}
}
});
mRestartBut.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
mIsRunning = true;
startCountTime(mSecnum);
mStopBut.setVisibility(View.VISIBLE);
mRestartBut.setVisibility(View.GONE);
}
});
mCancelBut.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View arg0) {
if (mSecnum >= 1 * 1000) {
if (mPendingIntent != null) {
mAlarmManager.cancel(mPendingIntent);
}
if (mCountTimer != null) {
mCountTimer.cancel();
}
mIsRunning = false;
mTimeScrolled = true;
showPage(Utils.PAGE_SETUP_TIMER);
}
}
});
return fragView;
}
}
--->2.1
fragment_timing.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:paddingBottom="15dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="15dp" >
<LinearLayout
android:id="@+id/time_picker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/fragment_timing_time_picker_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.sprd.wheel.widget.WheelView
android:id="@+id/hour"
android:layout_width="0dp"
android:layout_height="212dp"
android:layout_weight="1" />
<com.sprd.wheel.widget.WheelView
android:id="@+id/mins"
android:layout_width="0dp"
android:layout_height="212dp"
android:layout_weight="1" />
<com.sprd.wheel.widget.WheelView
android:id="@+id/secs"
android:layout_width="0dp"
android:layout_height="212dp"
android:layout_weight="1" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/show_timing"
android:layout_width="match_parent"
android:layout_height="214dp"
android:background="@drawable/timepicker"
android:gravity="center"
android:textColor="@android:color/white"
android:textSize="60sp"
android:visibility="gone" />
</FrameLayout>
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:background="#e4dbbd" >
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:src="@drawable/divider" />
<Button
android:id="@+id/start"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_marginTop="48dip"
android:background="@drawable/start_stopwatch"
android:enabled="false"
android:text="@string/start_timing"
android:textColor="#ffffffff" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
<Button
android:id="@+id/stop"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="11dip"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_marginTop="35dip"
android:layout_weight="1"
android:background="@drawable/stop_stopwatch"
android:text="@string/stop_timing"
android:textColor="#ffffffff"
android:visibility="invisible" />
<Button
android:id="@+id/restart"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="11dip"
android:layout_marginLeft="10dip"
android:layout_marginRight="10dip"
android:layout_marginTop="35dip"
android:layout_weight="1"
android:background="@drawable/start_stopwatch"
android:text="@string/restart_timing"
android:textColor="#ffffffff"
android:visibility="invisible" />
<Button
android:id="@+id/cancel"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="12dip"
android:layout_marginLeft="11dip"
android:layout_marginRight="10dip"
android:layout_marginTop="35dip"
android:layout_weight="1"
android:background="@drawable/reset_stopwatch"
android:text="@string/cancel_timing"
android:textColor="#ffffffff"
android:visibility="invisible" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
WheelView.java
public class WheelView extends View {
/**
* Sets view adapter. Usually new adapters contain different views, so
* it needs to rebuild view by calling measure().
*
* @param viewAdapter the view adapter
*/
public void setViewAdapter(WheelViewAdapter viewAdapter) {
if (this.viewAdapter != null) {
this.viewAdapter.unregisterDataSetObserver(dataObserver);
}
this.viewAdapter = viewAdapter;
if (this.viewAdapter != null) {
this.viewAdapter.registerDataSetObserver(dataObserver);
}
invalidateWheel(true);
}
}
--->3.1
TimerFragment.java
private void startCountTime(long millsecs) {
mIsRunning = true;
Intent intent = new Intent(mContext, TimerAlertActivity.class); -->4.1 从这里设置闹钟提醒
intent.putExtra("stop_time", millSecsToString(millsecs)); //传入闹钟时间
mPendingIntent = PendingIntent.getActivity(mContext, 0,
intent, Intent.FLAG_ACTIVITY_NEW_TASK);
mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
Utils.getTimeNow() + millsecs, mPendingIntent); //管理定时
//以上设置闹钟
mCountTimer = new CountTimer(millsecs, 100, mContext); ---->3.2
mCountTimer.start(); --->3.3
}
---->3.2
public CountTimer(long millisInFuture, long countDownInterval, Context context) {
super(millisInFuture, countDownInterval); --->/*
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
*/
mContext = context;
mShowTiming.setText(millSecsToString(millisInFuture));
}
--->3.3
frameworks/../CountDownTimer.java
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG)); //msg.what = 1 -->3.4
return this;
}
-->3.4
CountDownTimer.java
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish(); //完成倒计时退出 -->3.5
} else if (millisLeft < mCountdownInterval) {
// no tick, just delay until done
sendMessageDelayed(obtainMessage(MSG), millisLeft); //在此进行倒计时
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft); -->3.9
// take into account user's onTick taking time to execute
long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
-->3.5
TimerFragment.java
class CountTimer extends CountDownTimer {
public CountTimer(long millisInFuture, long countDownInterval, Context context) {
super(millisInFuture, countDownInterval);
mContext = context;
mShowTiming.setText(millSecsToString(millisInFuture));
}
@Override
public void onFinish() {
mIsRunning = false;
showPage(Utils.PAGE_SETUP_TIMER);
}
--->3.9
@Override
public void onTick(long millisUntilFinished) {
mSecnum = millisUntilFinished;
mShowTiming.setText(millSecsToString(millisUntilFinished));
}
}
---> 3.6
TimerFragment.java
private void showPage(int pageIndex) {
if (pageIndex == Utils.PAGE_COUNTDOWN_TIMER) {
mPageIndex = Utils.PAGE_COUNTDOWN_TIMER;
showCountDownTimeView(); --> 3.7
} else {
mPageIndex = Utils.PAGE_SETUP_TIMER;
showTimeSetupPage(); -->3.8
}
}
--> 3.7
//对fragment_timing.xml的显示界面做出反应
private void showCountDownTimeView() {
mStartBut.setVisibility(View.GONE);
mShow.setVisibility(View.GONE);
mTimePickerLayout.setVisibility(View.GONE);
mShowTiming.setVisibility(View.VISIBLE);
mCancelBut.setVisibility(View.VISIBLE);
if (mIsRunning) {
mStopBut.setVisibility(View.VISIBLE);
mRestartBut.setVisibility(View.GONE);
} else {
mStopBut.setVisibility(View.GONE);
mRestartBut.setVisibility(View.VISIBLE);
}
}
当3.5完成时,也调用了showPage(Utils.PAGE_SETUP_TIMER);
此时 -->3.8
private void showTimeSetupPage() {
mCancelBut.setVisibility(View.GONE);
mStopBut.setVisibility(View.GONE);
mShowTiming.setVisibility(View.GONE);
mRestartBut.setVisibility(View.GONE);
mStartBut.setVisibility(View.VISIBLE);
mShow.setVisibility(View.VISIBLE);
mTimePickerLayout.setVisibility(View.VISIBLE);
}
---->4.1
TimerAlertActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.timer_alert); --->4.2
mContext = this;
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
NotificationManager mNotificationManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.cancel(TimerFragment.TIMER_NOTIFI_ID);
mHandler.post(mMediaRunnable); -->设置Audio
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (!getIntent().getBooleanExtra(SCREEN_OFF, false)) {
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON);
}
mTimerImg = (ImageView) findViewById(R.id.timer_anim);
mTimerAnimation = (AnimationDrawable) mTimerImg.getBackground(); //设置动态播放动画
String strStopTime = getIntent().getStringExtra("stop_time");
TextView stop_time = (TextView) findViewById(R.id.timer_stop_time);
if (strStopTime != null) {
stop_time.setText(strStopTime); //显示的是设置的最初时间
} else {
stop_time.setVisibility(View.GONE);
}
mCloseButton = findViewById(R.id.close_button);
mCloseButton.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
if (v == mCloseButton) {
stopAnimation(mTimerAnimation);
finish(); //关闭闹钟 -->6.1 调用 onDestroy
}
}
});
}
protected void onResume() { -->5.1
super.onResume();
mHandler.postDelayed(mRunnable, START_DELAY);
}
private Runnable mRunnable = new Runnable() { -->5.2
public void run() {
startAnimation(mTimerAnimation);
}
};
protected void startAnimation(final AnimationDrawable animation) {
if (animation != null && !animation.isRunning()) {
animation.run(); -->5.3
}
}
public void run() {
nextFrame(false); -->5.4 //持续播放闹钟
}
-->6.1
protected void onDestroy() {
super.onDestroy();
if (mAlarm != null) {
mAlarm.stop();
mAlarm.release();
mAlarm = null;
}
if (mAudioManager != null) {
mAudioManager.abandonAudioFocus(mAudioFocusListener);
}
}
---->4.2
timer_alert.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black" >
<ImageView
android:id="@+id/timer_anim"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="37dp"
android:layout_centerHorizontal="true"
android:background="@anim/timer_time_up" />
<TextView
android:id="@+id/timer_stop_time"
android:layout_width="wrap_content"
android:layout_marginTop="9dp"
android:layout_height="50dp"
android:layout_below="@+id/timer_anim"
android:layout_centerHorizontal="true"
android:textColor="@android:color/white"
android:textSize="41sp"
android:textStyle="bold" />
<TextView
android:id="@+id/timer_time_up"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/timer_stop_time"
android:layout_marginTop="30dp"
android:layout_centerHorizontal="true"
android:text="@string/timer_timeup"
android:textColor="#ffffffff"
android:textSize="25sp"
android:textStyle="bold" />
<Button
android:id="@+id/close_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="7dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="@string/timer_close"
android:textColor="#ffffffff"
android:textSize="20sp"
android:background="@drawable/stop_stopwatch"
android:textAppearance="?android:attr/textAppearance" />
</RelativeLayout>
private Runnable mMediaRunnable = new Runnable() {
public void run() {
mAlarm = new MediaPlayer();
AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(R.raw.alarm);
try {
if (afd != null) {
mAlarm.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
afd.getLength());
afd.close();
}
} catch (Exception ex2) {
// At this point we just don't play anything.
Log.e(TAG, "Failed to play fallback alarm");
}
if (mAudioManager != null) {
mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_ALARM,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
}
try {
if (mAudioManager.getStreamVolume(AudioManager.STREAM_ALARM) != 0) {
mAlarm.setAudioStreamType(AudioManager.STREAM_ALARM);
mAlarm.setLooping(true);
mAlarm.prepare();
mAlarm.start();
}
} catch (Exception ex3) {
Log.e(TAG, "Failed to prepare alarm");
}
}
};