You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
321 lines
8.6 KiB
321 lines
8.6 KiB
/* |
|
* Copyright 2010-2024 Eric Kok et al. |
|
* |
|
* Transdroid is free software: you can redistribute it and/or modify |
|
* it under the terms of the GNU General Public License as published by |
|
* the Free Software Foundation, either version 3 of the License, or |
|
* (at your option) any later version. |
|
* |
|
* Transdroid is distributed in the hope that it will be useful, |
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
* GNU General Public License for more details. |
|
* |
|
* You should have received a copy of the GNU General Public License |
|
* along with Transdroid. If not, see <http://www.gnu.org/licenses/>. |
|
*/ |
|
package org.transdroid.core.gui.lists; |
|
|
|
import android.database.DataSetObserver; |
|
import android.view.View; |
|
import android.view.ViewGroup; |
|
import android.widget.Adapter; |
|
import android.widget.BaseAdapter; |
|
import android.widget.ListAdapter; |
|
import android.widget.SectionIndexer; |
|
import android.widget.TextView; |
|
|
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
|
|
/** |
|
* An adapter that can contain many other adapters and shows them in sequence. Taken from |
|
* http://stackoverflow.com/questions/7964259/android-attaching-multiple-adapters-to-one-adapter and based on the Apache |
|
* 2-licensed CommonsWare MergeAdapter. |
|
* |
|
* @author Eric Kok |
|
* @author Alex Amiryan |
|
* @author Mark Murphy |
|
*/ |
|
public class MergeAdapter extends BaseAdapter implements SectionIndexer { |
|
|
|
protected ArrayList<ListAdapter> pieces = new ArrayList<>(); |
|
protected String noItemsText; |
|
|
|
/** |
|
* Stock constructor, simply chaining to the superclass. |
|
*/ |
|
public MergeAdapter() { |
|
super(); |
|
} |
|
|
|
/** |
|
* Adds a new adapter to the roster of things to appear in the aggregate list. |
|
* |
|
* @param adapter Source for row views for this section |
|
*/ |
|
public void addAdapter(ListAdapter adapter) { |
|
pieces.add(adapter); |
|
adapter.registerDataSetObserver(new CascadeDataSetObserver()); |
|
} |
|
|
|
/** |
|
* Get the data item associated with the specified position in the data set. |
|
* |
|
* @param position Position of the item whose data we want |
|
*/ |
|
public Object getItem(int position) { |
|
for (ListAdapter piece : pieces) { |
|
int size = piece.getCount(); |
|
|
|
if (position < size) { |
|
return (piece.getItem(position)); |
|
} |
|
|
|
position -= size; |
|
} |
|
|
|
return (null); |
|
} |
|
|
|
public void setNoItemsText(String text) { |
|
noItemsText = text; |
|
} |
|
|
|
/** |
|
* Get the adapter associated with the specified position in the data set. |
|
* |
|
* @param position Position of the item whose adapter we want |
|
*/ |
|
public ListAdapter getAdapter(int position) { |
|
for (ListAdapter piece : pieces) { |
|
int size = piece.getCount(); |
|
|
|
if (position < size) { |
|
return (piece); |
|
} |
|
|
|
position -= size; |
|
} |
|
|
|
return (null); |
|
} |
|
|
|
/** |
|
* How many items are in the data set represented by this {@link Adapter}. |
|
*/ |
|
public int getCount() { |
|
int total = 0; |
|
|
|
for (ListAdapter piece : pieces) { |
|
total += piece.getCount(); |
|
} |
|
|
|
if (total == 0 && noItemsText != null) { |
|
total = 1; |
|
} |
|
|
|
return (total); |
|
} |
|
|
|
/** |
|
* Returns the number of types of {@link View}s that will be created by {@link #getView(int, View, ViewGroup)}. |
|
*/ |
|
@Override |
|
public int getViewTypeCount() { |
|
int total = 0; |
|
|
|
for (ListAdapter piece : pieces) { |
|
total += piece.getViewTypeCount(); |
|
} |
|
|
|
return (Math.max(total, 1)); // needed for setListAdapter() before |
|
// content add' |
|
} |
|
|
|
/** |
|
* Get the type of {@link View} that will be created by {@link #getView(int, View, ViewGroup)} for the specified item. |
|
* |
|
* @param position Position of the item whose data we want |
|
*/ |
|
@Override |
|
public int getItemViewType(int position) { |
|
int typeOffset = 0; |
|
int result = -1; |
|
|
|
for (ListAdapter piece : pieces) { |
|
int size = piece.getCount(); |
|
|
|
if (position < size) { |
|
result = typeOffset + piece.getItemViewType(position); |
|
break; |
|
} |
|
|
|
position -= size; |
|
typeOffset += piece.getViewTypeCount(); |
|
} |
|
|
|
return (result); |
|
} |
|
|
|
/** |
|
* Are all items in this {@link ListAdapter} enabled? If yes it means all items are selectable and clickable. |
|
*/ |
|
@Override |
|
public boolean areAllItemsEnabled() { |
|
return (false); |
|
} |
|
|
|
/** |
|
* Returns true if the item at the specified position is not a separator. |
|
* |
|
* @param position Position of the item whose data we want |
|
*/ |
|
@Override |
|
public boolean isEnabled(int position) { |
|
for (ListAdapter piece : pieces) { |
|
int size = piece.getCount(); |
|
|
|
if (position < size) { |
|
return (piece.isEnabled(position)); |
|
} |
|
|
|
position -= size; |
|
} |
|
|
|
return (false); |
|
} |
|
|
|
/** |
|
* Get a {@link View} that displays the data at the specified position in the data set. |
|
* |
|
* @param position Position of the item whose data we want |
|
* @param convertView View to recycle, if not null |
|
* @param parent ViewGroup containing the returned View |
|
*/ |
|
public View getView(int position, View convertView, ViewGroup parent) { |
|
for (ListAdapter piece : pieces) { |
|
int size = piece.getCount(); |
|
|
|
if (position < size) { |
|
|
|
return (piece.getView(position, convertView, parent)); |
|
} |
|
|
|
position -= size; |
|
} |
|
|
|
if (noItemsText != null) { |
|
TextView text = new TextView(parent.getContext()); |
|
text.setText(noItemsText); |
|
return text; |
|
} |
|
|
|
return (null); |
|
} |
|
|
|
/** |
|
* Get the row id associated with the specified position in the list. |
|
* |
|
* @param position Position of the item whose data we want |
|
*/ |
|
public long getItemId(int position) { |
|
for (ListAdapter piece : pieces) { |
|
int size = piece.getCount(); |
|
|
|
if (position < size) { |
|
return (piece.getItemId(position)); |
|
} |
|
|
|
position -= size; |
|
} |
|
|
|
return (-1); |
|
} |
|
|
|
public final int getPositionForSection(int section) { |
|
int position = 0; |
|
|
|
for (ListAdapter piece : pieces) { |
|
if (piece instanceof SectionIndexer) { |
|
Object[] sections = ((SectionIndexer) piece).getSections(); |
|
int numSections = 0; |
|
|
|
if (sections != null) { |
|
numSections = sections.length; |
|
} |
|
|
|
if (section < numSections) { |
|
return (position + ((SectionIndexer) piece).getPositionForSection(section)); |
|
} else if (sections != null) { |
|
section -= numSections; |
|
} |
|
} |
|
|
|
position += piece.getCount(); |
|
} |
|
|
|
return (0); |
|
} |
|
|
|
public final int getSectionForPosition(int position) { |
|
int section = 0; |
|
|
|
for (ListAdapter piece : pieces) { |
|
int size = piece.getCount(); |
|
|
|
if (position < size) { |
|
if (piece instanceof SectionIndexer) { |
|
return (section + ((SectionIndexer) piece).getSectionForPosition(position)); |
|
} |
|
|
|
return (0); |
|
} else { |
|
if (piece instanceof SectionIndexer) { |
|
Object[] sections = ((SectionIndexer) piece).getSections(); |
|
|
|
if (sections != null) { |
|
section += sections.length; |
|
} |
|
} |
|
} |
|
|
|
position -= size; |
|
} |
|
|
|
return (0); |
|
} |
|
|
|
public final Object[] getSections() { |
|
ArrayList<Object> sections = new ArrayList<>(); |
|
|
|
for (ListAdapter piece : pieces) { |
|
if (piece instanceof SectionIndexer) { |
|
Object[] curSections = ((SectionIndexer) piece).getSections(); |
|
|
|
if (curSections != null) { |
|
sections.addAll(Arrays.asList(curSections)); |
|
} |
|
} |
|
} |
|
|
|
if (sections.size() == 0) { |
|
return (null); |
|
} |
|
|
|
return (sections.toArray(new Object[0])); |
|
} |
|
|
|
private class CascadeDataSetObserver extends DataSetObserver { |
|
@Override |
|
public void onChanged() { |
|
notifyDataSetChanged(); |
|
} |
|
|
|
@Override |
|
public void onInvalidated() { |
|
notifyDataSetInvalidated(); |
|
} |
|
} |
|
|
|
}
|
|
|