ListView 未使用搜索视图更新发布结果
ListView not updated on publish results using searchview
我一直在编写一个 android 应用程序,它使用 SearchView 搜索我的数据库,当我在结果数组列表上执行 Log.e() 时,我能够搜索笔记,然后我查看正确的结果,但应该更改以显示搜索结果的列表视图不会受到影响。我正在使用自定义适配器。我只是不明白 publishResults
方法的结果究竟是如何实时修改我的 listview
MainActivity.java
public class MainActivity extends RoboActionBarActivity {
private static final int NEW_NOTE_RESULT_CODE = 4;
private static final int EDIT_NOTE_RESULT_CODE = 5;
@InjectView(android.R.id.empty) private TextView emptyListTextView;
@InjectView(android.R.id.list) private ListView listView;
@InjectView(R.id.add_note_button) private FloatingActionButton addNoteButton;
@Inject private NoteDAO noteDAO;
private ArrayList<Integer> selectedPositions;
private ArrayList<NotesAdapter.NoteViewWrapper> notesData;
private NotesAdapter listAdapter;
private ActionMode.Callback actionModeCallback;
private ActionMode actionMode;
/** {@inheritDoc} */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inicializa los componentes //////////////////////////////////////////////////////////////
listView.setOnTouchListener(new ShowHideOnScroll(addNoteButton, getSupportActionBar())); // Esconde o muesta el FAB y la Action Bar
addNoteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Crear una nota nueva
startActivityForResult(EditNoteActivity.buildIntent(MainActivity.this), NEW_NOTE_RESULT_CODE);
}
});
selectedPositions = new ArrayList<>();
listView.setTextFilterEnabled(true);
setupNotesAdapter();
setupActionModeCallback();
setListOnItemClickListenersWhenNoActionMode();
updateView();
}
/** {@inheritDoc} */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
// Assumes current activity is the searchable activity
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(true); // Do not iconify the widget; expand it by default
SearchView.OnQueryTextListener textChangeListener = new SearchView.OnQueryTextListener()
{
@Override
public boolean onQueryTextChange(String query)
{
listAdapter.getFilter().filter(query);
return true;
}
@Override
public boolean onQueryTextSubmit(String query)
{
// this is your adapter that will be filtered
return false;
}
};
searchView.setOnQueryTextListener(textChangeListener);
return true;
}
/** {@inheritDoc} */
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_about_info:
new AboutNoticeDialog()
.show(getSupportFragmentManager(), "dialog_about_notice");
return true;
case R.id.action_import:
importNotes();
return true;
case R.id.action_export:
exportNotes();
return true;
default: return super.onOptionsItemSelected(item);
}
}
private void exportNotes() {
for (Note note : noteDAO.fetchAll()) {
StringBuilder exportNotes = new StringBuilder();
}
}
private void importNotes() {
}
/** {@inheritDoc} */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == NEW_NOTE_RESULT_CODE) {
if (resultCode == RESULT_OK) addNote(data);
}
if (requestCode == EDIT_NOTE_RESULT_CODE) {
if (resultCode == RESULT_OK) updateNote(data);
}
super.onActivityResult(requestCode, resultCode, data);
}
/** Crea la llamada al modo contextual. */
private void setupActionModeCallback() {
actionModeCallback = new ActionMode.Callback() {
/** {@inheritDoc} */
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
setListOnItemClickListenersWhenActionMode();
// inflar menu contextual
mode.getMenuInflater().inflate(R.menu.context_note, menu);
return true;
}
/** {@inheritDoc} */
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Nada
return false;
}
/** {@inheritDoc} */
@Override
public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
// borrar notas solo si hay notas a borrar; sino se acaba el modo contextual.
case R.id.action_delete:
if (!selectedPositions.isEmpty()) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(getString(R.string.delete_notes_alert, selectedPositions.size()))
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteNotes(selectedPositions);
mode.finish();
}
})
.show();
} else mode.finish();
return true;
case R.id.action_share:
if (!selectedPositions.isEmpty()){
shareNotes(selectedPositions);
}
return true;
case R.id.action_select_all:
selectAll();
default:
return false;
}
}
/** {@inheritDoc} */
@Override
public void onDestroyActionMode(ActionMode mode) {
// Regresar al modo normal
setListOnItemClickListenersWhenNoActionMode();
resetSelectedListItems();
}
};
}
private void selectAll() {
for ( int i=0; i < listAdapter.getCount(); i++) {
selectedPositions.add(i);
notesData.get(i).setSelected(true);
listAdapter.notifyDataSetChanged();
}
}
/** Inicializa el adaptador de notas. */
private void setupNotesAdapter() {
notesData = new ArrayList<>();
for (Note note : noteDAO.fetchAll()) { // Convertir a wrapper
NotesAdapter.NoteViewWrapper noteViewWrapper = new NotesAdapter.NoteViewWrapper(note);
notesData.add(noteViewWrapper);
}
listAdapter = new NotesAdapter(notesData);
listView.setAdapter(listAdapter);
}
/** Actualiza la vista de esta actividad cuando hay notas o no hay notas. */
private void updateView() {
if (notesData.isEmpty()) { // Mostrar mensaje
listView.setVisibility(View.GONE);
emptyListTextView.setVisibility(View.VISIBLE);
} else { // Mostrar lista
listView.setVisibility(View.VISIBLE);
emptyListTextView.setVisibility(View.GONE);
}
}
/**
* Agrega una nota a lista y la fuente de datos.
*
* @param data los datos de la actividad de edición de notas.
*/
private void addNote(Intent data) {
Note note = EditNoteActivity.getExtraNote(data);
noteDAO.insert(note);
NotesAdapter.NoteViewWrapper noteViewWrapper = new NotesAdapter.NoteViewWrapper(note);
notesData.add(0,noteViewWrapper);
updateView();
listAdapter.notifyDataSetChanged();
}
/*
this method is called when the contextual action button of share is pressed
*/
private void shareNotes(ArrayList<Integer> selectedPositions) {
StringBuilder shareBody = new StringBuilder();
// Primero borra de la base de datos
for (int position : selectedPositions) {
NotesAdapter.NoteViewWrapper noteViewWrapper = notesData.get(position);
shareBody.append(noteViewWrapper.getNote().getContent());
}
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody.toString());
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.share_using)));
}
/**
* Borra notas de la lista y de la fuente de datos.
*
* @param selectedPositions las posiciones de las notas en la lista.
*/
private void deleteNotes(ArrayList<Integer> selectedPositions) {
ArrayList<NotesAdapter.NoteViewWrapper> toRemoveList = new ArrayList<>(selectedPositions.size());
// Primero borra de la base de datos
for (int position : selectedPositions) {
NotesAdapter.NoteViewWrapper noteViewWrapper = notesData.get(position);
toRemoveList.add(noteViewWrapper);
noteDAO.delete(noteViewWrapper.getNote());
}
// Y luego de la vista (no al mismo tiempo porque pierdo las posiciones que hay que borrar)
for (NotesAdapter.NoteViewWrapper noteToRemove : toRemoveList) notesData.remove(noteToRemove);
updateView();
listAdapter.notifyDataSetChanged();
}
/**
* Actualiza una nota en la lista y la fuente de datos.
*
* @param data los datos de la actividad de edición de notas.
*/
private void updateNote(Intent data) {
Note updatedNote = EditNoteActivity.getExtraUpdatedNote(data);
noteDAO.update(updatedNote);
for (NotesAdapter.NoteViewWrapper noteViewWrapper : notesData) {
// Buscar la nota vieja para actulizarla en la vista
if (noteViewWrapper.getNote().getId().equals(updatedNote.getId())) {
noteViewWrapper.getNote().setContent(updatedNote.getContent());
noteViewWrapper.getNote().setUpdatedAt(updatedNote.getUpdatedAt());
}
}
listAdapter.notifyDataSetChanged();
}
/** Reinicia las notas seleccionadas a no seleccionadas y limpia la lista de seleccionados. */
private void resetSelectedListItems() {
for (NotesAdapter.NoteViewWrapper noteViewWrapper : notesData) noteViewWrapper.setSelected(false);
selectedPositions.clear();
listAdapter.notifyDataSetChanged();
}
/**
* Inicializa las acciones de la lista al hacer click en sus items cuando NO esta activo el
* modo contextual.
*/
private void setListOnItemClickListenersWhenNoActionMode() {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Ver la nota al hacer click
startActivityForResult(ViewNoteActivity.buildIntent(MainActivity.this, notesData.get(position).getNote()), EDIT_NOTE_RESULT_CODE);
}
});
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// Iniciar modo contextual para selección de items
notesData.get(position).setSelected(true);
listAdapter.notifyDataSetChanged();
selectedPositions.add(position);
actionMode = startSupportActionMode(actionModeCallback);
return true;
}
});
}
/**
* Inicializa las acciones de la lista al hacer click en sus items cuando esta activo el menu
* contextual.
*/
private void setListOnItemClickListenersWhenActionMode() {
listView.setOnItemLongClickListener(null);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Agregar items a la lista de seleccionados y cambiarles el fondo.
// Si se deseleccionan todos los items, se acaba el modo contextual
if (selectedPositions.contains(position)) {
selectedPositions.remove((Object)position); // no quiero el índice sino el objeto
if (selectedPositions.isEmpty()) actionMode.finish();
else {
notesData.get(position).setSelected(false);
listAdapter.notifyDataSetChanged();
}
} else {
notesData.get(position).setSelected(true);
listAdapter.notifyDataSetChanged();
selectedPositions.add(position);
}
}
});
}
}
NotesAdapter.java
public class NotesAdapter extends BaseAdapter implements Filterable{
private static final DateFormat DATETIME_FORMAT = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
private final List<NoteViewWrapper> data;
private List<NoteViewWrapper> filtered_data;
/** Wrapper para notas. Util para cambiar el fondo de los item seleccionados. */
public static class NoteViewWrapper {
private final Note note;
private boolean isSelected;
/**
* Contruye un nuevo NoteWrapper con la nota dada.
*
* @param note una nota.
*/
public NoteViewWrapper(Note note) {
this.note = note;
}
public Note getNote() {
return note;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
}
}
/**
* Constructor.
*
* @param data la lista de notas a usar como fuente de datos para este adaptador.
*/
public NotesAdapter(List<NoteViewWrapper> data) {
this.data = data;
this.filtered_data = data;
}
/** @return cuantos datos hay en la lista de notas. */
@Override
public int getCount() {
return data.size();
}
/**
* @param position la posición de la nota que se quiere
* @return la nota en la posición dada.
*/
@Override
public NoteViewWrapper getItem(int position) {
return data.get(position);
}
/**
* @param position una posición
* @return la misma posición dada
*/
@Override
public long getItemId(int position) {
return position;
}
/**
* Muestra los datos de la nota en la posición dada en una instancia del componente visual
* {@link com.rants.R.layout#notes_row}.
*
* @see <a href="http://bit.ly/MJqzXb">Hold View Objects in a View Holder</a>
* @param position la posición de la nota en curso.
* @param convertView el componente visual a usar.
* @param parent el componente visual padre del componente visual a usar.
* @return la vista con los datos.
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) { // inflar componente visual
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.notes_row, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else holder = (ViewHolder) convertView.getTag(); // ya existe, solo es reciclarlo
// Inicializa la vista con los datos de la nota
NoteViewWrapper noteViewWrapper = data.get(position);
holder.noteIdText.setText(String.valueOf(noteViewWrapper.note.getId()));
// Corta la cadena a 80 caracteres y le agrega "..."
SpannableString hashText = new SpannableString(noteViewWrapper.note.getContent());
Matcher matcher = Pattern.compile("#([A-Za-z0-9_-]+)").matcher(hashText);
while (matcher.find())
{
hashText.setSpan(new ForegroundColorSpan(Color.BLUE), matcher.start(), matcher.end(), 0);
}
holder.noteContentText.setText(hashText);
holder.noteDateText.setText(DATETIME_FORMAT.format(noteViewWrapper.note.getUpdatedAt()));
// Cambia el color del fondo si es seleccionado
if (noteViewWrapper.isSelected) holder.parent.setBackgroundColor(parent.getContext().getResources().getColor(R.color.selected_note));
// Sino lo regresa a transparente
else holder.parent.setBackgroundColor(parent.getContext().getResources().getColor(android.R.color.transparent));
return convertView;
}
/** Almacena componentes visuales para acceso rápido sin necesidad de buscarlos muy seguido.*/
private static class ViewHolder {
private TextView noteIdText;
private TextView noteContentText;
private TextView noteDateText;
private View parent;
/**
* Constructor. Encuentra todas los componentes visuales en el componente padre dado.
*
* @param parent un componente visual.
*/
private ViewHolder(View parent) {
this.parent = parent;
noteIdText = (TextView) parent.findViewById(R.id.note_id);
noteContentText = (TextView) parent.findViewById(R.id.note_content);
noteDateText = (TextView) parent.findViewById(R.id.note_date);
}
}
@Override
public Filter getFilter() {
Filter filter = new Filter(){
@Override
protected FilterResults performFiltering(CharSequence query) {
FilterResults results = new FilterResults();
ArrayList<NoteViewWrapper> filtered = new ArrayList<NoteViewWrapper>();
query = query.toString().toLowerCase();
for(int i=0;i<data.size();i++){
String dataName = data.get(i).getNote().getContent().toLowerCase();
if (dataName.contains(query)){
filtered.add(data.get(i));
}
}
results.count = filtered.size();
results.values = filtered;
Log.e("values", results.values.toString());
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0){
notifyDataSetInvalidated();
} else {
filtered_data = (ArrayList<NoteViewWrapper>) results.values;
NotesAdapter.this.notifyDataSetChanged();
Log.e("we are in publish results",filtered_data.toString());
}
}
};
return filter;
}
}
现在我的问题是:
- 为什么我的列表视图根本没有更新?我确信我的 performFiltering 方法可以正常工作,因为在 logcat 输出中记录了正确的搜索结果?
- publishResult方法对整个流程有什么影响
- listview怎么知道哪个arraylist的数据会更新到listview?
- 发布结果中的notifyDataSet()有什么作用?它如何刷新甚至不在同一列表视图中的数据集 class?
如果这是一个愚蠢的问题,请原谅我,我不是 android 方面的专家,而且我已经有一段时间没有在 Java 中编程了。
您筛选的数据是
filtered_data = (ArrayList<NoteViewWrapper>) results.values;
NotesAdapter.this.notifyDataSetChanged();
Log.e("we are in publish results",filtered_data.toString());
您正在使用
NoteViewWrapper noteViewWrapper = data.get(position);
改用此代码
NoteViewWrapper noteViewWrapper = filtered_data.get(position);
在你的方法中
@Override
public View getView(int position, View convertView, ViewGroup parent) {}
以下方法用于告诉适配器数据集(ArraList)已经改变,适配器应该更新ListView、GridView等中的结果
notifyDataSetChanged();
我一直在编写一个 android 应用程序,它使用 SearchView 搜索我的数据库,当我在结果数组列表上执行 Log.e() 时,我能够搜索笔记,然后我查看正确的结果,但应该更改以显示搜索结果的列表视图不会受到影响。我正在使用自定义适配器。我只是不明白 publishResults
方法的结果究竟是如何实时修改我的 listview
MainActivity.java
public class MainActivity extends RoboActionBarActivity {
private static final int NEW_NOTE_RESULT_CODE = 4;
private static final int EDIT_NOTE_RESULT_CODE = 5;
@InjectView(android.R.id.empty) private TextView emptyListTextView;
@InjectView(android.R.id.list) private ListView listView;
@InjectView(R.id.add_note_button) private FloatingActionButton addNoteButton;
@Inject private NoteDAO noteDAO;
private ArrayList<Integer> selectedPositions;
private ArrayList<NotesAdapter.NoteViewWrapper> notesData;
private NotesAdapter listAdapter;
private ActionMode.Callback actionModeCallback;
private ActionMode actionMode;
/** {@inheritDoc} */
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Inicializa los componentes //////////////////////////////////////////////////////////////
listView.setOnTouchListener(new ShowHideOnScroll(addNoteButton, getSupportActionBar())); // Esconde o muesta el FAB y la Action Bar
addNoteButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// Crear una nota nueva
startActivityForResult(EditNoteActivity.buildIntent(MainActivity.this), NEW_NOTE_RESULT_CODE);
}
});
selectedPositions = new ArrayList<>();
listView.setTextFilterEnabled(true);
setupNotesAdapter();
setupActionModeCallback();
setListOnItemClickListenersWhenNoActionMode();
updateView();
}
/** {@inheritDoc} */
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
// Get the SearchView and set the searchable configuration
SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
// Assumes current activity is the searchable activity
searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
searchView.setIconifiedByDefault(true); // Do not iconify the widget; expand it by default
SearchView.OnQueryTextListener textChangeListener = new SearchView.OnQueryTextListener()
{
@Override
public boolean onQueryTextChange(String query)
{
listAdapter.getFilter().filter(query);
return true;
}
@Override
public boolean onQueryTextSubmit(String query)
{
// this is your adapter that will be filtered
return false;
}
};
searchView.setOnQueryTextListener(textChangeListener);
return true;
}
/** {@inheritDoc} */
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_about_info:
new AboutNoticeDialog()
.show(getSupportFragmentManager(), "dialog_about_notice");
return true;
case R.id.action_import:
importNotes();
return true;
case R.id.action_export:
exportNotes();
return true;
default: return super.onOptionsItemSelected(item);
}
}
private void exportNotes() {
for (Note note : noteDAO.fetchAll()) {
StringBuilder exportNotes = new StringBuilder();
}
}
private void importNotes() {
}
/** {@inheritDoc} */
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == NEW_NOTE_RESULT_CODE) {
if (resultCode == RESULT_OK) addNote(data);
}
if (requestCode == EDIT_NOTE_RESULT_CODE) {
if (resultCode == RESULT_OK) updateNote(data);
}
super.onActivityResult(requestCode, resultCode, data);
}
/** Crea la llamada al modo contextual. */
private void setupActionModeCallback() {
actionModeCallback = new ActionMode.Callback() {
/** {@inheritDoc} */
@Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
setListOnItemClickListenersWhenActionMode();
// inflar menu contextual
mode.getMenuInflater().inflate(R.menu.context_note, menu);
return true;
}
/** {@inheritDoc} */
@Override
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// Nada
return false;
}
/** {@inheritDoc} */
@Override
public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
switch (item.getItemId()) {
// borrar notas solo si hay notas a borrar; sino se acaba el modo contextual.
case R.id.action_delete:
if (!selectedPositions.isEmpty()) {
new AlertDialog.Builder(MainActivity.this)
.setMessage(getString(R.string.delete_notes_alert, selectedPositions.size()))
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
deleteNotes(selectedPositions);
mode.finish();
}
})
.show();
} else mode.finish();
return true;
case R.id.action_share:
if (!selectedPositions.isEmpty()){
shareNotes(selectedPositions);
}
return true;
case R.id.action_select_all:
selectAll();
default:
return false;
}
}
/** {@inheritDoc} */
@Override
public void onDestroyActionMode(ActionMode mode) {
// Regresar al modo normal
setListOnItemClickListenersWhenNoActionMode();
resetSelectedListItems();
}
};
}
private void selectAll() {
for ( int i=0; i < listAdapter.getCount(); i++) {
selectedPositions.add(i);
notesData.get(i).setSelected(true);
listAdapter.notifyDataSetChanged();
}
}
/** Inicializa el adaptador de notas. */
private void setupNotesAdapter() {
notesData = new ArrayList<>();
for (Note note : noteDAO.fetchAll()) { // Convertir a wrapper
NotesAdapter.NoteViewWrapper noteViewWrapper = new NotesAdapter.NoteViewWrapper(note);
notesData.add(noteViewWrapper);
}
listAdapter = new NotesAdapter(notesData);
listView.setAdapter(listAdapter);
}
/** Actualiza la vista de esta actividad cuando hay notas o no hay notas. */
private void updateView() {
if (notesData.isEmpty()) { // Mostrar mensaje
listView.setVisibility(View.GONE);
emptyListTextView.setVisibility(View.VISIBLE);
} else { // Mostrar lista
listView.setVisibility(View.VISIBLE);
emptyListTextView.setVisibility(View.GONE);
}
}
/**
* Agrega una nota a lista y la fuente de datos.
*
* @param data los datos de la actividad de edición de notas.
*/
private void addNote(Intent data) {
Note note = EditNoteActivity.getExtraNote(data);
noteDAO.insert(note);
NotesAdapter.NoteViewWrapper noteViewWrapper = new NotesAdapter.NoteViewWrapper(note);
notesData.add(0,noteViewWrapper);
updateView();
listAdapter.notifyDataSetChanged();
}
/*
this method is called when the contextual action button of share is pressed
*/
private void shareNotes(ArrayList<Integer> selectedPositions) {
StringBuilder shareBody = new StringBuilder();
// Primero borra de la base de datos
for (int position : selectedPositions) {
NotesAdapter.NoteViewWrapper noteViewWrapper = notesData.get(position);
shareBody.append(noteViewWrapper.getNote().getContent());
}
Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND);
sharingIntent.setType("text/plain");
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody.toString());
startActivity(Intent.createChooser(sharingIntent, getResources().getString(R.string.share_using)));
}
/**
* Borra notas de la lista y de la fuente de datos.
*
* @param selectedPositions las posiciones de las notas en la lista.
*/
private void deleteNotes(ArrayList<Integer> selectedPositions) {
ArrayList<NotesAdapter.NoteViewWrapper> toRemoveList = new ArrayList<>(selectedPositions.size());
// Primero borra de la base de datos
for (int position : selectedPositions) {
NotesAdapter.NoteViewWrapper noteViewWrapper = notesData.get(position);
toRemoveList.add(noteViewWrapper);
noteDAO.delete(noteViewWrapper.getNote());
}
// Y luego de la vista (no al mismo tiempo porque pierdo las posiciones que hay que borrar)
for (NotesAdapter.NoteViewWrapper noteToRemove : toRemoveList) notesData.remove(noteToRemove);
updateView();
listAdapter.notifyDataSetChanged();
}
/**
* Actualiza una nota en la lista y la fuente de datos.
*
* @param data los datos de la actividad de edición de notas.
*/
private void updateNote(Intent data) {
Note updatedNote = EditNoteActivity.getExtraUpdatedNote(data);
noteDAO.update(updatedNote);
for (NotesAdapter.NoteViewWrapper noteViewWrapper : notesData) {
// Buscar la nota vieja para actulizarla en la vista
if (noteViewWrapper.getNote().getId().equals(updatedNote.getId())) {
noteViewWrapper.getNote().setContent(updatedNote.getContent());
noteViewWrapper.getNote().setUpdatedAt(updatedNote.getUpdatedAt());
}
}
listAdapter.notifyDataSetChanged();
}
/** Reinicia las notas seleccionadas a no seleccionadas y limpia la lista de seleccionados. */
private void resetSelectedListItems() {
for (NotesAdapter.NoteViewWrapper noteViewWrapper : notesData) noteViewWrapper.setSelected(false);
selectedPositions.clear();
listAdapter.notifyDataSetChanged();
}
/**
* Inicializa las acciones de la lista al hacer click en sus items cuando NO esta activo el
* modo contextual.
*/
private void setListOnItemClickListenersWhenNoActionMode() {
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Ver la nota al hacer click
startActivityForResult(ViewNoteActivity.buildIntent(MainActivity.this, notesData.get(position).getNote()), EDIT_NOTE_RESULT_CODE);
}
});
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// Iniciar modo contextual para selección de items
notesData.get(position).setSelected(true);
listAdapter.notifyDataSetChanged();
selectedPositions.add(position);
actionMode = startSupportActionMode(actionModeCallback);
return true;
}
});
}
/**
* Inicializa las acciones de la lista al hacer click en sus items cuando esta activo el menu
* contextual.
*/
private void setListOnItemClickListenersWhenActionMode() {
listView.setOnItemLongClickListener(null);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// Agregar items a la lista de seleccionados y cambiarles el fondo.
// Si se deseleccionan todos los items, se acaba el modo contextual
if (selectedPositions.contains(position)) {
selectedPositions.remove((Object)position); // no quiero el índice sino el objeto
if (selectedPositions.isEmpty()) actionMode.finish();
else {
notesData.get(position).setSelected(false);
listAdapter.notifyDataSetChanged();
}
} else {
notesData.get(position).setSelected(true);
listAdapter.notifyDataSetChanged();
selectedPositions.add(position);
}
}
});
}
}
NotesAdapter.java
public class NotesAdapter extends BaseAdapter implements Filterable{
private static final DateFormat DATETIME_FORMAT = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM);
private final List<NoteViewWrapper> data;
private List<NoteViewWrapper> filtered_data;
/** Wrapper para notas. Util para cambiar el fondo de los item seleccionados. */
public static class NoteViewWrapper {
private final Note note;
private boolean isSelected;
/**
* Contruye un nuevo NoteWrapper con la nota dada.
*
* @param note una nota.
*/
public NoteViewWrapper(Note note) {
this.note = note;
}
public Note getNote() {
return note;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
}
}
/**
* Constructor.
*
* @param data la lista de notas a usar como fuente de datos para este adaptador.
*/
public NotesAdapter(List<NoteViewWrapper> data) {
this.data = data;
this.filtered_data = data;
}
/** @return cuantos datos hay en la lista de notas. */
@Override
public int getCount() {
return data.size();
}
/**
* @param position la posición de la nota que se quiere
* @return la nota en la posición dada.
*/
@Override
public NoteViewWrapper getItem(int position) {
return data.get(position);
}
/**
* @param position una posición
* @return la misma posición dada
*/
@Override
public long getItemId(int position) {
return position;
}
/**
* Muestra los datos de la nota en la posición dada en una instancia del componente visual
* {@link com.rants.R.layout#notes_row}.
*
* @see <a href="http://bit.ly/MJqzXb">Hold View Objects in a View Holder</a>
* @param position la posición de la nota en curso.
* @param convertView el componente visual a usar.
* @param parent el componente visual padre del componente visual a usar.
* @return la vista con los datos.
*/
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) { // inflar componente visual
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.notes_row, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else holder = (ViewHolder) convertView.getTag(); // ya existe, solo es reciclarlo
// Inicializa la vista con los datos de la nota
NoteViewWrapper noteViewWrapper = data.get(position);
holder.noteIdText.setText(String.valueOf(noteViewWrapper.note.getId()));
// Corta la cadena a 80 caracteres y le agrega "..."
SpannableString hashText = new SpannableString(noteViewWrapper.note.getContent());
Matcher matcher = Pattern.compile("#([A-Za-z0-9_-]+)").matcher(hashText);
while (matcher.find())
{
hashText.setSpan(new ForegroundColorSpan(Color.BLUE), matcher.start(), matcher.end(), 0);
}
holder.noteContentText.setText(hashText);
holder.noteDateText.setText(DATETIME_FORMAT.format(noteViewWrapper.note.getUpdatedAt()));
// Cambia el color del fondo si es seleccionado
if (noteViewWrapper.isSelected) holder.parent.setBackgroundColor(parent.getContext().getResources().getColor(R.color.selected_note));
// Sino lo regresa a transparente
else holder.parent.setBackgroundColor(parent.getContext().getResources().getColor(android.R.color.transparent));
return convertView;
}
/** Almacena componentes visuales para acceso rápido sin necesidad de buscarlos muy seguido.*/
private static class ViewHolder {
private TextView noteIdText;
private TextView noteContentText;
private TextView noteDateText;
private View parent;
/**
* Constructor. Encuentra todas los componentes visuales en el componente padre dado.
*
* @param parent un componente visual.
*/
private ViewHolder(View parent) {
this.parent = parent;
noteIdText = (TextView) parent.findViewById(R.id.note_id);
noteContentText = (TextView) parent.findViewById(R.id.note_content);
noteDateText = (TextView) parent.findViewById(R.id.note_date);
}
}
@Override
public Filter getFilter() {
Filter filter = new Filter(){
@Override
protected FilterResults performFiltering(CharSequence query) {
FilterResults results = new FilterResults();
ArrayList<NoteViewWrapper> filtered = new ArrayList<NoteViewWrapper>();
query = query.toString().toLowerCase();
for(int i=0;i<data.size();i++){
String dataName = data.get(i).getNote().getContent().toLowerCase();
if (dataName.contains(query)){
filtered.add(data.get(i));
}
}
results.count = filtered.size();
results.values = filtered;
Log.e("values", results.values.toString());
return results;
}
@Override
protected void publishResults(CharSequence constraint, FilterResults results) {
if (results.count == 0){
notifyDataSetInvalidated();
} else {
filtered_data = (ArrayList<NoteViewWrapper>) results.values;
NotesAdapter.this.notifyDataSetChanged();
Log.e("we are in publish results",filtered_data.toString());
}
}
};
return filter;
}
}
现在我的问题是:
- 为什么我的列表视图根本没有更新?我确信我的 performFiltering 方法可以正常工作,因为在 logcat 输出中记录了正确的搜索结果?
- publishResult方法对整个流程有什么影响
- listview怎么知道哪个arraylist的数据会更新到listview?
- 发布结果中的notifyDataSet()有什么作用?它如何刷新甚至不在同一列表视图中的数据集 class?
如果这是一个愚蠢的问题,请原谅我,我不是 android 方面的专家,而且我已经有一段时间没有在 Java 中编程了。
您筛选的数据是
filtered_data = (ArrayList<NoteViewWrapper>) results.values;
NotesAdapter.this.notifyDataSetChanged();
Log.e("we are in publish results",filtered_data.toString());
您正在使用
NoteViewWrapper noteViewWrapper = data.get(position);
改用此代码
NoteViewWrapper noteViewWrapper = filtered_data.get(position);
在你的方法中
@Override
public View getView(int position, View convertView, ViewGroup parent) {}
以下方法用于告诉适配器数据集(ArraList)已经改变,适配器应该更新ListView、GridView等中的结果
notifyDataSetChanged();