隨着網購的持續發展,搶購類倒計時在各種電商應用中已十分常見,這種設計能夠提升用戶的點擊率和下單率等。java
可是國內的電商應用大部分都僅支持中文,不適配其餘的語言,所以當倒計時與其餘文案處於同一行展現時,無需考慮倒計時的展現方式。在海外應用中,因爲須要適配各類語言,有些小語種的文案較長,所以當倒計時和其餘文案處於同一行展現時,須要充分考慮多語言的適配,如何優雅地完成倒計時自適應顯示是一個值得深思的問題。android
爲進一步優化倒計時效果,咱們爲倒計時增長了數字滾動動畫,以下圖所示。倒計時的功能必然會帶來性能的消耗,如何避免倒計時帶來的性能問題,本文也將給出相應的解決方案。web
該控件預期展示兩種狀態,距離活動開始還有X天XX:XX:XX 和距離活動結束還有X天XX:XX:XX,所以須要一個活動狀態屬性,並經過這個活動開始與否的屬性設置時間前的文案。具體時間時分秒之間相互獨立,所以將它們拆分紅獨立的textview進行處理。canvas
倒計時控件的核心是計時器,安卓中已經有現成的CountDownTimer類可供使用以實現倒計時功能。此外,還須要實現一些監聽的接口。數組
首先,定義回調接口app
public interface OnCountDownTimerListener { /** * 倒計時正在進行時調用的方法 * * @param millisUntilFinished 剩餘的時間(毫秒) */ void onRemain(long millisUntilFinished); /** * 倒計時結束 */ void onFinish(); /** * 每過一分鐘調用的方法 */ void onArrivalOneMinute(); }
在該接口中定義三個方法:ide
onRemain(long millisUntilFinished):倒計時進行中回調的方法,用於後續功能的拓展函數
onFinish():倒計時結束回調,用於活動狀態的切換和計時的暫停等佈局
onArrivalOneMinute():每過一分鐘回調,用於定時上報的埋點性能
其次,初始化自定義view,基於實際開發需求,將整個控件細分爲修飾文案、天數、時、分、秒等幾個獨立的textview,並在自定義BaseCountDownTimerView中初始化:
private void init() { mDayTextView = findViewById(R.id.days_tv); mHourTextView = findViewById(R.id.hours_tv); mMinTextView = findViewById(R.id.min_tv); mSecondTextView = findViewById(R.id.sec_tv); mHeaderText = findViewById(R.id.header_tv); mDayText = findViewById(R.id.new_arrival_day); }
首先構造設置剩餘時間的方法,入參是剩餘的毫秒數,在方法內部將時間轉化爲具體的天時分秒,並將結果賦予給textview
private void setSecond(long millis) { long day = millis / ONE_DAY; long hour = millis / ONE_HOUR - day * 24; long min = millis / ONE_MIN - day * 24 * 60 - hour * 60; long sec = millis / ONE_SEC - day * 24 * 60 * 60 - hour * 60 * 60 - min * 60; String second = (int) sec + ""; // 秒 String minute = (int) min + ""; // 分 String hours = (int) hour + ""; // 時 String days = (int) day + ""; //天 if (hours.length() == 1) { hours = "0" + hours; } if (minute.length() == 1) { minute = "0" + minute; } if (second.length() == 1) { second = "0" + second; } if (day == 0) { mDayTextView.setVisibility(GONE); mDayText.setVisibility(GONE); } else { setDayText(day); mDayTextView.setVisibility(VISIBLE); mDayText.setVisibility(VISIBLE); } mDayTextView.setText(days); if (mFirstSetTimer) { mHourTextView.setInitialNumber(hours); mMinTextView.setInitialNumber(minute); mSecondTextView.setInitialNumber(second); mFirstSetTimer = false; } else { mHourTextView.flipNumber(hours); mMinTextView.flipNumber(minute); mSecondTextView.flipNumber(second); } }
須要注意的是,當單位時間爲個位數時,爲了視覺效果的統一,要在數字前加「0」進行補位。
其次,構建一個建立倒計時的方法,其代碼以下:
private void createCountDownTimer(final int eventStatus) { if (mCountDownTimer != null) { mCountDownTimer.cancel(); } mCountDownTimer = new CountDownTimer(mMillis, 1000) { @Override public void onTick(long millisUntilFinished) { //策劃要求:倒計時爲00:00:01時,活動狀態刷新,倒計時不展現00:00:00這個狀態 if (millisUntilFinished >= ONE_SEC) { setSecond(millisUntilFinished); //當活動狀態爲進行中時,每隔一分鐘調用一次回調 if (eventStatus == HomeItemViewNewArrival.EVENT_START) { mArrivalOneMinuteFlag--; if (mArrivalOneMinuteFlag == Constant.ZERO) { mArrivalOneMinuteFlag = Constant.SIXTY; mOnCountDownTimerListener.onArrivalOneMinute(); } } } } @Override public void onFinish() { mOnCountDownTimerListener.onFinish(); } }; }
在該方法中,建立一個倒計時實例CountDownTimer,CountDownTimer() 有兩個參數,分別是剩餘的總時間和刷新間隔。
在實例的onTick()方法中,調用setSecond()方法在每次間隔時間(也就是1s)後按期刷新view,完成倒計時控件的更新。此外,產品中還有一個一分鐘按期上報埋點的需求,也能夠在onTick()方法中完成。在實際項目事件中,如有定時的任務需求,也可在該方法中自由設置。最後,還需重寫該CountDownTimer的onFinish()方法,觸發listener接口裏的onFinish()
首先是設置倒計時的監聽事件:
public void setDownTimerListener(OnCountDownTimerListener listener) { this.mOnCountDownTimerListener = listener; }
其次是外露一個設置初始時間和活動開始或結束文案的方法:
public void setDownTime(long millis) { this.mMillis = millis; } public void setHeaderText(int eventStatus) { if (eventStatus == HomeItemViewNewArrival.EVENT_NOT_START) { mHeaderText.setText("Start in"); } else { mHeaderText.setText("Ends in"); } }
最後,也是最重要的,須要給倒計時類設計開始與取消倒計時的方法:
public void startDownTimer(int eventStatus) { mArrivalOneMinuteFlag = Constant.SIXTY; mFirstSetTimer = true; //設置須要倒計時的初始值 setSecond(mMillis); createCountDownTimer(eventStatus);// 建立倒計時 mCountDownTimer.start(); } public void cancelDownTimer() { mCountDownTimer.cancel(); }
在開始倒計時的方法中,初始化倒計時的初始值並建立倒計時,最後調用CountDownTimer實例的start()方法開始倒計時。在取消的方法中,直接調用CountDownTimer實例的cancel()方法取消倒計時。
實際調用倒計時控件時,只需在具體佈局中添加該倒計時類佈局,在調用的類中實例化BaseCountDownTimerView。接着,使用實例的setDownTime()、setHeaderText()初始化數據,使用setDownTimerListener()給view實例設置監聽。
最後調用startDownTimer()開啓倒計時。
if (view != null) { view.setDownTime(mDuration); view.setHeaderText(mEventStatus); view.startDownTimer(mEventStatus); view.setDownTimerListener(new BaseCountDownTimerView.OnCountDownTimerListener() { @Override public void onRemain(long millisUntilFinished) { } @Override public void onFinish() { view.cancelDownTimer(); if (bean.mNewArrivalType == TYPE_EVENT && mEventStatus == EVENT_START) { mEventStatus = EVENT_END; //活動狀態以前爲進行中,倒計時變爲0,若是還有下一個活動/新品,則刷新爲下一個活動/新品的數據 refreshNewArrivalBeanDate(bean); onBindView(bean, 1, true, null); } else { setEventStatus(bean); } } @Override public void onArrivalOneMinute() { } });
在多語言環境或者不一樣屏幕條件下,某些語種的控件長度過長,須要自適應控件進行折行顯示以適應UI規範
本來考慮只實例化一個自定義倒計時控件的對象,可是在設計對象佈局的過程當中發現,一個對象不方便同時實如今行尾展現或折行後在第二行行首顯示。所以,本文采用了在佈局的時候同時預置兩個倒計時對象的方法,一個對象位於行尾,另外一個位於第二行的行首。
在measure過程當中,若是測量獲得控件的寬度大於某一個寬度閾值,則初始化次行行首的view,並將行尾的view可見狀態置爲Gone,若小於某一個寬度閾值,則初始化行尾的view,並將次行行首的view可見狀態置爲Gone
首先來看一看xml佈局文件,如下是標題加倒計時位於行尾的一個總體佈局文件main\_view\_header\_new\_arrival
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="@dimen/qb_px_48"> <com.example.website.general.ui.widget.TextView android:id="@+id/new_arrival_txt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_centerInParent="true" android:layout_marginStart="@dimen/qb_px_20" android:text="@string/new_arrival" android:textColor="@color/common_color_de000000" android:textSize="@dimen/qb_px_16" android:textStyle="bold" /> <com.example.website.widget.BaseCountDownTimerView android:id="@+id/count_down_timer_short" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_alignParentEnd="true" android:layout_marginEnd="@dimen/qb_px_20" android:gravity="center_vertical" /> </RelativeLayout>
它的實際展現效果以下圖所示
可是此佈局只能展現單行能展現全部內容的狀況,所以還須要在此佈局上拓展雙行展現的狀況,再看一看main\_list\_item\_home\_new_arrival的佈局
<?xml version="1.0" encoding="utf-8"?> <merge xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" tools:parentTag="android.widget.LinearLayout"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <include layout="@layout/main_view_header_new_arrival"/> <com.example.website.widget.BaseCountDownTimerView android:id="@+id/count_down_timer_long" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_marginStart="@dimen/qb_px_20" android:layout_marginTop="@dimen/qb_px_n_4" android:layout_marginEnd="@dimen/qb_px_20" android:layout_marginBottom="@dimen/qb_px_8" android:gravity="center_vertical" /> </LinearLayout> </merge>
它的實際展現效果以下圖所示
在類中將以上兩個view分別進行實例關聯。
View.inflate(getContext(), R.layout.main_list_item_home_new_arrival, this); mBaseCountDownTimerViewShort = findViewById(R.id.count_down_timer_short); //行尾倒計時view mBaseCountDownTimerViewLong = findViewById(R.id.count_down_timer_long); //次行行首倒計時view
經過以上的步驟搞定了兩種狀況下倒計時控件的佈局,接下來就該考慮折行展現的判斷條件了。
在多語言環境中,標題textview與倒計時view的寬度都是不肯定的,所以須要綜合考慮兩個控件的寬度。同時,由於策劃要求,還需考慮某些語種特殊狀況的展現要求。判斷代碼以下所示:
private boolean isShortCountDownTimerViewShow() { String languageCode = LocaleManager.getInstance().getCurrentLanguage(); if (Constant.EN_US.equals(languageCode) || Constant.EN_GB.equals(languageCode) || Constant.EN_AU.equals(languageCode)) { //因策劃要求,美式英語、英國英語、澳大利亞英語,強制在New Arrivals標題欄右側展現 return true; } else { View newArrivalHeader = inflate(mContext, R.layout.main_view_header_new_arrival, null); TextView newArrivalTextView = newArrivalHeader.findViewById(R.id.new_arrival_txt); LinearLayout countDownTimer = newArrivalHeader.findViewById(R.id.count_down_timer_short); int measureSpecW = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); int measureSpecH = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); newArrivalTextView.measure(measureSpecW, measureSpecH); countDownTimer.measure(measureSpecW, measureSpecH); VLog.i(TAG, countDownTimer.getMeasuredWidth() + "--" + newArrivalTextView.getMeasuredWidth()); if (countDownTimer.getMeasuredWidth() + newArrivalTextView.getMeasuredWidth() <= mContext.getResources().getDimensionPixelSize(R.dimen.qb_px_302)) { return true; } else { return false; } } }
在代碼中,能夠根據實際須要定製具體某幾款語言是否換行顯示。
而對於剩下的大多數語言,可使用MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)獲取measureSpecW 和 measureSpecH ,第一個參數是系統測量該View後獲得的規格值,這裏使用0表明省略(在系統對該View繪製以前就直接調用了measure方法,因此寬高爲0,該值與最終獲取的寬高無關),第二個參數MeasureSpec.UNSPECIFIED表明父容器不對View有任何限制。獲取完成後也就順利完成具體view寬度的測量。
經過該方法的返回值,咱們就能夠控制兩個倒計時view的展現與隱藏,從而達到自適應折行展現的效果。
if (isShortCountDownTimerViewShow()) { initCountDownTimerView(mBaseCountDownTimerViewShort, bean); mBaseCountDownTimerViewShort.setVisibility(VISIBLE); mBaseCountDownTimerViewLong.setVisibility(GONE); } else { initCountDownTimerView(mBaseCountDownTimerViewLong, bean); mBaseCountDownTimerViewShort.setVisibility(GONE); mBaseCountDownTimerViewLong.setVisibility(VISIBLE); }
此外,該方法也不侷限於倒計時控件view,針對多語言中各類各樣的自定義view,依然可使用這種測量方法實現自適應換行的美觀展現。
從效果圖上能夠看到,時、分、秒都是兩位數,且數字的變化規律都相同:首先是從個位數開始變化,舊數字從正常展現區域向上移動必定距離,新數字從下向上移動必定距離到達正常展現區域。若是個位數遞減至0,則十位數須要遞減,因此變化是十位和個位一塊兒移動。
具體的實現思路爲:
一、將時/分/秒的兩位數當成一個數字滾動組件;
二、將數字滾動組件的兩位數,拆分紅一個數字數組,變化操做針對數組中的單個元素操做便可;
三、保存舊數字,將舊數字和新數字的數組元素逐個比較,數字相同的位繪製新數字,數字不一樣的位一塊兒移動便可;
四、在移動數字時,須要將舊數字向上移動,移動的距離是 0 至 負的最大滾動距離;同時要將新數字向上移動,移動距離爲最大滾動距離 至 0;其中最大滾動距離是數字滾動控件的高度,該值須要根據實際的UI稿肯定。
倒計時滾動組件繼承自TextView,在構造函數中設置【最大滾動距離】和【畫筆相關屬性】,這二者都須要根據實際UI稿肯定。
其中,最大滾動距離mMaxMoveHeight是UI稿中時/分/秒數字控件的總體高度;畫筆設置的字體顏色、大小等,均爲UI稿中時/分/秒數字的字體顏色、大小等。具體代碼以下所示:
//構造函數 public NumberFlipView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mResources = context.getResources(); //最大滾動高度18dp mMaxMoveHeight = mResources.getDimensionPixelSize(R.dimen.qb_px_18); //設置畫筆相關屬性 setPaint(); } //設置畫筆相關屬性 private void setPaint() { //設置繪製數字爲白色 mPaint.setColor(Color.WHITE); //設置繪製數字樣式爲實心 mPaint.setStyle(Paint.Style.FILL); //設置繪製數字字體加粗 mPaint.setFakeBoldText(true); //設置繪製文字大小14dp mPaint.setTextSize(mResources.getDimensionPixelSize(R.dimen.qb_px_14)); }
繪製倒計時數字是經過重寫onDraw()實現的。首先拆分舊數字和新數字成爲相應的數字數組;
具體代碼以下所示:
//拆分新數字成爲新數字數組 for (int i = 0; i < mNewNumber.length(); i++) { mNewNumberArray.add(String.valueOf(mNewNumber.charAt(i))); } //拆分老數字成爲老數字數組 for (int i = 0; i < mOldNumber.length(); i++) { mOldNumberArray.add(String.valueOf(mOldNumber.charAt(i))); }
而後繪製數字:繪製新數字時,逐位判斷舊數字和新數字是否相同,若是數字相同,直接繪製新數字;若是數字不相同,舊數字和新數字均須要移動。
具體代碼以下所示:
//兩位數的newNumber的文字寬度 int textWidth = mResources.getDimensionPixelSize(R.dimen.qb_px_16); float curTextWidth = 0; for (int i = 0; i < mNewNumberArray.size(); i++) { //newNumber中每一個數字的邊界 mPaint.getTextBounds(mNewNumberArray.get(i), 0, mNewNumberArray.get(i).length(), mTextRect); //newNumber中每一個數字的寬度 int numWidth = mResources.getDimensionPixelSize(R.dimen.qb_px_5); //逐位判斷舊數字和新數字是否相同 if (mNewNumberArray.get(i).equals(mOldNumberArray.get(i))) { //數字相同,直接繪製新數字 canvas.drawText(mNewNumberArray.get(i), getWidth() * ONE_HALF - textWidth * ONE_HALF + curTextWidth, getHeight() * ONE_HALF + mTextRect.height() * ONE_HALF, mPaint); } else { //數字不相同,舊數字和新數字均須要移動 canvas.drawText(mOldNumberArray.get(i), getWidth() * ONE_HALF - textWidth * ONE_HALF + curTextWidth, mOldNumberMoveHeight + getHeight() * ONE_HALF + mTextRect.height() * ONE_HALF, mPaint); canvas.drawText(mNewNumberArray.get(i), getWidth() * ONE_HALF - textWidth * ONE_HALF + curTextWidth, mNewNumberMoveHeight + getHeight() * ONE_HALF + mTextRect.height() * ONE_HALF, mPaint); } curTextWidth += (numWidth + mResources.getDimensionPixelSize(R.dimen.qb_px_3));
getWidth()獲取的是倒計時控件的整個寬度;textWidth是兩位數字的寬度;numWidth是單個數字的寬度;curTextWidth是每一個數字水平起始繪製位置的間距,curTextWidth=numWidth+兩個數字之間的間距。
十位數字的水平繪製起始位置爲getWidth()/2 + textWidth/2;個位數字的水平繪製起始位置爲getWidth()/2textWidth/2 + curTextWidth。getHight()獲取的是倒計時控件的整個高度;textRect.height()獲取的是數字的高度。
舊數字的垂直繪製起始位置爲mOldNumberMoveHeight + getHeight()/2 + textRect.height()/2;新數字的垂直繪製起始位置爲mNewNumberMoveHeightgetHeight()/2 + textRect.height()/2。
舊數字和新數字的滾動效果是經過ValueAnimator不斷改變舊數字的滾動距離mOldNumberMoveHeight和新數字的滾動距離mNewNumberMoveHeight實現的。
在規定的動畫時間FLIP\_NUMBER\_DURATION內,mNewNumberMoveHeight須要從最大滾動距離mMaxMoveHeight變爲0,mOldNumberMoveHeight須要從0變爲負的最大滾動距離mMaxMoveHeight;每次計算出新的滾動距離後,調用invalidate()方法,觸發onDraw()方法,不斷地繪製舊數字和新數字,以實現數字滾動的效果。
具體代碼以下所示:
/* 利用ValueAnimator,在規定時間FLIP_NUMBER_DURATION以內,將值從MAX_MOVE_HEIGHT變爲0, 每次值變化都賦給mNewNumberMoveHeight,同時將mNewNumberMoveHeight - MAX_MOVE_HEIGHT的值賦給mOldNumberMoveHeight, 並從新繪製,實現新數字和舊數字的上滑; */ mNumberAnimator = ValueAnimator.ofFloat(mMaxMoveHeight, 0); mNumberAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mNewNumberMoveHeight = (float) animation.getAnimatedValue(); mOldNumberMoveHeight = mNewNumberMoveHeight - mMaxMoveHeight; invalidate(); } }); mNumberAnimator.setDuration(FLIP_NUMBER_DURATION); mNumberAnimator.start();
首先在佈局中引入,用法和TextView相同。下圖爲時、分、秒對應的佈局:
<!--時--> <com.example.materialdesginpractice.NumberFlipView android:id="@+id/hours_tv" android:layout_width="@dimen/qb_px_22" android:layout_height="@dimen/qb_px_18" android:gravity="center" android:background="@drawable/number_bg" android:textSize="@dimen/qb_px_14" android:textColor="@color/common_color_ffffff"/> <!--分--> <com.example.materialdesginpractice.NumberFlipView android:id="@+id/min_tv" android:layout_width="@dimen/qb_px_22" android:layout_height="@dimen/qb_px_18" android:gravity="center" android:background="@drawable/number_bg" android:textSize="@dimen/qb_px_14" android:textColor="@color/common_color_ffffff"/> <!--秒--> <com.example.materialdesginpractice.NumberFlipView android:id="@+id/sec_tv" android:layout_width="@dimen/qb_px_22" android:layout_height="@dimen/qb_px_18" android:gravity="center" android:background="@drawable/number_bg" android:textSize="@dimen/qb_px_14" android:textColor="@color/common_color_ffffff"/>
而後經過id找到對應的倒計時數字控件:
mHourTextView = findViewById(R.id.hours_tv); mMinTextView = findViewById(R.id.min_tv); mSecondTextView = findViewById(R.id.sec_tv);
最後調用時/分/秒倒計時數字控件的方法,設置倒計時初始值或者倒計時新數字。若是是首次進行倒計時,須要調用setInitialNumber()方法設置初始值;不然調用flipNumber()方法設置新的倒計時數值。
具體用法以下所示:
if (mFirstSetTimer) { mHourTextView.setInitialNumber(hours); mMinTextView.setInitialNumber(minute); mSecondTextView.setInitialNumber(second); mFirstSetTimer = false; } else { mHourTextView.flipNumber(hours); mMinTextView.flipNumber(minute); mSecondTextView.flipNumber(second); }
在實現中,倒計時控件是做爲ListView的子元素,並且ListView是處於一個Fragment中。
爲了減小功耗,須要在倒計時控件不在可見範圍內時,暫停倒計時;當倒計時控件從新出如今可見範圍內時,從新開始倒計時。下圖是倒計時暫停與開始的場景。
頁面滑動,倒計時控件滑出可視區域,當倒計時控件滑出ListView的可視範圍內,須要暫停倒計時。該狀況的重點是:須要判斷出子view是否已經移出ListView中。
若是應用只須要兼容安卓7及以上,能夠經過重寫onDetachedFromWindow()方法,在方法體內進行取消倒計時的操做。由於每當子view移出ListView時就會調用這個方法。
@Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); //移出屏幕調用,暫停倒計時 stopCountDownTimerAndAnimation(); }
若是應用須要兼容安卓7如下,則上述方法會失效,由於onDetachedFromWindow()方法並不兼容低版本。可是但是經過重寫onStartTemporaryDetach()方法實現相同的效果。
@Override public void onStartTemporaryDetach() { super.onStartTemporaryDetach(); //移出屏幕調用,暫停倒計時 stopCountDownTimerAndAnimation(); }
經過tab切換到其餘Fragment
當倒計時控件位於可視範圍內,此時經過tab切換到其餘Fragment時,須要暫停倒計時。該狀況下倒計時控件所在的Fragment會隱藏,能夠在Fragment隱藏時獲取倒計時控件的View,而後調用其方法暫停倒計時。
@Override public void onFragmentHide() { super.onFragmentHide(); //暫停倒計時 stopNewArrivalCountDownTimerAndAnimation(); }
爲了獲取倒計時控件所在的View對象,經過遍歷ListView可視範圍內的子View,判斷其是不是倒計時控件所在的View對象。而後調用倒計時控件所在View對象的stopCountDownTimerAndAnimation()方法,暫停倒計時。
/** * 獲取倒計時控件所在的view對象,暫停倒計時 */ private void stopNewArrivalCountDownTimerAndAnimation() { if (mListView != null) { for (int index = 0; index < mListView.getChildCount(); index++) { View view = mListView.getChildAt(index); if (view instanceof HomeItemViewNewArrival) { ((HomeItemViewNewArrival) view).stopCountDownTimerAndAnimation(); } } } }
應用切換至後臺/跳轉到其餘界面
當倒計時控件位於可視範圍內,此時應用切換到至後臺 或者 點擊倒計時控件所在界面的其餘內容,跳轉到其餘界面,都須要暫停倒計時。因爲這些狀況都會觸發倒計時所在Fragment的onStop()方法。所以能夠重寫onStop(),並在該方法體內獲取倒計時控件的View,而後暫停倒計時。
stopNewArrivalCountDownTimerAndAnimation()方法同上。
@Override public void onStop() { super.onStop(); //暫停倒計時 stopNewArrivalCountDownTimerAndAnimation(); }
頁面滑動,倒計時控件滑入可視區域
當倒計時控件滑出可視區域後,再次滑入可視區域,會自動調用Adapter的getView()方法,而後調用倒計時控件的onBindView()方法。因爲onBindView()方法中會初始化倒計時控件,所以該狀況下,無需再手動開始倒計時。
經過tab切換回到倒計時所在的Fragment
經過tab切換回到倒計時控件所在的Fragment,若此時倒計時控件在可視範圍內,則須要從新開始倒計時。因爲該狀況下Fragment會從新顯示,所以能夠在Fragment顯示時獲取倒計時控件的View,而後調用其方法從新開始倒計時。
@Override public void onFragmentShow(int source, int floor) { super.onFragmentShow(source, floor); //從新開始倒計時 refreshNewArrival(); }
一樣,爲了獲取倒計時控件所在的View對象,須要經過遍歷ListView可視範圍內的子View,判斷其是不是倒計時控件所在的View對象。而後調用倒計時控件所在View對象的refreshEventStatus ()方法,開始倒計時。
/** * 獲取倒計時控件所在的view對象,開始倒計時 */ private void refreshNewArrival() { if (mListView != null) { for (int index = 0; index < mListView.getChildCount(); index++) { View view = mListView.getChildAt(index); if (view instanceof HomeItemViewNewArrival) { ((HomeItemViewNewArrival) view).refreshEventStatus(); } } } }
應用切換回前臺/從其餘界面回退
當應用切換到回前臺 或者 從其餘界面回退到倒計時控件所在的界面,若此時倒計時控件在可視範圍內,則都須要從新開始倒計時。因爲這些狀況都會觸發倒計時所在Fragment的onResume()方法。所以能夠重寫onResume(),並在該方法體內獲取倒計時控件的View,而後調用其方法從新開始倒計時。
其中refreshNewArrival()方法同上。
@Override public void onResume() { super.onResume(); //從新開始倒計時 refreshNewArrival(); }
做者:vivo 互聯網客戶端團隊Liu Zhiyi、Zhen Yiqing