/* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * 按照许可要求使用此文件,否则不允许使用。 * 可以通过以下网址获取许可证副本: * http://www.apache.org/licenses/LICENSE-2.0 * * 除非适用法律要求或书面同意,软件依据许可证分发是“按现状”分发, * 不附带任何明示或暗示的保证或条件。请查看许可证了解具体权限和限制。 */ // 所在包声明,表明该类属于笔记应用(net.micode.notes)的用户界面(ui)相关模块。 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)的适配器类, // 负责处理笔记数据展示、管理笔记项的选中状态等相关操作逻辑,是笔记列表界面展示与数据交互的重要组成部分。 public class NotesListAdapter extends CursorAdapter { // 定义一个静态的字符串常量,作为日志输出时的标签,方便在查看日志时快速识别与该适配器相关的日志信息,利于调试与问题排查。 private static final String TAG = "NotesListAdapter"; // 用于保存上下文对象,通过构造函数传入,在整个适配器的生命周期内提供与应用环境相关的资源访问、视图创建等操作所需的上下文环境。 private Context mContext; // 使用HashMap来存储笔记项在列表中的选中状态,键(Integer类型)为笔记项在列表中的位置索引,值(Boolean类型)表示对应位置的笔记项是否被选中, // 以此来跟踪用户在笔记列表中的多选操作情况,方便后续根据选中状态进行相应的数据处理和界面更新。 private HashMap mSelectedIndex; // 记录笔记的数量(仅统计实际的笔记类型数据项数量,不包含文件夹等其他非笔记类型的数据), // 该数量会在数据内容变化(如游标更新、数据重新加载)时通过重新计算得到,用于辅助判断如全选等相关业务逻辑。 private int mNotesCount; // 用于标识当前适配器是否处于多选模式的布尔变量,通过设置该变量来控制适配器在不同模式下的数据处理、视图显示等行为逻辑, // 例如在多选模式下需正确显示已选笔记项的选中状态、提供相应的批量操作菜单等。 private boolean mChoiceMode; // 内部静态类,用于封装与应用小部件(Widget)相关的属性信息,主要包含小部件的唯一标识ID和小部件的类型信息, // 在涉及笔记与小部件关联的相关操作中,用于传递和处理小部件的相关属性数据。 public static class AppWidgetAttribute { public int widgetId; public int widgetType; }; // 构造方法,接收一个Context上下文对象,调用父类(CursorAdapter)的构造方法并传入上下文以及初始化为null的游标对象, // 同时初始化用于记录选中状态的HashMap,保存传入的上下文对象,并将笔记数量初始化为0,完成适配器的初始化设置。 public NotesListAdapter(Context context) { super(context, null); mSelectedIndex = new HashMap(); mContext = context; mNotesCount = 0; } // 重写CursorAdapter的抽象方法,用于创建新的视图对象,该方法在需要为新的笔记项创建视图进行展示时被调用, // 此处简单地返回一个NotesListItem类型的视图实例,通常NotesListItem是自定义的视图类,用于具体实现笔记内容在界面上的展示布局等细节逻辑。 @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { return new NotesListItem(context); } // 重写CursorAdapter的抽象方法,负责将数据绑定到已创建的视图上,使其能够正确展示笔记信息, // 首先判断传入的视图是否是NotesListItem类型,如果是,则从游标中解析出笔记数据并封装成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())); } } // 用于设置指定位置笔记项的选中状态的公有方法,接收笔记项在列表中的位置索引(int类型)和要设置的选中状态(布尔值)作为参数, // 将对应的位置和选中状态存入mSelectedIndex这个HashMap中,之后调用notifyDataSetChanged方法通知适配器数据集已发生变化, // 触发视图的刷新操作,使得界面上该笔记项的选中状态显示能够根据新设置的状态进行更新,例如改变选中项的背景颜色等视觉效果。 public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); notifyDataSetChanged(); } // 用于判断当前适配器是否处于多选模式的公有方法,直接返回表示多选模式状态的成员变量mChoiceMode的值, // 外部代码可以通过调用该方法来了解适配器当前所处的交互模式状态,进而执行与之对应的不同操作逻辑,例如在多选模式下显示特定的操作菜单等。 public boolean isInChoiceMode() { return mChoiceMode; } // 用于设置适配器的多选模式状态的公有方法,接收一个布尔值参数,当进入多选模式时(传入true), // 先清空之前记录的所有笔记项选中状态(通过调用mSelectedIndex的clear方法),然后将mChoiceMode变量设置为传入的模式值, // 此后适配器会根据新的模式状态调整相关的数据处理和视图交互逻辑,例如更新界面上笔记项的选中显示效果等。 public void setChoiceMode(boolean mode) { mSelectedIndex.clear(); mChoiceMode = mode; } // 用于实现全选或全不选所有笔记项的公有方法,接收一个布尔值参数,根据该参数决定是将所有笔记项设置为选中还是取消选中状态, // 首先获取当前适配器关联的游标对象(通过调用getCursor方法),然后遍历游标中的所有笔记项(通过getCount方法获取笔记项总数,并移动游标到每个位置), // 对于每个位置的笔记项,通过NoteItemData.getNoteType方法判断其类型是否为笔记(与Notes.TYPE_NOTE进行比较),如果是笔记类型,则调用setCheckedItem方法设置其选中状态。 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集合的公有方法,创建一个HashSet类型的集合用于存储笔记项的ID, // 遍历mSelectedIndex这个记录选中状态的HashMap,对于值为true(即被选中)的项,通过调用getItemId方法获取其对应的笔记项ID, // 接着进行合法性判断,如果获取到的ID等于Notes.ID_ROOT_FOLDER(通常根文件夹ID不应作为笔记项被选中,此处作为一种异常情况的判断), // 则在日志中输出相应提示信息(使用Log.d方法输出调试级别的日志),否则将合法的ID添加到HashSet集合中,最后返回该包含所有选中笔记项ID的集合, // 方便外部代码基于这些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; } // 用于获取所有被选中笔记项关联的小部件属性集合的公有方法,创建一个HashSet类型的集合用于存储小部件相关属性对象, // 遍历mSelectedIndex这个记录选中状态的HashMap,对于值为true(即被选中)的项,通过调用getItem方法获取对应的游标对象(代表选中的笔记项数据), // 如果游标不为空,则创建一个AppWidgetAttribute对象,从游标数据中解析出小部件的ID和类型信息(通过NoteItemData类的相关方法获取)并赋值给该对象, // 然后将其添加到HashSet集合中,最后返回包含所有选中笔记项关联小部件属性的集合,该集合可用于后续与小部件相关的批量更新等操作, // 注意按照代码中的注释说明,此处不会关闭游标,游标关闭操作由适配器统一管理,以确保数据访问的正确性和一致性。 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中所有值的集合(即所有笔记项的选中状态值集合,Collection类型), // 如果该集合为空(表示没有任何笔记项设置了选中状态),则直接返回0;否则通过迭代器(Iterator)遍历该集合, // 统计值为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; } // 用于判断是否所有笔记项都被选中的公有方法,首先通过调用getSelectedCount方法获取当前被选中笔记项的数量, // 然后判断如果选中数量不为0(即至少有一个笔记项被选中)且选中数量等于笔记的总数量(通过比较与mNotesCount的值),则返回true,表示处于全选状态,否则返回false, // 可用于在界面上显示全选相关的操作提示或进行与全选状态相关的业务逻辑处理等情况。 public boolean isAllSelected() { int checkedCount = getSelectedCount(); return (checkedCount!= 0 && checkedCount == mNotesCount); } // 用于判断指定位置的笔记项是否被选中的公有方法,通过获取mSelectedIndex中对应位置的选中状态值来判断, // 如果该位置的值为null(可能是该位置的笔记项还未设置过选中状态),则返回false,表示未被选中;否则返回对应位置记录的布尔值(即是否被选中的状态), // 方便外部代码在处理具体的笔记项交互逻辑(如点击某个笔记项时)判断其当前的选中情况,进而执行相应的操作逻辑。 public boolean isSelectedItem(final int position) { if (null == mSelectedIndex.get(position)) { return false; } return mSelectedIndex.get(position); } // 重写父类(CursorAdapter)的方法,当数据集的内容发生变化时(例如数据库中的笔记数据有更新、插入或删除等操作导致游标所关联的数据改变)会被调用, // 在此方法中,先调用父类的onContentChanged方法执行默认的内容改变处理逻辑,然后调用calcNotesCount私有方法重新计算笔记的总数量, // 以确保后续基于笔记数量的相关业务逻辑(如全选判断等)能够基于最新的数据情况进行正确操作。 @Override protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } // 重写父类(CursorAdapter)的方法,用于更改适配器所使用的游标对象(通常意味着数据来源发生了变化,比如重新从数据库查询获取了新的笔记数据), // 先调用父类的changeCursor方法完成游标切换的默认操作,然后调用calcNotesCount私有方法重新计算笔记的总数量, // 使得适配器能够基于新的游标数据更新界面展示,并保证相关数据统计逻辑的准确性,确保后续各种基于笔记数据的操作和显示都是基于最新的有效数据。 @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } // 私有方法,用于计算笔记的总数量,首先将mNotesCount初始化为0,然后遍历游标中的所有笔记项(通过getCount方法获取笔记项总数,并移动游标到每个位置), // 对于每个笔记项,通过NoteItemData.getNoteType方法判断其类型是否为笔记(与Notes.TYPE_NOTE进行比较),如果是笔记类型,则将笔记总数量加1, // 在遍历过程中,如果遇到游标为无效的情况(即游标为null),则通过Log.e方法输出错误日志信息,并直接返回,不再继续计算笔记数量, // 以此保证数量统计的准确性是基于有效的游标数据进行的,避免因无效游标导致的错误计算或异常情况。 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; } } } }