回答編集履歴
6
追加
test
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
質問の画像を見る限り、まだリストビューがスクロールするほどの件数が無い為に再利用が発生していないようですが、再利用が発生した場合、convertView が null で無い=チェックボックスのリスナが古いまま新しいデータの表示に使われ、チェックボックスの変更は古いデータに対して処理が行われることになってしまいます。
|
4
4
|
|
5
5
|
追加毎にログが出るのは、追加毎にリストに setAdapter し全ての(再)表示が行われて getView が呼ばれるため、デフォルト false のチェックボックスに setChecked で true を設定しているデータで OnCheckedChangeListener が呼ばれている為でしょう。
|
6
|
+
それだけなら常に true と出るはずですが・・・なぜ false になるのかは再現出来ていません。
|
6
7
|
|
7
8
|
---
|
8
9
|
```java
|
5
追加
test
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
レイアウトをインフレートした時だけリスナを定義するのでは、問題があります。
|
2
2
|
getView で convertView が null かどうかによって infrate を制御するのは、ListView が convertView を再利用するからです。
|
3
3
|
質問の画像を見る限り、まだリストビューがスクロールするほどの件数が無い為に再利用が発生していないようですが、再利用が発生した場合、convertView が null で無い=チェックボックスのリスナが古いまま新しいデータの表示に使われ、チェックボックスの変更は古いデータに対して処理が行われることになってしまいます。
|
4
|
+
|
5
|
+
追加毎にログが出るのは、追加毎にリストに setAdapter し全ての(再)表示が行われて getView が呼ばれるため、デフォルト false のチェックボックスに setChecked で true を設定しているデータで OnCheckedChangeListener が呼ばれている為でしょう。
|
4
6
|
|
5
7
|
---
|
6
8
|
```java
|
4
コード修正
test
CHANGED
@@ -82,8 +82,7 @@
|
|
82
82
|
return position;
|
83
83
|
}
|
84
84
|
@Override
|
85
|
-
public @NonNull
|
86
|
-
View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
85
|
+
public @NonNull View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
87
86
|
if(convertView == null) {
|
88
87
|
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
|
89
88
|
convertView.setTag(new ViewHolder(convertView));
|
3
誤字
test
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
レイアウトをインフレートした時だけリスナを定義するのでは、問題があります。
|
2
|
-
getView で convertView が null かどうかによって infrate を制御するのは、ListView が conv
|
2
|
+
getView で convertView が null かどうかによって infrate を制御するのは、ListView が convertView を再利用するからです。
|
3
3
|
質問の画像を見る限り、まだリストビューがスクロールするほどの件数が無い為に再利用が発生していないようですが、再利用が発生した場合、convertView が null で無い=チェックボックスのリスナが古いまま新しいデータの表示に使われ、チェックボックスの変更は古いデータに対して処理が行われることになってしまいます。
|
4
4
|
|
5
5
|
---
|
2
コード修正
test
CHANGED
@@ -95,7 +95,7 @@
|
|
95
95
|
vh.viewCheck.setOnCheckedChangeListener(null); //次の setChecked で古いリスナが実行されないように、先に消す.
|
96
96
|
vh.viewCheck.setChecked(cellDataItem.check);
|
97
97
|
vh.viewCheck.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
98
|
-
cellData
|
98
|
+
cellDataItem.check = isChecked;
|
99
99
|
Log.i("Test", "cellDataList["+position+"]のcheckに["+isChecked+"]を入れました");
|
100
100
|
notifyDataSetChanged(); //データが変わったのでリストに再表示を要求
|
101
101
|
});
|
1
コード追加
test
CHANGED
@@ -1,3 +1,187 @@
|
|
1
1
|
レイアウトをインフレートした時だけリスナを定義するのでは、問題があります。
|
2
2
|
getView で convertView が null かどうかによって infrate を制御するのは、ListView が convartView を再利用するからです。
|
3
3
|
質問の画像を見る限り、まだリストビューがスクロールするほどの件数が無い為に再利用が発生していないようですが、再利用が発生した場合、convertView が null で無い=チェックボックスのリスナが古いまま新しいデータの表示に使われ、チェックボックスの変更は古いデータに対して処理が行われることになってしまいます。
|
4
|
+
|
5
|
+
---
|
6
|
+
```java
|
7
|
+
import android.graphics.Color;
|
8
|
+
import android.os.*;
|
9
|
+
import android.util.Log;
|
10
|
+
import android.view.*;
|
11
|
+
import android.widget.*;
|
12
|
+
|
13
|
+
import androidx.annotation.NonNull;
|
14
|
+
import androidx.appcompat.app.AppCompatActivity;
|
15
|
+
import androidx.lifecycle.*;
|
16
|
+
|
17
|
+
import java.text.*;
|
18
|
+
import java.util.*;
|
19
|
+
|
20
|
+
public class MainActivity extends AppCompatActivity {
|
21
|
+
@Override
|
22
|
+
protected void onCreate(Bundle savedInstanceState) {
|
23
|
+
super.onCreate(savedInstanceState);
|
24
|
+
setContentView(R.layout.activity_main);
|
25
|
+
|
26
|
+
getSupportActionBar().setTitle(R.string.app_name);
|
27
|
+
|
28
|
+
ListView listView = findViewById(R.id.list_view);
|
29
|
+
ListViewAdapter adapter = new ListViewAdapter();
|
30
|
+
listView.setAdapter(adapter);
|
31
|
+
|
32
|
+
NowTimer nowTimer = new NowTimer(100);
|
33
|
+
getLifecycle().addObserver(nowTimer);
|
34
|
+
|
35
|
+
Counter counter = new Counter(0, 9999);
|
36
|
+
|
37
|
+
EditText commentText = findViewById(R.id.comment_text);
|
38
|
+
|
39
|
+
Button addButton = findViewById(R.id.add_button);
|
40
|
+
addButton.setOnClickListener(v -> {
|
41
|
+
CellData cellData = new CellData(nowTimer.getDate(), counter.getCount(), commentText.getText().toString());
|
42
|
+
adapter.add(cellData);
|
43
|
+
});
|
44
|
+
}
|
45
|
+
|
46
|
+
// データを保持するクラス
|
47
|
+
private static class CellData {
|
48
|
+
boolean check = false;
|
49
|
+
Date time;
|
50
|
+
int count;
|
51
|
+
String comment;
|
52
|
+
|
53
|
+
CellData(Date time, int count, String comment) {
|
54
|
+
this.time = time;
|
55
|
+
this.count = count;
|
56
|
+
this.comment = comment;
|
57
|
+
}
|
58
|
+
}
|
59
|
+
|
60
|
+
// カスタムアダプタークラス
|
61
|
+
private class ListViewAdapter extends BaseAdapter {
|
62
|
+
private final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
|
63
|
+
private final int[] ROW_BG_COLORS = { Color.rgb(240,240,240), Color.WHITE };
|
64
|
+
|
65
|
+
private List<CellData> cellDataList = new ArrayList<>();
|
66
|
+
|
67
|
+
void add(CellData cellData) {
|
68
|
+
cellDataList.add(cellData);
|
69
|
+
notifyDataSetChanged();
|
70
|
+
}
|
71
|
+
|
72
|
+
@Override
|
73
|
+
public int getCount() {
|
74
|
+
return cellDataList.size();
|
75
|
+
}
|
76
|
+
@Override
|
77
|
+
public Object getItem(int position) {
|
78
|
+
return cellDataList.get(position);
|
79
|
+
}
|
80
|
+
@Override
|
81
|
+
public long getItemId(int position) {
|
82
|
+
return position;
|
83
|
+
}
|
84
|
+
@Override
|
85
|
+
public @NonNull
|
86
|
+
View getView(int position, View convertView, @NonNull ViewGroup parent) {
|
87
|
+
if(convertView == null) {
|
88
|
+
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list, parent, false);
|
89
|
+
convertView.setTag(new ViewHolder(convertView));
|
90
|
+
}
|
91
|
+
|
92
|
+
CellData cellDataItem = cellDataList.get(position);
|
93
|
+
ViewHolder vh = (ViewHolder)convertView.getTag();
|
94
|
+
|
95
|
+
vh.viewCheck.setOnCheckedChangeListener(null); //次の setChecked で古いリスナが実行されないように、先に消す.
|
96
|
+
vh.viewCheck.setChecked(cellDataItem.check);
|
97
|
+
vh.viewCheck.setOnCheckedChangeListener((buttonView, isChecked) -> {
|
98
|
+
cellDataList.get(position).check = isChecked;
|
99
|
+
Log.i("Test", "cellDataList["+position+"]のcheckに["+isChecked+"]を入れました");
|
100
|
+
notifyDataSetChanged(); //データが変わったのでリストに再表示を要求
|
101
|
+
});
|
102
|
+
vh.viewTime.setText(dateFormat.format(cellDataItem.time));
|
103
|
+
vh.viewCount.setText("" + cellDataItem.count);
|
104
|
+
vh.viewComment.setText(cellDataItem.comment);
|
105
|
+
convertView.setBackgroundColor(cellDataItem.check ? Color.GREEN : ROW_BG_COLORS[position%2]);
|
106
|
+
|
107
|
+
return convertView;
|
108
|
+
}
|
109
|
+
|
110
|
+
// Viewを保持するクラス
|
111
|
+
private class ViewHolder {
|
112
|
+
final CheckBox viewCheck;
|
113
|
+
final TextView viewTime;
|
114
|
+
final TextView viewCount;
|
115
|
+
final TextView viewComment;
|
116
|
+
ViewHolder(View itemView) {
|
117
|
+
viewCheck = itemView.findViewById(R.id.listCheckBox);
|
118
|
+
viewTime = itemView.findViewById(R.id.listTime);
|
119
|
+
viewCount = itemView.findViewById(R.id.listCount);
|
120
|
+
viewComment = itemView.findViewById(R.id.listComment);
|
121
|
+
}
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
private class NowTimer extends TimerTask implements DefaultLifecycleObserver {
|
126
|
+
private SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
|
127
|
+
private TextView clockText;
|
128
|
+
private Handler handler;
|
129
|
+
private Timer timer;
|
130
|
+
private long period;
|
131
|
+
private Date now;
|
132
|
+
|
133
|
+
NowTimer(long period) {
|
134
|
+
this.period = period;
|
135
|
+
clockText = findViewById(R.id.clock_text);
|
136
|
+
handler = new Handler(getMainLooper());
|
137
|
+
}
|
138
|
+
|
139
|
+
Date getDate() {
|
140
|
+
return now;
|
141
|
+
}
|
142
|
+
|
143
|
+
@Override
|
144
|
+
public void run() {
|
145
|
+
now = new Date();
|
146
|
+
handler.post(() -> clockText.setText(dateFormat.format(now)));
|
147
|
+
}
|
148
|
+
@Override
|
149
|
+
public void onStart(@NonNull LifecycleOwner owner) {
|
150
|
+
DefaultLifecycleObserver.super.onStart(owner);
|
151
|
+
timer = new Timer();
|
152
|
+
timer.schedule(this, 0, period);
|
153
|
+
}
|
154
|
+
@Override
|
155
|
+
public void onStop(@NonNull LifecycleOwner owner) {
|
156
|
+
DefaultLifecycleObserver.super.onStop(owner);
|
157
|
+
timer.cancel();
|
158
|
+
timer = null;
|
159
|
+
}
|
160
|
+
}
|
161
|
+
|
162
|
+
private class Counter {
|
163
|
+
private DecimalFormat df = new DecimalFormat("#,###");
|
164
|
+
private TextView countText;
|
165
|
+
private int count;
|
166
|
+
|
167
|
+
Counter(int min, int max) {
|
168
|
+
countText = findViewById(R.id.count_text);
|
169
|
+
setCount(min);
|
170
|
+
|
171
|
+
Button plusButton = findViewById(R.id.plus_button);
|
172
|
+
plusButton.setOnClickListener(v -> setCount(Math.min(count+1, max)));
|
173
|
+
|
174
|
+
Button minusButton = findViewById(R.id.minus_button);
|
175
|
+
minusButton.setOnClickListener(v -> setCount(Math.max(count-1, min)));
|
176
|
+
}
|
177
|
+
|
178
|
+
void setCount(int count) {
|
179
|
+
this.count = count;
|
180
|
+
countText.setText(df.format(count));
|
181
|
+
}
|
182
|
+
int getCount() {
|
183
|
+
return count;
|
184
|
+
}
|
185
|
+
}
|
186
|
+
}
|
187
|
+
```
|