私のスタックは本当にオーバーフローしています。つまり、現在、私の脳は以下の問題を解決する方法を考えることができません (私は何日も試行錯誤していますが)。私がやりたいのは、( http://asset0.cbsistatic.com/cnwk.1d/i/tim/2010/11/18/Foreman_11654166_1073_com.probosoft.masscontactsdelete0_257x386.png )のような削除目的のスクリーンです。
私はこのようなリストビュー項目のレイアウトを持っています(log_view.xml):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:padding="5dp" >
<TextView
android:id="@+id/date_view"
android:gravity="top|left"
android:textStyle="bold"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true" />
<TextView
android:id="@+id/value_view"
android:gravity="top|left"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/date_view"
android:layout_alignParentLeft="true" />
<CheckBox
android:id="@+id/del_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:visibility="gone"
android:focusable="false"
android:focusableInTouchMode="false" />
</RelativeLayout>
そして、これは私のカスタム アダプターです。
class LogViewAdapter extends ArrayAdapter<Log> {
public ArrayList<Boolean> checkList;
private int layoutResourceId;
private List<Log> logList;
Context context;
public LogViewAdapter(Context context, int layoutResourceId, List<Log> data) {
super(context, R.layout.log_view, data);
this.layoutResourceId = layoutResourceId;
this.logList = data;
this.context = context;
checkList = new ArrayList<Boolean>();
for (int i = 0; i < logList.size(); i ++) {
checkList.add(false);
}
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
LogHolder logholder = null;
// Unidentified view
if(convertView == null) {
LayoutInflater inflater = ((Activity)context).getLayoutInflater();
convertView = inflater.inflate(layoutResourceId, parent, false);
// Create a new holder
logholder = new LogHolder();
logholder.del_box = (CheckBox)convertView.findViewById(R.id.del_box);
logholder.del_box.setChecked(false);
final int iFinal = position;
logholder.del_box.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
checkList.set(iFinal, buttonView.isChecked());
}
});
logholder.date_view = (TextView)convertView.findViewById(R.id.date_view);
logholder.value_view = (TextView)convertView.findViewById(R.id.value_view);
convertView.setTag(logholder);
}
// Identified view
else {
logholder = (LogHolder) convertView.getTag();
}
if (null != logList) {
if (null == checkList.get(position)) {
android.util.Log.e("getView", "Checklist null at " + position);
}
//logholder.del_box.setChecked((boolean)checkList.get(position));
logholder.date_view.setText(logList.get(position).date);
logholder.value_view.setText(logList.get(position).value);
}
android.util.Log.e("getView", " at " + position + ", value = " + logholder.value_view.getText().toString());
return convertView;
}
static class LogHolder {
TextView date_view;
TextView value_view;
CheckBox del_box;
}
}
これが私のアクティビティです:
public class LoggingActivity extends Activity {
//private EditText edt_name;
//private EditText edt_value;
private List<Log> logList;
private LogViewAdapter lw_adapter;
private ListView listView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.log_list);
//setContentView(android.R.layout.simple_list_item_multiple_choice);
logList = loadLog();
listView = (ListView)findViewById(R.id.log_list);
lw_adapter = new LogViewAdapter(this, R.layout.log_view, logList);
//lw_adapter = new LogViewAdapter(this, android.R.layout.simple_list_item_multiple_choice, logList);
listView.setAdapter(lw_adapter);
}
/** implements for the MENU soft-key. Android 2.3.x */
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.layout.log_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// IMPORTANT: listView.getChildAt(index) will only return the
// VISIBLE views in our current screen
if (null == listView) {
android.util.Log.e("onOptionsItemSelected", "list view null");
return true;
}
for (int i = 0; i < listView.getChildCount(); i++) {
if (null == listView.getChildAt(i)) continue;
CheckBox cb = (CheckBox) listView.getChildAt(i).findViewById(R.id.del_box);
if (null == cb) {
android.util.Log.e("CB", "CheckBox at i = " + i + " null");
return true;
}
final int iFinal = i + listView.getFirstVisiblePosition();
cb.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
listView.setItemChecked(iFinal, buttonView.isChecked());
lw_adapter.checkList.set(iFinal, buttonView.isChecked());
}
});
SparseBooleanArray selectedItems = listView.getCheckedItemPositions();
for (int j = 0; j < selectedItems.size(); j++) {
if (selectedItems.get(j)) {
android.util.Log.e("CHECKED", "Row "+ j + ", value = " + logList.get(j).value);
}
else {
android.util.Log.e("CHECKED", "Row "+ j + " not checked");
}
}
cb.setVisibility(View.VISIBLE);
cb.setEnabled(true);
}
File folder = new File(Environment.getExternalStorageDirectory(), "50802566/logs");
// Handle item selection
switch (item.getItemId()) {
case R.id.log_sort:
Collections.sort(logList, Log.COMPARE_BY_VALUE);
lw_adapter.notifyDataSetChanged();
return true;
case R.id.log_del:
for (int i = 0; i < logList.size(); i++) {
if (null == lw_adapter.checkList.get(i)) {
android.util.Log.e("DEL", "checklist["+ i + "] = null");
continue;
}
if (true == lw_adapter.checkList.get(i)) {
// Delete correlative xml file
Date date = null;
try {
date = new SimpleDateFormat("dd/MM/yyyy - HH:mm:ss").parse(logList.get(i).date);
} catch (ParseException e) {
e.printStackTrace();
}
String filename = new SimpleDateFormat("dd_MM_yyyy - HH_mm_ss").format(date) + ".xml";
File toDelete = new File(folder, filename);
if (!toDelete.delete()) {
android.util.Log.e("DEL", "File " + filename + ": not deleted or not existed");
}
// Update the UI
//lw_adapter.remove(logList.get(i));
//lw_adapter.checkList.remove(i);
logList.set(i, null);
lw_adapter.checkList.set(i, null);
android.util.Log.e("DEL", "Deleted " + i + ", size now = " + logList.size());
}
}
for (int i = 0; i < logList.size(); i ++) {
if (null == logList.get(i)) {
logList.remove(i);
lw_adapter.checkList.remove(i);
}
}
lw_adapter.notifyDataSetChanged();
// Refresh the checklist
for (int i = 0; i < lw_adapter.checkList.size(); i ++) {
lw_adapter.checkList.set(i, false);
}
for (int i = 0; i < listView.getChildCount(); i++) {
if (null == listView.getChildAt(i)) continue;
CheckBox cb = (CheckBox) listView.getChildAt(i).findViewById(R.id.del_box);
cb.setChecked(false);
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private ArrayList<Log> loadLog() {
/** Access folder "my_logs" in external memory */
File baseDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
File.separator +
"50802566/logs");
if (!baseDir.exists()) { baseDir.mkdirs(); }
File[] list_file = baseDir.listFiles();
/** For internal memory */
//File[] list_file = this.getFilesDir().listFiles();
for (int i = 0; i < list_file.length; i ++) {
//android.util.Log.e("PRINTLN", list_file[i].getPath());
}
int l = list_file.length;
ArrayList<Log> list = new ArrayList<Log>();
while (l-- > 0) {
File file = list_file[l];
FileInputStream fin = null;
LogReader logr = new LogReader();
try {
fin = new FileInputStream(file);
list.add(list_file.length - l - 1, logr.read(fin));
fin.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
return list;
}
}
問題は次のとおりです。
各 CheckBox のブール値を追跡できません。たとえば、logList (データベース) を並べ替えてから "notifyDataSetChanged" を listView に並べ替えますが、checkList (CheckBox のブール値を含む) は同じままで、奇妙なことが起こります。あるリストのすべてのソート操作を別のリストに正確に適用するにはどうすればよいですか? (IMO、「同期ソート」またはそのようなもの...)
ご覧のとおり、行から del_box (私の CheckBox id) を取得して #1 を解決しようとしました。次に、「OnCheckedChangeListener」を設定して割り当てを行います (チェックすると、ブール値リストの相対値が更新されます)チェックリスト」)。しかし、奇妙なことが再び起こります。
- 行をチェックして下にスクロールすると、別の行が(自動的に)チェックされていることがわかります。
- スクロールバックすると、前にチェックされたものは...チェックが外されます(もちろん、自動的に)。
アダプターが間違ったビュー/値を再利用している可能性がありますが、それを正しく行う方法がわかりません... >"<
あなたの助けとガイドは素晴らしいです。あなたのサンプルコードは私にとって完璧以上のものになるでしょう... (私は今本当に疲れ果てています >"<)
アップデート:
私はそれを(何とか)解決する方法を見つけましたが、おそらくそれはAndroidのAPI動作に反しています...より安全な解決策があれば教えてください^^
2 番目のコメントで述べたように、コードは次のとおりです。
@Override
public void onCheckedChanged(CompoundButton cb, boolean isChecked) {
int graphicalPos = -1;
// This is top view on the screen for sure
if (((View)cb.getParent()).getTop() < 0) {
graphicalPos = 0;
}
else if (((View)cb.getParent()).getTop() == 0) {
graphicalPos = (int) (((View)cb.getParent()).getTop() / ((View)cb.getParent()).getHeight());
}
else if (((View)cb.getParent()).getTop() > 0) {
graphicalPos = (int) (((View)cb.getParent()).getTop() / ((View)cb.getParent()).getHeight());
if (listView.getChildAt(0).getTop() < 0) {
graphicalPos = graphicalPos + 1;
}
else {
android.util.Log.e("NOT PLUS", "First Top = " + listView.getChildAt(0).getTop());
}
}
int dataPos = graphicalPos + listView.getFirstVisiblePosition();
lw_adapter.checkList.set(dataPos, isChecked);
android.util.Log.e("onCheckedChanged", "Checked row " + dataPos + ", graphicalPos = " + graphicalPos + ", top = " + ((View)cb.getParent()).getTop() + ", firstVisible = " + listView.getFirstVisiblePosition());
}