我的Android应用程序中有一个ListView,它应该包含秒表计时器以及每行中的开始和暂停按钮。问题是,当我在一行中启动计时器时,如果我在ListView中启动属于另一行的另一行,则第二行也从第一行所在的同一时间开始。所以基本上他们并不是独立的。如何修改我的代码,以便每一行都有一个独立的计时器,只有当按下它(启动或停止)按钮时,它才会生效。
这是我的自定义适配器类
public class CustomAdapterStopWatch extends BaseAdapter {
private final LayoutInflater layoutInflater;
Context context;
public static List<String> timerList;
private long startTime = 0L;
private Handler customHandler;
long timeInMilliseconds = 0L;
long timeSwapBuff = 0L;
long updatedTime = 0L;
public CustomAdapterStopWatch(Context context, List<String> timerList) {
this.context = context;
this.timerList = timerList;
layoutInflater = LayoutInflater.from(context);
customHandler = new Handler();
}
@Override
public int getCount() {
return timerList.size();
}
@Override
public Object getItem(int position) {
return timerList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final ViewHolder holder;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.stop_watch_list_view, null);
holder = new CustomAdapterStopWatch.ViewHolder();
holder.timerTextView = (TextView) convertView.findViewById(R.id.timerInListView);
holder.startTimer = (Button) convertView.findViewById(R.id.startTimerInListView);
holder.pauseTimer = (Button) convertView.findViewById(R.id.stopTimerInListView);
holder.timerTextView.setTag(position);
holder.startTimer.setTag(position);
holder.pauseTimer.setTag(position);
convertView.setTag(holder);
// Alarm alarm = (Alarm) getItem(position);
} else {
holder = (CustomAdapterStopWatch.ViewHolder) convertView.getTag();
}
holder.timerTextView.setText(timerList.get(position));
holder.startTimer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startTime = SystemClock.uptimeMillis();
customHandler.postDelayed( new Runnable() {
public void run() {
timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
updatedTime = timeSwapBuff + timeInMilliseconds;
int secs = (int) (updatedTime / 1000);
int mins = secs / 60;
secs = secs % 60;
int milliseconds = (int) (updatedTime % 1000);
holder.timerTextView.setText("" + mins + ":"
+ String.format("%02d", secs) + ":"
+ String.format("%03d", milliseconds));
customHandler.postDelayed(this, 0);
}
}, 0);
}
});
holder.pauseTimer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
timeSwapBuff += timeInMilliseconds;
customHandler.removeCallbacks(new Runnable() {
public void run() {
timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
updatedTime = timeSwapBuff + timeInMilliseconds;
int secs = (int) (updatedTime / 1000);
int mins = secs / 60;
secs = secs % 60;
int milliseconds = (int) (updatedTime % 1000);
holder.timerTextView.setText("" + mins + ":"
+ String.format("%02d", secs) + ":"
+ String.format("%03d", milliseconds));
customHandler.postDelayed(this, 0);
}
});
}
});
return convertView;
}
我使用的布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/timerInListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:text="00:00:00"
android:textSize="25dp"
android:textColor="@color/colorGray"/>
<Button
android:id="@+id/stopTimerInListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="20dp"
android:text="Pause"
android:background="@color/colorGreen"/>
<Button
android:id="@+id/startTimerInListView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="22dp"
android:text="Start"
android:background="@color/colorGreen"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/stopTimerInListView"
android:layout_toStartOf="@+id/stopTimerInListView"
android:layout_marginEnd="22dp" />
</RelativeLayout>
我使用适配器的秒表类如下:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stop_watch);
listView = (ListView) findViewById(R.id.listViewInStopWatch);
timerList = new ArrayList<String>();
timerList.add("00:00:000");
timerList.add("00:00:000");
timerList.add("00:00:000");
timerList.add("00:00:000");
customAdapter = new CustomAdapterStopWatch(StopWatch.this, timerList);
listView.setAdapter(customAdapter);
}
更新
我将代码更改为:
public class CustomAdapterStopWatch extends BaseAdapter {
private final LayoutInflater layoutInflater;
Context context;
public static List<TimerState> timerList;
private long startTime = 0L;
private Handler customHandler;
long timeInMilliseconds = 0L;
long timeSwapBuff = 0L;
long updatedTime = 0L;
HashMap<ViewHolder,Integer> mHashHolder;
TimerState state;
public CustomAdapterStopWatch(Context context, final List<TimerState> timerList) {
this.context = context;
this.timerList = timerList;
layoutInflater = LayoutInflater.from(context);
customHandler = new Handler();
mHashHolder = new HashMap<ViewHolder,Integer>();
state = new TimerState();
// assuming you need to update the timer after every second.
customHandler.postDelayed(new Runnable() {
@Override
public void run() {
for (ViewHolder holder: mHashHolder.keySet()) {
state = timerList.get(mHashHolder.get(holder));
// update the state
}
}
}, 1000);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.stop_watch_list_view, null);
holder = new CustomAdapterStopWatch.ViewHolder();
holder.timerTextView = (TextView) convertView.findViewById(R.id.timerInListView);
holder.startTimer = (Button) convertView.findViewById(R.id.startTimerInListView);
holder.pauseTimer = (Button) convertView.findViewById(R.id.stopTimerInListView);
holder.timerTextView.setTag(position);
holder.startTimer.setTag(position);
holder.pauseTimer.setTag(position);
convertView.setTag(holder);
mHashHolder.put(holder,position);
// Alarm alarm = (Alarm) getItem(position);
} else {
// holder is being used again, so need to update its new position
// following line should be synchronized if timer is updated
// in a separate thread
mHashHolder.put(holder, position);
holder = (CustomAdapterStopWatch.ViewHolder) convertView.getTag();
}
holder.timerTextView.setText(timerList.get(position).currentTime);
holder.startTimer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startTime = SystemClock.uptimeMillis();
if (state.isPaused) {
state.timeSinceStarted = String.valueOf(SystemClock.uptimeMillis());
state.isPaused = false;
} else {
// nothing
}
}
});
holder.pauseTimer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (state.isPaused) {
state.isPaused = true;
}
}
});
return convertView;
}
public void startTime(final TextView t) {
Runnable updateTimerThread = new Runnable() {
public void run() {
timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
updatedTime = timeSwapBuff + timeInMilliseconds;
int secs = (int) (updatedTime / 1000);
int mins = secs / 60;
secs = secs % 60;
int milliseconds = (int) (updatedTime % 1000);
t.setText("" + mins + ":"
+ String.format("%02d", secs) + ":"
+ String.format("%03d", milliseconds));
customHandler.postDelayed(this, 0);
}
};
}
然而,按下按钮后,没有任何反应。我错过了什么吗?
更新2
public CustomAdapterStopWatch(Context context, final List<TimerState> timerList) {
this.context = context;
this.timerList = timerList;
layoutInflater = LayoutInflater.from(context);
customHandler = new Handler();
mHashHolder = new HashMap<ViewHolder,Integer>();
state = new TimerState();
// assuming you need to update the timer after every second.
customHandler.postDelayed(new Runnable() {
@Override
public void run() {
for (ViewHolder holder: mHashHolder.keySet()) {
state = timerList.get(mHashHolder.get(holder));
long tmp;
if (!state.isPaused) {
tmp = SystemClock.uptimeMillis();
state.currentTime += tmp - state.timeSinceStarted;
state.timeSinceStarted = tmp;
}
updatedTime = state.currentTime;
int secs = (int) (updatedTime / 1000);
int mins = secs / 60;
secs = secs % 60;
int milliseconds = (int) (updatedTime % 1000);
// set the text of the view in holder
holder.timerTextView.setText("" + mins + ":"
+ String.format("%02d", secs) + ":"
+ String.format("%03d", milliseconds));
// update the state
}
}
}, 1000);
}
更新3
public class CustomAdapterStopWatch extends BaseAdapter {
private final LayoutInflater layoutInflater;
Context context;
public static List<TimerState> timerList;
private long startTime = 0L;
private Handler customHandler;
long timeInMilliseconds = 0L;
long timeSwapBuff = 0L;
long updatedTime = 0L;
HashMap<ViewHolder,Integer> mHashHolder;
TimerState state;
public CustomAdapterStopWatch(final Context context, final List<TimerState> timerList) {
this.context = context;
this.timerList = timerList;
layoutInflater = LayoutInflater.from(context);
customHandler = new Handler();
mHashHolder = new HashMap<ViewHolder,Integer>();
state = new TimerState();
// assuming you need to update the timer after every second.
customHandler.postDelayed(new Runnable() {
@Override
public void run() {
for (ViewHolder holder: mHashHolder.keySet()) {
state = timerList.get(mHashHolder.get(holder));
long tmp = 0;
if (!state.isPaused) {
tmp = SystemClock.uptimeMillis();
state.currentTime += tmp - state.timeSinceStarted;
state.timeSinceStarted = tmp;
}
updatedTime = state.currentTime;
int secs = (int) (updatedTime / 1000);
int mins = secs / 60;
secs = secs % 60;
int milliseconds = (int) (updatedTime % 1000);
// set the text of the view in holder
holder.timerTextView.setText("" + mins + ":"
+ String.format("%02d", secs) + ":"
+ String.format("%03d", milliseconds));
// notifyDataSetChanged();
Toast.makeText(context,"I got here", Toast.LENGTH_SHORT).show();
// update the state
}
customHandler.postDelayed(this, 1000);
}
}, 1000);
}
@Override
public int getCount() {
return timerList.size();
}
@Override
public Object getItem(int position) {
return timerList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.stop_watch_list_view, null);
holder = new CustomAdapterStopWatch.ViewHolder();
holder.timerTextView = (TextView) convertView.findViewById(R.id.timerInListView);
holder.startTimer = (Button) convertView.findViewById(R.id.startTimerInListView);
holder.pauseTimer = (Button) convertView.findViewById(R.id.stopTimerInListView);
holder.timerTextView.setTag(position);
holder.startTimer.setTag(position);
holder.pauseTimer.setTag(position);
convertView.setTag(holder);
mHashHolder.put(holder,position);
// Alarm alarm = (Alarm) getItem(position);
} else {
// holder is being used again, so need to update its new position
// following line should be synchronized if timer is updated
// in a separate thread
mHashHolder.put(holder, position);
holder = (CustomAdapterStopWatch.ViewHolder) convertView.getTag();
}
holder.timerTextView.setText(String.valueOf(timerList.get(position)));
holder.startTimer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startTime = SystemClock.uptimeMillis();
if (state.isPaused) {
state.timeSinceStarted = SystemClock.uptimeMillis();
state.isPaused = false;
} else {
// nothing
}
}
});
holder.pauseTimer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (!state.isPaused) {
state.isPaused = true;
}
}
});
return convertView;
}
public void startTime(final TextView t) {
Runnable updateTimerThread = new Runnable() {
public void run() {
timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
updatedTime = timeSwapBuff + timeInMilliseconds;
int secs = (int) (updatedTime / 1000);
int mins = secs / 60;
secs = secs % 60;
int milliseconds = (int) (updatedTime % 1000);
t.setText("" + mins + ":"
+ String.format("%02d", secs) + ":"
+ String.format("%03d", milliseconds));
customHandler.postDelayed(this, 0);
}
};
}
private class ViewHolder {
TextView timerTextView;
Button startTimer;
Button pauseTimer;
}
}
答案 0 :(得分:1)
实际上有两件事你做错了。
您正在为每个计时器使用相同的变量updateTime。相反,你应该为每个计时器保持一个状态。所以最好有一个单独的类。例如,
class TimerState { boolean isPaused = true; long currentTime = 0; String timeSinceStarted; // matters only when the timer is started }
所以将上面类的数组列表传递给你的适配器。所以timerList将是TimerState的arrayList。
$str1
您的适配器将
public class CustomAdapterStopWatch extends BaseAdapter { private final LayoutInflater layoutInflater; Context context; public static List timerList; private long startTime = 0L; private Handler customHandler; long timeInMilliseconds = 0L; long timeSwapBuff = 0L; long updatedTime = 0L; // this will contain the holder of all visible views HashMap mHashHolder; public CustomAdapterStopWatch(Context context, List timerList) { this.context = context; this.timerList = timerList; layoutInflater = LayoutInflater.from(context); customHandler = new Handler(); // assuming you need to update the timer after every second. customHandler.postDelayed(new Runnable() { @Override public void run() { TimerState state; for (ViewHolderItem holder: mHashHolder.keySet()) { state = timerList.get(mHashHolder.get(holder)); long tmp; if (!state.isPaused) { tmp = SystemClock.uptimeMillis(); state.currentTime += tmp - state.timeSinceStarted) state.timeSinceStarted = tmp; } updatedTime = state.currentTime int secs = (int) (updatedTime / 1000); int mins = secs / 60; secs = secs % 60; int milliseconds = (int) (updatedTime % 1000); // set the text of the view in holder holder.t.setText("" + mins + ":" + String.format("%02d", secs) + ":" + String.format("%03d", milliseconds)); } customHandler.postDelayed(this, 1000); } }, 1000); } @Override public int getCount() { return timerList.size(); } @Override public Object getItem(int position) { return timerList.get(position); } @Override public long getItemId(int position) { return position; } @Override public View getView(final int position, View convertView, ViewGroup parent) { final ViewHolder holder; if (convertView == null) { convertView = layoutInflater.inflate(R.layout.stop_watch_list_view, null); holder = new CustomAdapterStopWatch.ViewHolder(); holder.timerTextView = (TextView) convertView.findViewById(R.id.timerInListView); holder.startTimer = (Button) convertView.findViewById(R.id.startTimerInListView); holder.pauseTimer = (Button) convertView.findViewById(R.id.stopTimerInListView); holder.timerTextView.setTag(position); holder.startTimer.setTag(position); holder.pauseTimer.setTag(position); convertView.setTag(holder); mHashHolder.put(position, holder); // Alarm alarm = (Alarm) getItem(position); } else { // holder is being used again, so need to update its new position // following line should be synchronized if timer is updated // in a separate thread holder = (CustomAdapterStopWatch.ViewHolder) convertView.getTag(); mHashHolder.put(holder, position) } holder.timerTextView.setText(timerList.get(position)); holder.startTimer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startTime = SystemClock.uptimeMillis(); if (state.isPaused) { state.timeSinceStarted = SystemClock.uptimeMillis(); state.isPaused = false; } else { // nothing } // logic for showing the timer will be in onScroll method } }); holder.pauseTimer.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!state.isPaused) { state.isPaused = True } } }); return convertView; }
答案 1 :(得分:0)
holder.startTimer.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startTime = SystemClock.uptimeMillis();
customHandler.postDelayed( new Runnable() {
public void run() {
timeInMilliseconds = SystemClock.uptimeMillis() - startTime;
updatedTime = timeSwapBuff + timeInMilliseconds;
int secs = (int) (updatedTime / 1000);
int mins = secs / 60;
secs = secs % 60;
int milliseconds = (int) (updatedTime % 1000);
holder.timerTextView.setText("" + mins + ":"
+ String.format("%02d", secs) + ":"
+ String.format("%03d", milliseconds));
customHandler.postDelayed(this, 0);
}
}, 0);
}
});
是你的问题。因此,您会看到您声明了一个要计数的变量,当您开始第二个变量时,您会计算相同的变量updatedTime
。
要使其独立,您需要将函数更改为不依赖于相同的变量。我的第一个想法是在调用计时器的开始和克隆变量时创建一个新的Thread。但是我在很长一段时间没有为android编程。