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"); } } };