/* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.micode.notes.ui; import android.content.Context; import android.database.Cursor; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; import net.micode.notes.data.Notes; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; public class NotesListAdapter extends CursorAdapter { private static final String TAG = "NotesListAdapter"; private Context mContext; private HashMap mSelectedIndex; private int mNotesCount; private boolean mChoiceMode; public static class AppWidgetAttribute { public int widgetId; public int widgetType; }; public NotesListAdapter(Context context) { super(context, null); mSelectedIndex = new HashMap(); mContext = context; mNotesCount = 0; } @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new NotesListItem(context); } @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof NotesListItem) { NoteItemData itemData = new NoteItemData(context, cursor); ((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition())); } } public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); notifyDataSetChanged(); } public boolean isInChoiceMode() { return mChoiceMode; } public void setChoiceMode(boolean mode) { mSelectedIndex.clear(); mChoiceMode = mode; } public void selectAll(boolean checked) { Cursor cursor = getCursor(); for (int i = 0; i < getCount(); i++) { if (cursor.moveToPosition(i)) { if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { setCheckedItem(i, checked); } } } } public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { if (mSelectedIndex.get(position) == true) { Long id = getItemId(position); if (id == Notes.ID_ROOT_FOLDER) { Log.d(TAG, "Wrong item id, should not happen"); } else { itemSet.add(id); } } } return itemSet; } public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { if (mSelectedIndex.get(position) == true) { Cursor c = (Cursor) getItem(position); if (c != null) { AppWidgetAttribute widget = new AppWidgetAttribute(); NoteItemData item = new NoteItemData(mContext, c); widget.widgetId = item.getWidgetId(); widget.widgetType = item.getWidgetType(); itemSet.add(widget); /** * Don't close cursor here, only the adapter could close it */ } else { Log.e(TAG, "Invalid cursor"); return null; } } } return itemSet; } public int getSelectedCount() { Collection values = mSelectedIndex.values(); if (null == values) { return 0; } Iterator iter = values.iterator(); int count = 0; while (iter.hasNext()) { if (true == iter.next()) { count++; } } return count; } public boolean isAllSelected() { int checkedCount = getSelectedCount(); return (checkedCount != 0 && checkedCount == mNotesCount); } public boolean isSelectedItem(final int position) { if (null == mSelectedIndex.get(position)) { return false; } return mSelectedIndex.get(position); } @Override protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } private void calcNotesCount() { mNotesCount = 0; for (int i = 0; i < getCount(); i++) { Cursor c = (Cursor) getItem(i); if (c != null) { if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { mNotesCount++; } } else { Log.e(TAG, "Invalid cursor"); return; } } } } package net.micode.notes.ui; import android.content.Context; import android.database.Cursor; import android.util.Log; import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; import net.micode.notes.data.Notes; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; // NotesListAdapter类继承自CursorAdapter,是一个用于适配便签列表数据到ListView等列表视图组件的适配器类, // 它负责将从数据库查询获取的Cursor数据转换为适合展示的视图项,并处理了列表项的选择、计数以及与小部件相关属性获取等功能逻辑。 public class NotesListAdapter extends CursorAdapter { // 用于日志记录的标签,方便在日志输出中识别该类相关的日志信息。 private static final String TAG = "NotesListAdapter"; // 用于存储上下文信息,方便后续在创建视图、获取资源等操作中使用。 private Context mContext; // 使用HashMap来存储列表项的选中状态,键为列表项的位置索引,值为布尔类型表示是否被选中,以此记录用户在多选模式下对各列表项的选择情况。 private HashMap mSelectedIndex; // 用于记录便签的数量,通过遍历游标数据统计得出,便于后续判断全选等相关逻辑时使用。 private int mNotesCount; // 用于标记当前列表是否处于多选模式,控制相关的界面交互和数据处理逻辑,例如是否显示多选菜单、是否响应多选相关操作等。 private boolean mChoiceMode; // 内部静态类,用于封装与便签关联的小部件的属性信息,包含小部件的ID和类型两个成员变量,方便在处理便签与小部件关联操作时传递和使用相关数据。 public static class AppWidgetAttribute { public int widgetId; public int widgetType; }; // 构造函数,接收上下文对象作为参数,调用父类构造函数传入上下文和空游标初始化,同时初始化选中状态的HashMap和记录便签数量的变量,并保存传入的上下文信息。 public NotesListAdapter(Context context) { super(context, null); mSelectedIndex = new HashMap(); mContext = context; mNotesCount = 0; } // 重写父类的方法,用于创建一个新的视图对象来展示列表项,在这里返回一个自定义的`NotesListItem`视图对象,该对象负责具体的便签数据展示布局和渲染。 @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new NotesListItem(context); } // 重写父类的方法,用于将数据绑定到已创建的视图对象上进行展示,在这里从游标中获取便签数据封装成`NoteItemData`对象, // 然后调用`NotesListItem`的`bind`方法将数据、多选模式状态以及该项是否被选中的信息传递进去,完成数据与视图的绑定展示。 @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof NotesListItem) { NoteItemData itemData = new NoteItemData(context, cursor); ((NotesListItem) view).bind(context, itemData, mChoiceMode, isSelectedItem(cursor.getPosition())); } } // 用于设置指定位置的列表项的选中状态,将位置索引和对应的选中状态存入`mSelectedIndex` HashMap中,并通知数据集已改变,使得列表视图能够更新相应的显示状态。 public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); notifyDataSetChanged(); } // 用于判断当前列表是否处于多选模式,外部代码可以调用该方法获取相应的状态信息,以决定是否显示多选相关的界面元素或执行相应逻辑。 public boolean isInChoiceMode() { return mChoiceMode; } // 用于设置列表的多选模式状态,当进入或退出多选模式时调用该方法,会先清空之前的选中状态记录,然后更新`mChoiceMode`变量,以切换相应的操作逻辑和界面显示。 public void setChoiceMode(boolean mode) { mSelectedIndex.clear(); mChoiceMode = mode; } // 用于全选或取消全选所有符合条件(通常是便签类型为普通便签)的列表项,通过遍历游标中的所有记录,根据便签类型判断是否进行选中状态设置,实现批量选中或取消选中的功能。 public void selectAll(boolean checked) { Cursor cursor = getCursor(); for (int i = 0; i < getCount(); i++) { if (cursor.moveToPosition(i)) { if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { setCheckedItem(i, checked); } } } } // 用于获取所有已选中列表项的ID集合,遍历`mSelectedIndex`中所有被标记为选中的位置索引,通过`getItemId`方法获取对应项的ID, // 并添加到`HashSet`集合中返回,同时对特殊的根文件夹ID情况进行日志记录(因为可能不符合预期的选中逻辑)。 public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { if (mSelectedIndex.get(position) == true) { Long id = getItemId(position); if (id == Notes.ID_ROOT_FOLDER) { Log.d(TAG, "Wrong item id, should not happen"); } else { itemSet.add(id); } } } return itemSet; } // 用于获取所有已选中列表项关联的小部件属性集合,遍历`mSelectedIndex`中所有被标记为选中的位置索引,通过`getItem`方法获取对应位置的游标数据, // 进而创建`AppWidgetAttribute`对象并填充小部件的ID和类型信息,添加到`HashSet`集合中返回,同时对游标无效的情况进行日志记录并返回`null`。 public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { if (mSelectedIndex.get(position) == true) { Cursor c = (Cursor) getItem(position); if (c!= null) { AppWidgetAttribute widget = new AppWidgetAttribute(); NoteItemData item = new NoteItemData(mContext, c); widget.widgetId = item.getWidgetId(); widget.widgetType = item.getWidgetType(); itemSet.add(widget); /** * Don't close cursor here, only the adapter could close it */ } else { Log.e(TAG, "Invalid cursor"); return null; } } } return itemSet; } // 用于获取当前已选中列表项的数量,通过获取`mSelectedIndex`中所有值(即各位置的选中状态)的集合,然后遍历该集合统计值为`true`(表示选中)的个数并返回。 public int getSelectedCount() { Collection values = mSelectedIndex.values(); if (null == values) { return 0; } Iterator iter = values.iterator(); int count = 0; while (iter.hasNext()) { if (true == iter.next()) { count++; } } return count; } // 用于判断是否所有符合条件(通常是便签类型相关限定)的列表项都已被选中,通过比较已选中的数量和总的便签数量来确定,若已选中数量不为0且等于便签数量则返回`true`,表示全选状态。 public boolean isAllSelected() { int checkedCount = getSelectedCount(); return (checkedCount!= 0 && checkedCount == mNotesCount); } // 用于判断指定位置的列表项是否被选中,先判断对应位置在`mSelectedIndex`中的值是否为`null`(未记录则视为未选中), // 若不为`null`则返回该位置记录的选中状态(`true`表示选中,`false`表示未选中)。 public boolean isSelectedItem(final int position) { if (null == mSelectedIndex.get(position)) { return false; } return mSelectedIndex.get(position); } // 重写父类的方法,当数据集内容发生变化时会被调用,在这里调用`calcNotesCount`方法重新计算便签的数量,以保证相关统计数据的准确性。 @Override protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } // 重写父类的方法,当游标数据发生改变(比如重新查询获取了新的数据)时会被调用,同样在这里调用`calcNotesCount`方法重新计算便签的数量,以适配新的数据情况。 @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } // 私有方法,用于计算便签的数量,通过遍历游标中的所有记录,根据记录对应的便签类型判断是否为普通便签,若是则数量加1,最终得到总的便签数量并更新`mNotesCount`变量。 private void calcNotesCount() { mNotesCount = 0; for (int i = 0; i < getCount(); i++) { Cursor c = (Cursor) getItem(i); if (c!= null) { if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { mNotesCount++; } } else { Log.e(TAG, "Invalid cursor"); return; } } } }