一、前言
随着网购的继续倒退,抢购类倒计时在各类电商利用中已非常常见,这种设计能够进步用户的点击率和下单率等。
然而国内的电商利用大部分都仅反对中文,不适配其余的语言,因而当倒计时与其余文案处于同一行展现时,无需思考倒计时的展现形式。在海内利用中,因为须要适配各种语言,有些小语种的文案较长,因而当倒计时和其余文案处于同一行展现时,须要充分考虑多语言的适配,如何优雅地实现倒计时自适应显示是一个值得沉思的问题。
为进一步优化倒计时成果,咱们为倒计时减少了数字滚动动画,如下图所示。倒计时的性能必然会带来性能的耗费,如何防止倒计时带来的性能问题,本文也将给出相应的解决方案。
二、实现倒计时基本功能
2.1 需要与原理剖析
该控件预期展示两种状态,间隔流动开始还有 X 天 XX:XX:XX 和间隔流动完结还有 X 天 XX:XX:XX,因而须要一个活动状态属性,并通过这个流动开始与否的属性设置工夫前的文案。具体工夫时分秒之间互相独立,因而将它们拆分成独立的 textview 进行解决。
倒计时控件的外围是计时器,安卓中曾经有现成的 CountDownTimer 类可供使用以实现倒计时性能。此外,还须要实现一些监听的接口。
2.2 具体实现
2.2.1 回调监听接口设计
首先,定义回调接口
public interface OnCountDownTimerListener {
/**
* 倒计时正在进行时调用的办法
*
* @param millisUntilFinished 残余的工夫(毫秒)*/
void onRemain(long millisUntilFinished);
/**
* 倒计时完结
*/
void onFinish();
/**
* 每过一分钟调用的办法
*/
void onArrivalOneMinute();}
在该接口中定义三个办法:
onRemain(long millisUntilFinished):倒计时进行中回调的办法,用于后续性能的拓展
onFinish():倒计时完结回调,用于活动状态的切换和计时的暂停等
onArrivalOneMinute():每过一分钟回调,用于定时上报的埋点
2.2.2 view 的构建与绑定
其次,初始化自定义 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);
}
2.2.3 构建外部应用的公有办法
首先结构设置剩余时间的办法,入参是残余的毫秒数,在办法外部将工夫转化为具体的天时分秒,并将后果赋予给 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()
2.2.4 构建私有办法供内部应用
首先是设置倒计时的监听事件:
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() 办法勾销倒计时。
2.3 倒计时类的理论调用
理论调用倒计时控件时,只需在具体布局中增加该倒计时类布局,在调用的类中实例化 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() {}
});
三、实现倒计时整体布局
3.1 需要形容
在多语言环境或者不同屏幕条件下,某些语种的控件长度过长,须要自适应控件进行折行显示以适应 UI 标准
3.2 实施方案
本来思考只实例化一个自定义倒计时控件的对象,然而在设计对象布局的过程中发现,一个对象不不便同时实现在行尾展现或折行后在第二行行首显示。因而,本文采纳了在布局的时候同时预置两个倒计时对象的办法,一个对象位于行尾,另一个位于第二行的行首。
在 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,仍然能够应用这种测量方法实现自适应换行的好看展现。
四、实现倒计时动画成果
4.1 倒计时数字滚动动画的原理剖析
从效果图上能够看到,时、分、秒都是两位数,且数字的变化规律都雷同:首先是从个位数开始变动,旧数字从失常展现区域向上挪动肯定间隔,新数字从下向上挪动肯定间隔达到失常展现区域。如果个位数递加至 0,则十位数须要递加,所以变动是十位和个位一起挪动。
具体的实现思路为:
1、将时 / 分 / 秒的两位数当成一个数字滚动组件;
2、将数字滚动组件的两位数,拆分成一个数字数组,变动操作针对数组中的单个元素操作即可;
3、保留旧数字,将旧数字和新数字的数组元素一一比拟,数字雷同的位绘制新数字,数字不同的位一起挪动即可;
4、在挪动数字时,须要将旧数字向上挪动,挪动的间隔是 0 至 负的最大滚动间隔;同时要将新数字向上挪动,挪动间隔为最大滚动间隔 至 0;其中最大滚动间隔是数字滚动控件的高度,该值须要依据理论的 UI 稿确定。
4.2 具体实现
4.2.1 倒计时滚动组件初始化
倒计时滚动组件继承自 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));
}
4.2.2 绘制倒计时滚动组件
绘制倒计时数字是通过重写 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。
4.2.3 倒计时数字滚动成果实现
旧数字和新数字的滚动成果是通过 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();
4.3 具体应用
首先在布局中引入,用法和 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);
}
五、优化倒计时性能
5.1 倒计时数字滚动动画的原理剖析
在实现中,倒计时控件是作为 ListView 的子元素,而且 ListView 是处于一个 Fragment 中。
为了缩小功耗,须要在倒计时控件不在可见范畴内时,暂停倒计时;当倒计时控件从新呈现在可见范畴内时,从新开始倒计时。下图是倒计时暂停与开始的场景。
5.2 具体实现
5.2.1 暂停倒计时
页面滑动,倒计时控件滑出可视区域,当倒计时控件滑出 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();}
5.2.2 开始倒计时
页面滑动,倒计时控件滑入可视区域
当倒计时控件滑出可视区域后,再次滑入可视区域,会主动调用 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