diff options
Diffstat (limited to 'core/java/android/widget')
-rw-r--r-- | core/java/android/widget/AbsListView.java | 91 | ||||
-rw-r--r-- | core/java/android/widget/AutoCompleteTextView.java | 54 | ||||
-rw-r--r-- | core/java/android/widget/ListView.java | 20 | ||||
-rw-r--r-- | core/java/android/widget/PopupWindow.java | 61 | ||||
-rw-r--r-- | core/java/android/widget/RelativeLayout.java | 2 | ||||
-rw-r--r-- | core/java/android/widget/TextView.java | 48 |
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; + } } } } |