/* * Copyright (C) 2006 The Android Open Source Project * * 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 android.widget; import android.content.Context; import android.database.DataSetObserver; import android.os.Handler; import android.os.Parcelable; import android.os.SystemClock; import android.util.AttributeSet; import android.util.SparseArray; import android.view.ContextMenu; import android.view.SoundEffectConstants; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; import android.view.ContextMenu.ContextMenuInfo; import android.view.accessibility.AccessibilityEvent; /** * An AdapterView is a view whose children are determined by an {@link Adapter}. * *
* See {@link ListView}, {@link GridView}, {@link Spinner} and
* {@link Gallery} for commonly used subclasses of AdapterView.
*/
public abstract class AdapterView
* Implementers can call getItemAtPosition(position) if they need
* to access the data associated with the selected item.
*
* @param parent The AdapterView where the click happened.
* @param view The view within the AdapterView that was clicked (this
* will be a view provided by the adapter)
* @param position The position of the view in the adapter.
* @param id The row id of the item that was clicked.
*/
void onItemClick(AdapterView> parent, View view, int position, long id);
}
/**
* Register a callback to be invoked when an item in this AdapterView has
* been clicked.
*
* @param listener The callback that will be invoked.
*/
public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}
/**
* @return The callback to be invoked with an item in this AdapterView has
* been clicked, or null id no callback has been set.
*/
public final OnItemClickListener getOnItemClickListener() {
return mOnItemClickListener;
}
/**
* Call the OnItemClickListener, if it is defined.
*
* @param view The view within the AdapterView that was clicked.
* @param position The position of the view in the adapter.
* @param id The row id of the item that was clicked.
* @return True if there was an assigned OnItemClickListener that was
* called, false otherwise is returned.
*/
public boolean performItemClick(View view, int position, long id) {
if (mOnItemClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
mOnItemClickListener.onItemClick(this, view, position, id);
return true;
}
return false;
}
/**
* Interface definition for a callback to be invoked when an item in this
* view has been clicked and held.
*/
public interface OnItemLongClickListener {
/**
* Callback method to be invoked when an item in this view has been
* clicked and held.
*
* Implementers can call getItemAtPosition(position) if they need to access
* the data associated with the selected item.
*
* @param parent The AbsListView where the click happened
* @param view The view within the AbsListView that was clicked
* @param position The position of the view in the list
* @param id The row id of the item that was clicked
*
* @return true if the callback consumed the long click, false otherwise
*/
boolean onItemLongClick(AdapterView> parent, View view, int position, long id);
}
/**
* Register a callback to be invoked when an item in this AdapterView has
* been clicked and held
*
* @param listener The callback that will run
*/
public void setOnItemLongClickListener(OnItemLongClickListener listener) {
if (!isLongClickable()) {
setLongClickable(true);
}
mOnItemLongClickListener = listener;
}
/**
* @return The callback to be invoked with an item in this AdapterView has
* been clicked and held, or null id no callback as been set.
*/
public final OnItemLongClickListener getOnItemLongClickListener() {
return mOnItemLongClickListener;
}
/**
* Interface definition for a callback to be invoked when
* an item in this view has been selected.
*/
public interface OnItemSelectedListener {
/**
* Callback method to be invoked when an item in this view has been
* selected.
*
* Impelmenters can call getItemAtPosition(position) if they need to access the
* data associated with the selected item.
*
* @param parent The AdapterView where the selection happened
* @param view The view within the AdapterView that was clicked
* @param position The position of the view in the adapter
* @param id The row id of the item that is selected
*/
void onItemSelected(AdapterView> parent, View view, int position, long id);
/**
* Callback method to be invoked when the selection disappears from this
* view. The selection can disappear for instance when touch is activated
* or when the adapter becomes empty.
*
* @param parent The AdapterView that now contains no selected item.
*/
void onNothingSelected(AdapterView> parent);
}
/**
* Register a callback to be invoked when an item in this AdapterView has
* been selected.
*
* @param listener The callback that will run
*/
public void setOnItemSelectedListener(OnItemSelectedListener listener) {
mOnItemSelectedListener = listener;
}
public final OnItemSelectedListener getOnItemSelectedListener() {
return mOnItemSelectedListener;
}
/**
* Extra menu information provided to the
* {@link android.view.View.OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo) }
* callback when a context menu is brought up for this AdapterView.
*
*/
public static class AdapterContextMenuInfo implements ContextMenu.ContextMenuInfo {
public AdapterContextMenuInfo(View targetView, int position, long id) {
this.targetView = targetView;
this.position = position;
this.id = id;
}
/**
* The child view for which the context menu is being displayed. This
* will be one of the children of this AdapterView.
*/
public View targetView;
/**
* The position in the adapter for which the context menu is being
* displayed.
*/
public int position;
/**
* The row id of the item for which the context menu is being displayed.
*/
public long id;
}
/**
* Returns the adapter currently associated with this widget.
*
* @return The adapter used to provide this view's content.
*/
public abstract T getAdapter();
/**
* Sets the adapter that provides the data and the views to represent the data
* in this widget.
*
* @param adapter The adapter to use to create this view's content.
*/
public abstract void setAdapter(T adapter);
/**
* This method is not supported and throws an UnsupportedOperationException when called.
*
* @param child Ignored.
*
* @throws UnsupportedOperationException Every time this method is invoked.
*/
@Override
public void addView(View child) {
throw new UnsupportedOperationException("addView(View) is not supported in AdapterView");
}
/**
* This method is not supported and throws an UnsupportedOperationException when called.
*
* @param child Ignored.
* @param index Ignored.
*
* @throws UnsupportedOperationException Every time this method is invoked.
*/
@Override
public void addView(View child, int index) {
throw new UnsupportedOperationException("addView(View, int) is not supported in AdapterView");
}
/**
* This method is not supported and throws an UnsupportedOperationException when called.
*
* @param child Ignored.
* @param params Ignored.
*
* @throws UnsupportedOperationException Every time this method is invoked.
*/
@Override
public void addView(View child, LayoutParams params) {
throw new UnsupportedOperationException("addView(View, LayoutParams) "
+ "is not supported in AdapterView");
}
/**
* This method is not supported and throws an UnsupportedOperationException when called.
*
* @param child Ignored.
* @param index Ignored.
* @param params Ignored.
*
* @throws UnsupportedOperationException Every time this method is invoked.
*/
@Override
public void addView(View child, int index, LayoutParams params) {
throw new UnsupportedOperationException("addView(View, int, LayoutParams) "
+ "is not supported in AdapterView");
}
/**
* This method is not supported and throws an UnsupportedOperationException when called.
*
* @param child Ignored.
*
* @throws UnsupportedOperationException Every time this method is invoked.
*/
@Override
public void removeView(View child) {
throw new UnsupportedOperationException("removeView(View) is not supported in AdapterView");
}
/**
* This method is not supported and throws an UnsupportedOperationException when called.
*
* @param index Ignored.
*
* @throws UnsupportedOperationException Every time this method is invoked.
*/
@Override
public void removeViewAt(int index) {
throw new UnsupportedOperationException("removeViewAt(int) is not supported in AdapterView");
}
/**
* This method is not supported and throws an UnsupportedOperationException when called.
*
* @throws UnsupportedOperationException Every time this method is invoked.
*/
@Override
public void removeAllViews() {
throw new UnsupportedOperationException("removeAllViews() is not supported in AdapterView");
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mLayoutHeight = getHeight();
}
/**
* Return the position of the currently selected item within the adapter's data set
*
* @return int Position (starting at 0), or {@link #INVALID_POSITION} if there is nothing selected.
*/
@ViewDebug.CapturedViewProperty
public int getSelectedItemPosition() {
return mNextSelectedPosition;
}
/**
* @return The id corresponding to the currently selected item, or {@link #INVALID_ROW_ID}
* if nothing is selected.
*/
@ViewDebug.CapturedViewProperty
public long getSelectedItemId() {
return mNextSelectedRowId;
}
/**
* @return The view corresponding to the currently selected item, or null
* if nothing is selected
*/
public abstract View getSelectedView();
/**
* @return The data corresponding to the currently selected item, or
* null if there is nothing selected.
*/
public Object getSelectedItem() {
T adapter = getAdapter();
int selection = getSelectedItemPosition();
if (adapter != null && adapter.getCount() > 0 && selection >= 0) {
return adapter.getItem(selection);
} else {
return null;
}
}
/**
* @return The number of items owned by the Adapter associated with this
* AdapterView. (This is the number of data items, which may be
* larger than the number of visible view.)
*/
@ViewDebug.CapturedViewProperty
public int getCount() {
return mItemCount;
}
/**
* Get the position within the adapter's data set for the view, where view is a an adapter item
* or a descendant of an adapter item.
*
* @param view an adapter item, or a descendant of an adapter item. This must be visible in this
* AdapterView at the time of the call.
* @return the position within the adapter's data set of the view, or {@link #INVALID_POSITION}
* if the view does not correspond to a list item (or it is not currently visible).
*/
public int getPositionForView(View view) {
View listItem = view;
try {
View v;
while (!(v = (View) listItem.getParent()).equals(this)) {
listItem = v;
}
} catch (ClassCastException e) {
// We made it up to the window without find this list view
return INVALID_POSITION;
}
// Search the children for the list item
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
if (getChildAt(i).equals(listItem)) {
return mFirstPosition + i;
}
}
// Child not found!
return INVALID_POSITION;
}
/**
* Returns the position within the adapter's data set for the first item
* displayed on screen.
*
* @return The position within the adapter's data set
*/
public int getFirstVisiblePosition() {
return mFirstPosition;
}
/**
* Returns the position within the adapter's data set for the last item
* displayed on screen.
*
* @return The position within the adapter's data set
*/
public int getLastVisiblePosition() {
return mFirstPosition + getChildCount() - 1;
}
/**
* Sets the currently selected item. To support accessibility subclasses that
* override this method must invoke the overriden super method first.
*
* @param position Index (starting at 0) of the data item to be selected.
*/
public abstract void setSelection(int position);
/**
* Sets the view to show if the adapter is empty
*/
public void setEmptyView(View emptyView) {
mEmptyView = emptyView;
final T adapter = getAdapter();
final boolean empty = ((adapter == null) || adapter.isEmpty());
updateEmptyStatus(empty);
}
/**
* When the current adapter is empty, the AdapterView can display a special view
* call the empty view. The empty view is used to provide feedback to the user
* that no data is available in this AdapterView.
*
* @return The view to show if the adapter is empty.
*/
public View getEmptyView() {
return mEmptyView;
}
/**
* Indicates whether this view is in filter mode. Filter mode can for instance
* be enabled by a user when typing on the keyboard.
*
* @return True if the view is in filter mode, false otherwise.
*/
boolean isInFilterMode() {
return false;
}
@Override
public void setFocusable(boolean focusable) {
final T adapter = getAdapter();
final boolean empty = adapter == null || adapter.getCount() == 0;
mDesiredFocusableState = focusable;
if (!focusable) {
mDesiredFocusableInTouchModeState = false;
}
super.setFocusable(focusable && (!empty || isInFilterMode()));
}
@Override
public void setFocusableInTouchMode(boolean focusable) {
final T adapter = getAdapter();
final boolean empty = adapter == null || adapter.getCount() == 0;
mDesiredFocusableInTouchModeState = focusable;
if (focusable) {
mDesiredFocusableState = true;
}
super.setFocusableInTouchMode(focusable && (!empty || isInFilterMode()));
}
void checkFocus() {
final T adapter = getAdapter();
final boolean empty = adapter == null || adapter.getCount() == 0;
final boolean focusable = !empty || isInFilterMode();
// The order in which we set focusable in touch mode/focusable may matter
// for the client, see View.setFocusableInTouchMode() comments for more
// details
super.setFocusableInTouchMode(focusable && mDesiredFocusableInTouchModeState);
super.setFocusable(focusable && mDesiredFocusableState);
if (mEmptyView != null) {
updateEmptyStatus((adapter == null) || adapter.isEmpty());
}
}
/**
* Update the status of the list based on the empty parameter. If empty is true and
* we have an empty view, display it. In all the other cases, make sure that the listview
* is VISIBLE and that the empty view is GONE (if it's not null).
*/
private void updateEmptyStatus(boolean empty) {
if (isInFilterMode()) {
empty = false;
}
if (empty) {
if (mEmptyView != null) {
mEmptyView.setVisibility(View.VISIBLE);
setVisibility(View.GONE);
} else {
// If the caller just removed our empty view, make sure the list view is visible
setVisibility(View.VISIBLE);
}
// We are now GONE, so pending layouts will not be dispatched.
// Force one here to make sure that the state of the list matches
// the state of the adapter.
if (mDataChanged) {
this.onLayout(false, mLeft, mTop, mRight, mBottom);
}
} else {
if (mEmptyView != null) mEmptyView.setVisibility(View.GONE);
setVisibility(View.VISIBLE);
}
}
/**
* Gets the data associated with the specified position in the list.
*
* @param position Which data to get
* @return The data associated with the specified position in the list
*/
public Object getItemAtPosition(int position) {
T adapter = getAdapter();
return (adapter == null || position < 0) ? null : adapter.getItem(position);
}
public long getItemIdAtPosition(int position) {
T adapter = getAdapter();
return (adapter == null || position < 0) ? INVALID_ROW_ID : adapter.getItemId(position);
}
@Override
public void setOnClickListener(OnClickListener l) {
throw new RuntimeException("Don't call setOnClickListener for an AdapterView. "
+ "You probably want setOnItemClickListener instead");
}
/**
* Override to prevent freezing of any views created by the adapter.
*/
@Override
protected void dispatchSaveInstanceState(SparseArray