android 特卖列表倒计时卡顿问题
时间:2022-04-26
本文章向大家介绍android 特卖列表倒计时卡顿问题,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
在Android的开发中,我们经常遇见倒计时的操作,通常使用Timer和Handler共同操作来完成。当然也可以使用Android系统控件CountDownTimer,这里我们封装成一个控件,也方便大家的使用。
首先上一张效果图吧:
说一下造成卡顿的原因,由于滑动的时候,adapter的getView频繁的创建和销毁,就会出现卡顿和数据错位问题,那么我们每一个item的倒计时就需要单独维护,这里我用的Handler与timer及TimerTask结合的方法,我们知道TimerTask运行在自己子线程,然后通过Timer的schedule()方法实现倒计时功能,最后通过Hander实现View的刷新,其核心代码如下:
public class CountDownView extends LinearLayout {
@BindView(R.id.tv_day)
TextView tvDay;
@BindView(R.id.tv_hour)
TextView tvHour;
@BindView(R.id.tv_minute)
TextView tvMinute;
@BindView(R.id.tv_second)
TextView tvSecond;
@BindView(R.id.day)
TextView day;
@BindView(R.id.hour)
TextView hour;
@BindView(R.id.minute)
TextView minute;
private Context context;
private int viewBg;//倒计时的背景
private int cellBg;//每个倒计时的背景
private int cellTextColor;//文字颜色
private int textColor;//外部:等颜色
private int textSize = 14;//外部文字大小
private int cellTextSize = 12;//cell文字大小
private TimerTask timerTask = null;
private Timer timer = new Timer();
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
countDown();
}
};
public CountDownView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
this.context = context;
}
public CountDownView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
initAttrs(attrs, defStyleAttr);
initView(context);
}
private void initAttrs(AttributeSet attrs, int defStyle) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CountDownView, defStyle,0);
viewBg = typedArray.getColor(R.styleable.CountDownView_viewBg, Color.parseColor("#FFFFFF"));
cellBg = typedArray.getColor(R.styleable.CountDownView_cellBg, Color.parseColor("#F4F4F4"));
cellTextColor = typedArray.getColor(R.styleable.CountDownView_cellTextColor, Color.parseColor("#646464"));
textColor = typedArray.getColor(R.styleable.CountDownView_TextColor, Color.parseColor("#B3B3B3"));
textSize = (int) typedArray.getDimension(R.styleable.CountDownView_TextSize, UIUtils.dp2px(getContext(), 14));
cellTextSize = (int) typedArray.getDimension(R.styleable.CountDownView_cellTextSize, UIUtils.dp2px(getContext(), 12));
typedArray.recycle();
}
private void initView(Context context) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.layout_countdown_layout, this);
ButterKnife.bind(view);
initProperty();
}
private void initProperty() {
tvDay.setBackgroundColor(cellBg);
tvHour.setBackgroundColor(cellBg);
tvMinute.setBackgroundColor(cellBg);
tvSecond.setBackgroundColor(cellBg);
tvDay.setTextColor(cellTextColor);
tvHour.setTextColor(cellTextColor);
tvMinute.setTextColor(cellTextColor);
tvSecond.setTextColor(cellTextColor);
day.setTextColor(textColor);
hour.setTextColor(textColor);
minute.setTextColor(textColor);
}
public void setLeftTime(long leftTime) {
if (leftTime <= 0) return;
long time = leftTime / 1000;
long day = time / (3600 * 24);
long hours = (time - day * 3600 * 24) / 3600;
long minutes = (time - day * 3600 * 24 - hours * 3600) / 60;
long seconds = time - day * 3600 * 24 - hours * 3600 - minutes * 60;
setTextTime(time);
}
public void start() {
if (timerTask == null) {
timerTask = new TimerTask() {
@Override
public void run() {
handler.sendEmptyMessage(0);
}
};
timer.schedule(timerTask, 1000, 1000);
// timer.schedule(new TimerTask() {
// @Override
// public void run() {
// handler.sendEmptyMessage(0);
// }
// }, 0, 1000);
}
}
public void stop() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
//保证天,时,分,秒都两位显示,不足的补0
private void setTextTime(long time) {
String[] s = TimeUtils.formatTimer(time);
tvDay.setText(s[0]);
tvHour.setText(s[1]);
tvMinute.setText(s[2]);
tvSecond.setText(s[3]);
}
private void countDown() {
if (isCarry4Unit(tvSecond)) {
if (isCarry4Unit(tvMinute)) {
if (isCarry4Unit(tvHour)) {
if (isCarry4Unit(tvDay)) {
stop();
}
}
}
}
}
private boolean isCarry4Unit(TextView tv) {
int time = Integer.valueOf(tv.getText().toString());
time = time - 1;
if (time < 0) {
time = 59;
tv.setText(time + "");
return true;
} else if (time < 10) {
tv.setText("0" + time);
return false;
} else {
tv.setText(time + "");
return false;
}
}
}
另一种写法:
public class CountDownTimerView extends LinearLayout {
private TextView hourView, minuteView, secondView;
private LimitTimer mTimer;
private Handler handler;
public static final int START_LIMIT_TIME_MSG = 0x0111;
public static final int END_LIMIT_TIME_MSG = 0x0112;
private long endTime, leftTime;
private LimitTimeListener listener=null;
public CountDownTimerView(Context context) {
super(context);
initView(context);
}
public CountDownTimerView(Context context, AttributeSet attrs) {
super(context, attrs);
initView(context);
}
private void initView(Context context) {
setOrientation(HORIZONTAL);
setGravity(Gravity.CENTER_VERTICAL);
inflate(context, R.layout.layout_countdown_layout, this);
hourView = (TextView) findViewById(R.id.tv_hour);
minuteView = (TextView) findViewById(R.id.tv_minute);
secondView = (TextView) findViewById(R.id.tv_second);
}
public long getLeftTime() {
return leftTime;
}
//设置剩余时间
public void initLeftTime(long endTime) {
endTime=endTime*1000;
if (null != mTimer) {
mTimer.cancel();
mTimer = null;
}
this.endTime = endTime;
mTimer = new LimitTimer(endTime, 1000);
mTimer.start();
if (handler != null) {
handler.sendEmptyMessage(START_LIMIT_TIME_MSG);
}
}
/**
* 如果该控件使用在碎片中,返回时,则最好还是要stop
*/
public void stopTimeCount() {
if (null != mTimer) {
mTimer.cancel();
mTimer = null;
}
}
public Handler getHandler() {
return handler;
}
public void setHandler(Handler handler) {
this.handler = handler;
}
private class LimitTimer extends CountDownTimer {
public LimitTimer(long millisInFuture, long countDownInterval) {
super(0 != leftTime ? leftTime : endTime, countDownInterval);
}
@Override
public void onTick(long millisUntilFinished) {
leftTime = millisUntilFinished;
long totalSecond = millisUntilFinished / 1000;
int second = (int) (totalSecond % 60);
int minute = (int) ((totalSecond / 60) % 60);
int hour = (int) ((totalSecond / 3600) % 24);
int day = (int) (totalSecond / (3600 * 24));
formatView(hourView, hour);
formatView(minuteView, minute);
formatView(secondView, second);
}
@Override
public void onFinish() {
String zero = "00";
hourView.setText(zero);
minuteView.setText(zero);
secondView.setText(zero);
if (null != handler) {
handler.sendEmptyMessage(END_LIMIT_TIME_MSG);
}
if (listener!=null){
listener.onTimeOver(true);
}
}
private void formatView(TextView view, int time) {
DecimalFormat df = new DecimalFormat("#00");
view.setText(df.format(time));
}
}
//倒计时结束监听
public void setOnLimitTimeListener(LimitTimeListener listener) {
this.listener = listener;
}
public interface LimitTimeListener {
void onTimeOver(boolean flag);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if (handler != null) {
handler.removeMessages(START_LIMIT_TIME_MSG);
}
}
}
涉及到的布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_hour"
style="@style/style_countdown" />
<TextView
style="@style/style_wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:layout_gravity="center_vertical"
android:text="时"
android:textColor="@color/color_646464" />
<TextView
android:id="@+id/tv_minute"
style="@style/style_countdown" />
<TextView
style="@style/style_wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:layout_gravity="center_vertical"
android:text="分"
android:textColor="@color/color_646464" />
<TextView
android:id="@+id/tv_second"
style="@style/style_countdown" />
<TextView
style="@style/style_wrap_content"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:layout_gravity="center_vertical"
android:text="秒"
android:textColor="@color/color_646464" />
</LinearLayout>
移动技术交流(Android,ios,RactNtive),请加群:278792776
附上源码地址:点击打开链接
- [译]Laravel 5.0 之事件及处理程序
- 自相关与偏自相关的简单介绍
- [译]Laravel 5.0 之命令及处理程序
- Deep Photo Styletransfer的一种纯Tensorflow实现,教你如何转换图片风格
- 如何提前体验 Laravel 5.5
- Laravel 4 小技巧两则
- [译]Laravel 5.0 之 ValidatesWhenResolved
- Python机器学习的练习七:K-Means聚类和主成分分析
- [译]Laravel 5.0 之方法注入
- [译]Laravel 5.0 之 Middleware (Filter-Style)
- [译]Laravel 5.0 之目录结构与命名空间
- Python机器学习的练习六:支持向量机
- [译]Laravel 5.0 之路由缓存
- [译]Laravel 5.0 之 表单验证类 (Form Requests)
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- 代码失而复得心塞往事 - git stash命令
- 如何通过 Shell 监控异常等待事件和活跃会话
- PHP中环境变量的操作
- 一文读懂JAVA并发容器类ConcurrentHashMap
- Creator3D新版本震撼来袭
- SpringBoot源码学习(十)-Spring类级别注解解析原理
- 从安全切面到Security Mesh
- SpringBoot源码学习(十一) - bean的实例化过程
- 每天一杯力扣快乐水
- Typescript的tsconfig.json
- python和R语言计算蛋白质内部氨基酸相互作用
- 超文本传输协议 - 白话篇
- 为什么你的简历没人看?7份案例分析
- 浅析动态切换数据源的原理(接上篇)
- SpringBoot源码解析(十二)- Autowired是如何注入的