summaryrefslogtreecommitdiffstats
path: root/core/java/android/widget
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/widget')
-rw-r--r--core/java/android/widget/AbsListView.java91
-rw-r--r--core/java/android/widget/AutoCompleteTextView.java54
-rw-r--r--core/java/android/widget/ListView.java20
-rw-r--r--core/java/android/widget/PopupWindow.java61
-rw-r--r--core/java/android/widget/RelativeLayout.java2
-rw-r--r--core/java/android/widget/TextView.java48
6 files changed, 224 insertions, 52 deletions
diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java
index 767c7e7..1ca59b2 100644
--- a/core/java/android/widget/AbsListView.java
+++ b/core/java/android/widget/AbsListView.java
@@ -433,7 +433,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
private InputConnection mDefInputConnection;
private InputConnectionWrapper mPublicInputConnection;
-
+
+ private Runnable mClearScrollingCache;
+
/**
* Interface definition for a callback to be invoked when the list or grid
* has been scrolled.
@@ -2299,6 +2301,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
if (more) {
+ invalidate();
mLastFlingY = y;
post(this);
} else {
@@ -2322,16 +2325,23 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
private void clearScrollingCache() {
- if (mCachingStarted) {
- setChildrenDrawnWithCacheEnabled(false);
- if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
- setChildrenDrawingCacheEnabled(false);
- }
- if (!isAlwaysDrawnWithCacheEnabled()) {
- invalidate();
- }
- mCachingStarted = false;
+ if (mClearScrollingCache == null) {
+ mClearScrollingCache = new Runnable() {
+ public void run() {
+ if (mCachingStarted) {
+ mCachingStarted = false;
+ setChildrenDrawnWithCacheEnabled(false);
+ if ((mPersistentDrawingCache & PERSISTENT_SCROLLING_CACHE) == 0) {
+ setChildrenDrawingCacheEnabled(false);
+ }
+ if (!isAlwaysDrawnWithCacheEnabled()) {
+ invalidate();
+ }
+ }
+ }
+ };
}
+ post(mClearScrollingCache);
}
/**
@@ -2788,7 +2798,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
int screenHeight = getResources().getDisplayMetrics().heightPixels;
final int[] xy = new int[2];
getLocationOnScreen(xy);
- // TODO: The 20 below should come from the theme and be expressed in dip
+ // TODO: The 20 below should come from the theme
// TODO: And the gravity should be defined in the theme as well
final int bottomGap = screenHeight - xy[1] - getHeight() + (int) (mDensityScale * 20);
if (!mPopup.isShowing()) {
@@ -3180,7 +3190,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
// Reclaim views on screen
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
- AbsListView.LayoutParams lp = (AbsListView.LayoutParams)child.getLayoutParams();
+ AbsListView.LayoutParams lp = (AbsListView.LayoutParams) child.getLayoutParams();
// Don't reclaim header or footer views, or views that should be ignored
if (lp != null && mRecycler.shouldRecycleViewType(lp.viewType)) {
views.add(child);
@@ -3195,6 +3205,63 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te
}
/**
+ * @hide
+ */
+ @Override
+ protected boolean onConsistencyCheck(int consistency) {
+ boolean result = super.onConsistencyCheck(consistency);
+
+ final boolean checkLayout = (consistency & ViewDebug.CONSISTENCY_LAYOUT) != 0;
+
+ if (checkLayout) {
+ // The active recycler must be empty
+ final View[] activeViews = mRecycler.mActiveViews;
+ int count = activeViews.length;
+ for (int i = 0; i < count; i++) {
+ if (activeViews[i] != null) {
+ result = false;
+ android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG,
+ "AbsListView " + this + " has a view in its active recycler: " +
+ activeViews[i]);
+ }
+ }
+
+ // All views in the recycler must NOT be on screen and must NOT have a parent
+ final ArrayList<View> scrap = mRecycler.mCurrentScrap;
+ if (!checkScrap(scrap)) result = false;
+ final ArrayList<View>[] scraps = mRecycler.mScrapViews;
+ count = scraps.length;
+ for (int i = 0; i < count; i++) {
+ if (!checkScrap(scraps[i])) result = false;
+ }
+ }
+
+ return result;
+ }
+
+ private boolean checkScrap(ArrayList<View> scrap) {
+ if (scrap == null) return true;
+ boolean result = true;
+
+ final int count = scrap.size();
+ for (int i = 0; i < count; i++) {
+ final View view = scrap.get(i);
+ if (view.getParent() != null) {
+ result = false;
+ android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
+ " has a view in its scrap heap still attached to a parent: " + view);
+ }
+ if (indexOfChild(view) >= 0) {
+ result = false;
+ android.util.Log.d(ViewDebug.CONSISTENCY_LOG_TAG, "AbsListView " + this +
+ " has a view in its scrap heap that is also a direct child: " + view);
+ }
+ }
+
+ return result;
+ }
+
+ /**
* Sets the recycler listener to be notified whenever a View is set aside in
* the recycler for later reuse. This listener can be used to free resources
* associated to the View.
diff --git a/core/java/android/widget/AutoCompleteTextView.java b/core/java/android/widget/AutoCompleteTextView.java
index 8da7c6b..bfc5f08 100644
--- a/core/java/android/widget/AutoCompleteTextView.java
+++ b/core/java/android/widget/AutoCompleteTextView.java
@@ -126,6 +126,8 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
// Indicates whether this AutoCompleteTextView is attached to a window or not
// The widget is attached to a window when mAttachCount > 0
private int mAttachCount;
+
+ private AutoCompleteTextView.PassThroughClickListener mPassThroughClickListener;
public AutoCompleteTextView(Context context) {
this(context, null);
@@ -186,6 +188,28 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
setFocusable(true);
addTextChangedListener(new MyWatcher());
+
+ mPassThroughClickListener = new PassThroughClickListener();
+ super.setOnClickListener(mPassThroughClickListener);
+ }
+
+ @Override
+ public void setOnClickListener(OnClickListener listener) {
+ mPassThroughClickListener.mWrapped = listener;
+ }
+
+ /**
+ * Private hook into the on click event, dispatched from {@link PassThroughClickListener}
+ */
+ private void onClickImpl() {
+ // if drop down should always visible, bring it back in front of the soft
+ // keyboard when the user touches the text field
+ if (mDropDownAlwaysVisible
+ && mPopup.isShowing()
+ && mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED) {
+ mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
+ showDropDown();
+ }
}
/**
@@ -1130,9 +1154,14 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
}
}
- // Max height available on the screen for a popup
- final int maxHeight =
- mPopup.getMaxAvailableHeight(getDropDownAnchorView(), mDropDownVerticalOffset);
+ // Max height available on the screen for a popup. If this AutoCompleteTextView has
+ // the dropDownAlwaysVisible attribute, and the input method is not currently required,
+ // we then we ask for the height ignoring any bottom decorations like the input method.
+ // Otherwise we respect the input method.
+ boolean ignoreBottomDecorations = mDropDownAlwaysVisible &&
+ mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
+ final int maxHeight = mPopup.getMaxAvailableHeight(
+ getDropDownAnchorView(), mDropDownVerticalOffset, ignoreBottomDecorations);
final int measuredHeight = mDropDownList.measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
0, ListView.NO_POSITION, maxHeight - otherHeights, 2) + otherHeights;
@@ -1214,7 +1243,7 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
- mPopup.update();
+ showDropDown();
}
return false;
}
@@ -1353,4 +1382,21 @@ public class AutoCompleteTextView extends EditText implements Filter.FilterListe
*/
CharSequence fixText(CharSequence invalidText);
}
+
+ /**
+ * Allows us a private hook into the on click event without preventing users from setting
+ * their own click listener.
+ */
+ private class PassThroughClickListener implements OnClickListener {
+
+ private View.OnClickListener mWrapped;
+
+ /** {@inheritDoc} */
+ public void onClick(View v) {
+ onClickImpl();
+
+ if (mWrapped != null) mWrapped.onClick(v);
+ }
+ }
+
}
diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java
index 66c162e..5472d68 100644
--- a/core/java/android/widget/ListView.java
+++ b/core/java/android/widget/ListView.java
@@ -20,6 +20,7 @@ import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
+import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.ColorDrawable;
import android.os.Parcel;
@@ -113,7 +114,11 @@ public class ListView extends AbsListView {
Drawable mDivider;
int mDividerHeight;
+
+ private boolean mIsCacheColorOpaque;
+ private boolean mDividerIsOpaque;
private boolean mClipDivider;
+
private boolean mHeaderDividersEnabled;
private boolean mFooterDividersEnabled;
@@ -2776,6 +2781,20 @@ public class ListView extends AbsListView {
return mItemsCanFocus;
}
+ /**
+ * @hide Pending API council approval.
+ */
+ @Override
+ public boolean isOpaque() {
+ return (mCachingStarted && mIsCacheColorOpaque && mDividerIsOpaque) || super.isOpaque();
+ }
+
+ @Override
+ public void setCacheColorHint(int color) {
+ mIsCacheColorOpaque = (color >>> 24) == 0xFF;
+ super.setCacheColorHint(color);
+ }
+
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the dividers
@@ -2897,6 +2916,7 @@ public class ListView extends AbsListView {
mClipDivider = false;
}
mDivider = divider;
+ mDividerIsOpaque = divider == null || divider.getOpacity() == PixelFormat.OPAQUE;
requestLayoutIfNecessary();
}
diff --git a/core/java/android/widget/PopupWindow.java b/core/java/android/widget/PopupWindow.java
index 2c9714e..78c7bd8 100644
--- a/core/java/android/widget/PopupWindow.java
+++ b/core/java/android/widget/PopupWindow.java
@@ -25,6 +25,7 @@ import android.view.WindowManager;
import android.view.Gravity;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
+import android.view.WindowManagerImpl;
import android.view.ViewTreeObserver.OnScrollChangedListener;
import android.view.View.OnTouchListener;
import android.graphics.PixelFormat;
@@ -72,8 +73,8 @@ public class PopupWindow {
*/
public static final int INPUT_METHOD_NOT_NEEDED = 2;
- private final Context mContext;
- private final WindowManager mWindowManager;
+ private Context mContext;
+ private WindowManager mWindowManager;
private boolean mIsShowing;
private boolean mIsDropdown;
@@ -158,8 +159,7 @@ public class PopupWindow {
*/
public PopupWindow(Context context, AttributeSet attrs, int defStyle) {
mContext = context;
- mWindowManager = (WindowManager)context.getSystemService(
- Context.WINDOW_SERVICE);
+ mWindowManager = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
TypedArray a =
context.obtainStyledAttributes(
@@ -272,11 +272,11 @@ public class PopupWindow {
* @param height the popup's height
* @param focusable true if the popup can be focused, false otherwise
*/
- public PopupWindow(View contentView, int width, int height,
- boolean focusable) {
- mContext = contentView.getContext();
- mWindowManager = (WindowManager)mContext.getSystemService(
- Context.WINDOW_SERVICE);
+ public PopupWindow(View contentView, int width, int height, boolean focusable) {
+ if (contentView != null) {
+ mContext = contentView.getContext();
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ }
setContentView(contentView);
setWidth(width);
setHeight(height);
@@ -373,6 +373,14 @@ public class PopupWindow {
}
mContentView = contentView;
+
+ if (mContext == null) {
+ mContext = mContentView.getContext();
+ }
+
+ if (mWindowManager == null) {
+ mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ }
}
/**
@@ -747,6 +755,11 @@ public class PopupWindow {
* @param p the layout parameters of the popup's content view
*/
private void preparePopup(WindowManager.LayoutParams p) {
+ if (mContentView == null || mContext == null || mWindowManager == null) {
+ throw new IllegalStateException("You must specify a valid content view by "
+ + "calling setContentView() before attempting to show the popup.");
+ }
+
if (mBackground != null) {
final ViewGroup.LayoutParams layoutParams = mContentView.getLayoutParams();
int height = ViewGroup.LayoutParams.FILL_PARENT;
@@ -948,14 +961,38 @@ public class PopupWindow {
* shown.
*/
public int getMaxAvailableHeight(View anchor, int yOffset) {
+ return getMaxAvailableHeight(anchor, yOffset, false);
+ }
+
+ /**
+ * Returns the maximum height that is available for the popup to be
+ * completely shown, optionally ignoring any bottom decorations such as
+ * the input method. It is recommended that this height be the maximum for
+ * the popup's height, otherwise it is possible that the popup will be
+ * clipped.
+ *
+ * @param anchor The view on which the popup window must be anchored.
+ * @param yOffset y offset from the view's bottom edge
+ * @param ignoreBottomDecorations if true, the height returned will be
+ * all the way to the bottom of the display, ignoring any
+ * bottom decorations
+ * @return The maximum available height for the popup to be completely
+ * shown.
+ *
+ * @hide Pending API council approval.
+ */
+ public int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
final Rect displayFrame = new Rect();
anchor.getWindowVisibleDisplayFrame(displayFrame);
final int[] anchorPos = mDrawingLocation;
anchor.getLocationOnScreen(anchorPos);
- final int distanceToBottom = displayFrame.bottom -
- (anchorPos[1] + anchor.getHeight()) - yOffset;
+ int bottomEdge = displayFrame.bottom;
+ if (ignoreBottomDecorations) {
+ bottomEdge = WindowManagerImpl.getDefault().getDefaultDisplay().getHeight();
+ }
+ final int distanceToBottom = bottomEdge - (anchorPos[1] + anchor.getHeight()) - yOffset;
final int distanceToTop = anchorPos[1] - displayFrame.top + yOffset;
// anchorPos[1] is distance from anchor to top of screen
@@ -1116,7 +1153,7 @@ public class PopupWindow {
p.flags = newFlags;
update = true;
}
-
+
if (update) {
mWindowManager.updateViewLayout(mPopupView, p);
}
diff --git a/core/java/android/widget/RelativeLayout.java b/core/java/android/widget/RelativeLayout.java
index c4f0abd..edbb3db 100644
--- a/core/java/android/widget/RelativeLayout.java
+++ b/core/java/android/widget/RelativeLayout.java
@@ -823,7 +823,7 @@ public class RelativeLayout extends ViewGroup {
@ViewDebug.IntToString(from = RIGHT_OF, to = "rightOf")
}, mapping = {
@ViewDebug.IntToString(from = TRUE, to = "true"),
- @ViewDebug.IntToString(from = 0, to = "NO_ID")
+ @ViewDebug.IntToString(from = 0, to = "FALSE/NO_ID")
})
private int[] mRules = new int[VERB_COUNT];
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index c6b0187..adfc74f 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -4434,29 +4434,31 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
boolean reportExtractedText() {
final InputMethodState ims = mInputMethodState;
- final boolean contentChanged = ims.mContentChanged;
- if (ims != null && (contentChanged || ims.mSelectionModeChanged)) {
- ims.mContentChanged = false;
- ims.mSelectionModeChanged = false;
- final ExtractedTextRequest req = mInputMethodState.mExtracting;
- if (req != null) {
- InputMethodManager imm = InputMethodManager.peekInstance();
- if (imm != null) {
- if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start="
- + ims.mChangedStart + " end=" + ims.mChangedEnd
- + " delta=" + ims.mChangedDelta);
- if (ims.mChangedStart < 0 && !contentChanged) {
- ims.mChangedStart = EXTRACT_NOTHING;
- }
- if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
- ims.mChangedDelta, ims.mTmpExtracted)) {
- if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start="
- + ims.mTmpExtracted.partialStartOffset
- + " end=" + ims.mTmpExtracted.partialEndOffset
- + ": " + ims.mTmpExtracted.text);
- imm.updateExtractedText(this, req.token,
- mInputMethodState.mTmpExtracted);
- return true;
+ if (ims != null) {
+ final boolean contentChanged = ims.mContentChanged;
+ if (contentChanged || ims.mSelectionModeChanged) {
+ ims.mContentChanged = false;
+ ims.mSelectionModeChanged = false;
+ final ExtractedTextRequest req = mInputMethodState.mExtracting;
+ if (req != null) {
+ InputMethodManager imm = InputMethodManager.peekInstance();
+ if (imm != null) {
+ if (DEBUG_EXTRACT) Log.v(TAG, "Retrieving extracted start="
+ + ims.mChangedStart + " end=" + ims.mChangedEnd
+ + " delta=" + ims.mChangedDelta);
+ if (ims.mChangedStart < 0 && !contentChanged) {
+ ims.mChangedStart = EXTRACT_NOTHING;
+ }
+ if (extractTextInternal(req, ims.mChangedStart, ims.mChangedEnd,
+ ims.mChangedDelta, ims.mTmpExtracted)) {
+ if (DEBUG_EXTRACT) Log.v(TAG, "Reporting extracted start="
+ + ims.mTmpExtracted.partialStartOffset
+ + " end=" + ims.mTmpExtracted.partialEndOffset
+ + ": " + ims.mTmpExtracted.text);
+ imm.updateExtractedText(this, req.token,
+ mInputMethodState.mTmpExtracted);
+ return true;
+ }
}
}
}