mirror of https://github.com/tasks/tasks
You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
645 lines
24 KiB
Java
645 lines
24 KiB
Java
package com.actionbarsherlock.internal.widget;
|
|
|
|
import com.actionbarsherlock.R;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.database.DataSetObserver;
|
|
import android.graphics.Rect;
|
|
import android.graphics.drawable.Drawable;
|
|
import android.os.Build;
|
|
import android.os.Handler;
|
|
import android.util.AttributeSet;
|
|
import android.view.ContextThemeWrapper;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.View.MeasureSpec;
|
|
import android.view.View.OnTouchListener;
|
|
import android.view.ViewGroup;
|
|
import android.view.ViewParent;
|
|
import android.widget.AbsListView;
|
|
import android.widget.AdapterView;
|
|
import android.widget.LinearLayout;
|
|
import android.widget.ListAdapter;
|
|
import android.widget.ListView;
|
|
import android.widget.PopupWindow;
|
|
|
|
/**
|
|
* A proxy between pre- and post-Honeycomb implementations of this class.
|
|
*/
|
|
public class IcsListPopupWindow {
|
|
/**
|
|
* This value controls the length of time that the user
|
|
* must leave a pointer down without scrolling to expand
|
|
* the autocomplete dropdown list to cover the IME.
|
|
*/
|
|
private static final int EXPAND_LIST_TIMEOUT = 250;
|
|
|
|
private Context mContext;
|
|
private PopupWindow mPopup;
|
|
private ListAdapter mAdapter;
|
|
private DropDownListView mDropDownList;
|
|
|
|
private int mDropDownHeight = ViewGroup.LayoutParams.WRAP_CONTENT;
|
|
private int mDropDownWidth = ViewGroup.LayoutParams.WRAP_CONTENT;
|
|
private int mDropDownHorizontalOffset;
|
|
private int mDropDownVerticalOffset;
|
|
private boolean mDropDownVerticalOffsetSet;
|
|
|
|
private int mListItemExpandMaximum = Integer.MAX_VALUE;
|
|
|
|
private View mPromptView;
|
|
private int mPromptPosition = POSITION_PROMPT_ABOVE;
|
|
|
|
private DataSetObserver mObserver;
|
|
|
|
private View mDropDownAnchorView;
|
|
|
|
private Drawable mDropDownListHighlight;
|
|
|
|
private AdapterView.OnItemClickListener mItemClickListener;
|
|
private AdapterView.OnItemSelectedListener mItemSelectedListener;
|
|
|
|
private final ResizePopupRunnable mResizePopupRunnable = new ResizePopupRunnable();
|
|
private final PopupTouchInterceptor mTouchInterceptor = new PopupTouchInterceptor();
|
|
private final PopupScrollListener mScrollListener = new PopupScrollListener();
|
|
private final ListSelectorHider mHideSelector = new ListSelectorHider();
|
|
|
|
private Handler mHandler = new Handler();
|
|
|
|
private Rect mTempRect = new Rect();
|
|
|
|
private boolean mModal;
|
|
|
|
public static final int POSITION_PROMPT_ABOVE = 0;
|
|
public static final int POSITION_PROMPT_BELOW = 1;
|
|
|
|
public IcsListPopupWindow(Context context) {
|
|
this(context, null, R.attr.listPopupWindowStyle);
|
|
}
|
|
|
|
public IcsListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
mContext = context;
|
|
mPopup = new PopupWindow(context, attrs, defStyleAttr);
|
|
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
|
|
}
|
|
|
|
public IcsListPopupWindow(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
|
mContext = context;
|
|
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
|
|
Context wrapped = new ContextThemeWrapper(context, defStyleRes);
|
|
mPopup = new PopupWindow(wrapped, attrs, defStyleAttr);
|
|
} else {
|
|
mPopup = new PopupWindow(context, attrs, defStyleAttr, defStyleRes);
|
|
}
|
|
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED);
|
|
}
|
|
|
|
public void setAdapter(ListAdapter adapter) {
|
|
if (mObserver == null) {
|
|
mObserver = new PopupDataSetObserver();
|
|
} else if (mAdapter != null) {
|
|
mAdapter.unregisterDataSetObserver(mObserver);
|
|
}
|
|
mAdapter = adapter;
|
|
if (mAdapter != null) {
|
|
adapter.registerDataSetObserver(mObserver);
|
|
}
|
|
|
|
if (mDropDownList != null) {
|
|
mDropDownList.setAdapter(mAdapter);
|
|
}
|
|
}
|
|
|
|
public void setPromptPosition(int position) {
|
|
mPromptPosition = position;
|
|
}
|
|
|
|
public void setModal(boolean modal) {
|
|
mModal = true;
|
|
mPopup.setFocusable(modal);
|
|
}
|
|
|
|
public void setBackgroundDrawable(Drawable d) {
|
|
mPopup.setBackgroundDrawable(d);
|
|
}
|
|
|
|
public void setAnchorView(View anchor) {
|
|
mDropDownAnchorView = anchor;
|
|
}
|
|
|
|
public void setHorizontalOffset(int offset) {
|
|
mDropDownHorizontalOffset = offset;
|
|
}
|
|
|
|
public void setVerticalOffset(int offset) {
|
|
mDropDownVerticalOffset = offset;
|
|
mDropDownVerticalOffsetSet = true;
|
|
}
|
|
|
|
public void setContentWidth(int width) {
|
|
Drawable popupBackground = mPopup.getBackground();
|
|
if (popupBackground != null) {
|
|
popupBackground.getPadding(mTempRect);
|
|
mDropDownWidth = mTempRect.left + mTempRect.right + width;
|
|
} else {
|
|
mDropDownWidth = width;
|
|
}
|
|
}
|
|
|
|
public void setOnItemClickListener(AdapterView.OnItemClickListener clickListener) {
|
|
mItemClickListener = clickListener;
|
|
}
|
|
|
|
public void show() {
|
|
int height = buildDropDown();
|
|
|
|
int widthSpec = 0;
|
|
int heightSpec = 0;
|
|
|
|
boolean noInputMethod = isInputMethodNotNeeded();
|
|
//XXX mPopup.setAllowScrollingAnchorParent(!noInputMethod);
|
|
|
|
if (mPopup.isShowing()) {
|
|
if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
|
|
// The call to PopupWindow's update method below can accept -1 for any
|
|
// value you do not want to update.
|
|
widthSpec = -1;
|
|
} else if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
|
widthSpec = mDropDownAnchorView.getWidth();
|
|
} else {
|
|
widthSpec = mDropDownWidth;
|
|
}
|
|
|
|
if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
|
|
// The call to PopupWindow's update method below can accept -1 for any
|
|
// value you do not want to update.
|
|
heightSpec = noInputMethod ? height : ViewGroup.LayoutParams.MATCH_PARENT;
|
|
if (noInputMethod) {
|
|
mPopup.setWindowLayoutMode(
|
|
mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
|
|
ViewGroup.LayoutParams.MATCH_PARENT : 0, 0);
|
|
} else {
|
|
mPopup.setWindowLayoutMode(
|
|
mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT ?
|
|
ViewGroup.LayoutParams.MATCH_PARENT : 0,
|
|
ViewGroup.LayoutParams.MATCH_PARENT);
|
|
}
|
|
} else if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
|
heightSpec = height;
|
|
} else {
|
|
heightSpec = mDropDownHeight;
|
|
}
|
|
|
|
mPopup.setOutsideTouchable(true);
|
|
|
|
mPopup.update(mDropDownAnchorView, mDropDownHorizontalOffset,
|
|
mDropDownVerticalOffset, widthSpec, heightSpec);
|
|
} else {
|
|
if (mDropDownWidth == ViewGroup.LayoutParams.MATCH_PARENT) {
|
|
widthSpec = ViewGroup.LayoutParams.MATCH_PARENT;
|
|
} else {
|
|
if (mDropDownWidth == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
|
mPopup.setWidth(mDropDownAnchorView.getWidth());
|
|
} else {
|
|
mPopup.setWidth(mDropDownWidth);
|
|
}
|
|
}
|
|
|
|
if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
|
|
heightSpec = ViewGroup.LayoutParams.MATCH_PARENT;
|
|
} else {
|
|
if (mDropDownHeight == ViewGroup.LayoutParams.WRAP_CONTENT) {
|
|
mPopup.setHeight(height);
|
|
} else {
|
|
mPopup.setHeight(mDropDownHeight);
|
|
}
|
|
}
|
|
|
|
mPopup.setWindowLayoutMode(widthSpec, heightSpec);
|
|
//XXX mPopup.setClipToScreenEnabled(true);
|
|
|
|
// use outside touchable to dismiss drop down when touching outside of it, so
|
|
// only set this if the dropdown is not always visible
|
|
mPopup.setOutsideTouchable(true);
|
|
mPopup.setTouchInterceptor(mTouchInterceptor);
|
|
mPopup.showAsDropDown(mDropDownAnchorView,
|
|
mDropDownHorizontalOffset, mDropDownVerticalOffset);
|
|
mDropDownList.setSelection(ListView.INVALID_POSITION);
|
|
|
|
if (!mModal || mDropDownList.isInTouchMode()) {
|
|
clearListSelection();
|
|
}
|
|
if (!mModal) {
|
|
mHandler.post(mHideSelector);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void dismiss() {
|
|
mPopup.dismiss();
|
|
if (mPromptView != null) {
|
|
final ViewParent parent = mPromptView.getParent();
|
|
if (parent instanceof ViewGroup) {
|
|
final ViewGroup group = (ViewGroup) parent;
|
|
group.removeView(mPromptView);
|
|
}
|
|
}
|
|
mPopup.setContentView(null);
|
|
mDropDownList = null;
|
|
mHandler.removeCallbacks(mResizePopupRunnable);
|
|
}
|
|
|
|
public void setOnDismissListener(PopupWindow.OnDismissListener listener) {
|
|
mPopup.setOnDismissListener(listener);
|
|
}
|
|
|
|
public void setInputMethodMode(int mode) {
|
|
mPopup.setInputMethodMode(mode);
|
|
}
|
|
|
|
public void clearListSelection() {
|
|
final DropDownListView list = mDropDownList;
|
|
if (list != null) {
|
|
// WARNING: Please read the comment where mListSelectionHidden is declared
|
|
list.mListSelectionHidden = true;
|
|
//XXX list.hideSelector();
|
|
list.requestLayout();
|
|
}
|
|
}
|
|
|
|
public boolean isShowing() {
|
|
return mPopup.isShowing();
|
|
}
|
|
|
|
private boolean isInputMethodNotNeeded() {
|
|
return mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
|
|
}
|
|
|
|
public ListView getListView() {
|
|
return mDropDownList;
|
|
}
|
|
|
|
private int buildDropDown() {
|
|
ViewGroup dropDownView;
|
|
int otherHeights = 0;
|
|
|
|
if (mDropDownList == null) {
|
|
Context context = mContext;
|
|
|
|
mDropDownList = new DropDownListView(context, !mModal);
|
|
if (mDropDownListHighlight != null) {
|
|
mDropDownList.setSelector(mDropDownListHighlight);
|
|
}
|
|
mDropDownList.setAdapter(mAdapter);
|
|
mDropDownList.setOnItemClickListener(mItemClickListener);
|
|
mDropDownList.setFocusable(true);
|
|
mDropDownList.setFocusableInTouchMode(true);
|
|
mDropDownList.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
|
public void onItemSelected(AdapterView<?> parent, View view,
|
|
int position, long id) {
|
|
|
|
if (position != -1) {
|
|
DropDownListView dropDownList = mDropDownList;
|
|
|
|
if (dropDownList != null) {
|
|
dropDownList.mListSelectionHidden = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void onNothingSelected(AdapterView<?> parent) {
|
|
}
|
|
});
|
|
mDropDownList.setOnScrollListener(mScrollListener);
|
|
|
|
if (mItemSelectedListener != null) {
|
|
mDropDownList.setOnItemSelectedListener(mItemSelectedListener);
|
|
}
|
|
|
|
dropDownView = mDropDownList;
|
|
|
|
View hintView = mPromptView;
|
|
if (hintView != null) {
|
|
// if an hint has been specified, we accomodate more space for it and
|
|
// add a text view in the drop down menu, at the bottom of the list
|
|
LinearLayout hintContainer = new LinearLayout(context);
|
|
hintContainer.setOrientation(LinearLayout.VERTICAL);
|
|
|
|
LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
|
|
ViewGroup.LayoutParams.MATCH_PARENT, 0, 1.0f
|
|
);
|
|
|
|
switch (mPromptPosition) {
|
|
case POSITION_PROMPT_BELOW:
|
|
hintContainer.addView(dropDownView, hintParams);
|
|
hintContainer.addView(hintView);
|
|
break;
|
|
|
|
case POSITION_PROMPT_ABOVE:
|
|
hintContainer.addView(hintView);
|
|
hintContainer.addView(dropDownView, hintParams);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// measure the hint's height to find how much more vertical space
|
|
// we need to add to the drop down's height
|
|
int widthSpec = MeasureSpec.makeMeasureSpec(mDropDownWidth, MeasureSpec.AT_MOST);
|
|
int heightSpec = MeasureSpec.UNSPECIFIED;
|
|
hintView.measure(widthSpec, heightSpec);
|
|
|
|
hintParams = (LinearLayout.LayoutParams) hintView.getLayoutParams();
|
|
otherHeights = hintView.getMeasuredHeight() + hintParams.topMargin
|
|
+ hintParams.bottomMargin;
|
|
|
|
dropDownView = hintContainer;
|
|
}
|
|
|
|
mPopup.setContentView(dropDownView);
|
|
} else {
|
|
dropDownView = (ViewGroup) mPopup.getContentView();
|
|
final View view = mPromptView;
|
|
if (view != null) {
|
|
LinearLayout.LayoutParams hintParams =
|
|
(LinearLayout.LayoutParams) view.getLayoutParams();
|
|
otherHeights = view.getMeasuredHeight() + hintParams.topMargin
|
|
+ hintParams.bottomMargin;
|
|
}
|
|
}
|
|
|
|
// getMaxAvailableHeight() subtracts the padding, so we put it back
|
|
// to get the available height for the whole window
|
|
int padding = 0;
|
|
Drawable background = mPopup.getBackground();
|
|
if (background != null) {
|
|
background.getPadding(mTempRect);
|
|
padding = mTempRect.top + mTempRect.bottom;
|
|
|
|
// If we don't have an explicit vertical offset, determine one from the window
|
|
// background so that content will line up.
|
|
if (!mDropDownVerticalOffsetSet) {
|
|
mDropDownVerticalOffset = -mTempRect.top;
|
|
}
|
|
}
|
|
|
|
// Max height available on the screen for a popup.
|
|
boolean ignoreBottomDecorations =
|
|
mPopup.getInputMethodMode() == PopupWindow.INPUT_METHOD_NOT_NEEDED;
|
|
final int maxHeight = /*mPopup.*/getMaxAvailableHeight(
|
|
mDropDownAnchorView, mDropDownVerticalOffset, ignoreBottomDecorations);
|
|
|
|
if (mDropDownHeight == ViewGroup.LayoutParams.MATCH_PARENT) {
|
|
return maxHeight + padding;
|
|
}
|
|
|
|
final int listContent = /*mDropDownList.*/measureHeightOfChildren(MeasureSpec.UNSPECIFIED,
|
|
0, -1/*ListView.NO_POSITION*/, maxHeight - otherHeights, -1);
|
|
// add padding only if the list has items in it, that way we don't show
|
|
// the popup if it is not needed
|
|
if (listContent > 0) otherHeights += padding;
|
|
|
|
return listContent + otherHeights;
|
|
}
|
|
|
|
private int getMaxAvailableHeight(View anchor, int yOffset, boolean ignoreBottomDecorations) {
|
|
final Rect displayFrame = new Rect();
|
|
anchor.getWindowVisibleDisplayFrame(displayFrame);
|
|
|
|
final int[] anchorPos = new int[2];
|
|
anchor.getLocationOnScreen(anchorPos);
|
|
|
|
int bottomEdge = displayFrame.bottom;
|
|
if (ignoreBottomDecorations) {
|
|
Resources res = anchor.getContext().getResources();
|
|
bottomEdge = res.getDisplayMetrics().heightPixels;
|
|
}
|
|
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
|
|
int returnedHeight = Math.max(distanceToBottom, distanceToTop);
|
|
if (mPopup.getBackground() != null) {
|
|
mPopup.getBackground().getPadding(mTempRect);
|
|
returnedHeight -= mTempRect.top + mTempRect.bottom;
|
|
}
|
|
|
|
return returnedHeight;
|
|
}
|
|
|
|
private int measureHeightOfChildren(int widthMeasureSpec, int startPosition, int endPosition,
|
|
final int maxHeight, int disallowPartialChildPosition) {
|
|
|
|
final ListAdapter adapter = mAdapter;
|
|
if (adapter == null) {
|
|
return mDropDownList.getListPaddingTop() + mDropDownList.getListPaddingBottom();
|
|
}
|
|
|
|
// Include the padding of the list
|
|
int returnedHeight = mDropDownList.getListPaddingTop() + mDropDownList.getListPaddingBottom();
|
|
final int dividerHeight = ((mDropDownList.getDividerHeight() > 0) && mDropDownList.getDivider() != null) ? mDropDownList.getDividerHeight() : 0;
|
|
// The previous height value that was less than maxHeight and contained
|
|
// no partial children
|
|
int prevHeightWithoutPartialChild = 0;
|
|
int i;
|
|
View child;
|
|
|
|
// mItemCount - 1 since endPosition parameter is inclusive
|
|
endPosition = (endPosition == -1/*NO_POSITION*/) ? adapter.getCount() - 1 : endPosition;
|
|
|
|
for (i = startPosition; i <= endPosition; ++i) {
|
|
child = mAdapter.getView(i, null, mDropDownList);
|
|
if (mDropDownList.getCacheColorHint() != 0) {
|
|
child.setDrawingCacheBackgroundColor(mDropDownList.getCacheColorHint());
|
|
}
|
|
|
|
measureScrapChild(child, i, widthMeasureSpec);
|
|
|
|
if (i > 0) {
|
|
// Count the divider for all but one child
|
|
returnedHeight += dividerHeight;
|
|
}
|
|
|
|
returnedHeight += child.getMeasuredHeight();
|
|
|
|
if (returnedHeight >= maxHeight) {
|
|
// We went over, figure out which height to return. If returnedHeight > maxHeight,
|
|
// then the i'th position did not fit completely.
|
|
return (disallowPartialChildPosition >= 0) // Disallowing is enabled (> -1)
|
|
&& (i > disallowPartialChildPosition) // We've past the min pos
|
|
&& (prevHeightWithoutPartialChild > 0) // We have a prev height
|
|
&& (returnedHeight != maxHeight) // i'th child did not fit completely
|
|
? prevHeightWithoutPartialChild
|
|
: maxHeight;
|
|
}
|
|
|
|
if ((disallowPartialChildPosition >= 0) && (i >= disallowPartialChildPosition)) {
|
|
prevHeightWithoutPartialChild = returnedHeight;
|
|
}
|
|
}
|
|
|
|
// At this point, we went through the range of children, and they each
|
|
// completely fit, so return the returnedHeight
|
|
return returnedHeight;
|
|
}
|
|
private void measureScrapChild(View child, int position, int widthMeasureSpec) {
|
|
ListView.LayoutParams p = (ListView.LayoutParams) child.getLayoutParams();
|
|
if (p == null) {
|
|
p = new ListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
|
|
ViewGroup.LayoutParams.WRAP_CONTENT, 0);
|
|
child.setLayoutParams(p);
|
|
}
|
|
//XXX p.viewType = mAdapter.getItemViewType(position);
|
|
//XXX p.forceAdd = true;
|
|
|
|
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthMeasureSpec,
|
|
mDropDownList.getPaddingLeft() + mDropDownList.getPaddingRight(), p.width);
|
|
int lpHeight = p.height;
|
|
int childHeightSpec;
|
|
if (lpHeight > 0) {
|
|
childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY);
|
|
} else {
|
|
childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
|
}
|
|
child.measure(childWidthSpec, childHeightSpec);
|
|
}
|
|
|
|
private static class DropDownListView extends ListView {
|
|
/*
|
|
* WARNING: This is a workaround for a touch mode issue.
|
|
*
|
|
* Touch mode is propagated lazily to windows. This causes problems in
|
|
* the following scenario:
|
|
* - Type something in the AutoCompleteTextView and get some results
|
|
* - Move down with the d-pad to select an item in the list
|
|
* - Move up with the d-pad until the selection disappears
|
|
* - Type more text in the AutoCompleteTextView *using the soft keyboard*
|
|
* and get new results; you are now in touch mode
|
|
* - The selection comes back on the first item in the list, even though
|
|
* the list is supposed to be in touch mode
|
|
*
|
|
* Using the soft keyboard triggers the touch mode change but that change
|
|
* is propagated to our window only after the first list layout, therefore
|
|
* after the list attempts to resurrect the selection.
|
|
*
|
|
* The trick to work around this issue is to pretend the list is in touch
|
|
* mode when we know that the selection should not appear, that is when
|
|
* we know the user moved the selection away from the list.
|
|
*
|
|
* This boolean is set to true whenever we explicitly hide the list's
|
|
* selection and reset to false whenever we know the user moved the
|
|
* selection back to the list.
|
|
*
|
|
* When this boolean is true, isInTouchMode() returns true, otherwise it
|
|
* returns super.isInTouchMode().
|
|
*/
|
|
private boolean mListSelectionHidden;
|
|
|
|
private boolean mHijackFocus;
|
|
|
|
public DropDownListView(Context context, boolean hijackFocus) {
|
|
super(context, null, /*com.android.internal.*/R.attr.dropDownListViewStyle);
|
|
mHijackFocus = hijackFocus;
|
|
// TODO: Add an API to control this
|
|
setCacheColorHint(0); // Transparent, since the background drawable could be anything.
|
|
}
|
|
|
|
//XXX @Override
|
|
//View obtainView(int position, boolean[] isScrap) {
|
|
// View view = super.obtainView(position, isScrap);
|
|
|
|
// if (view instanceof TextView) {
|
|
// ((TextView) view).setHorizontallyScrolling(true);
|
|
// }
|
|
|
|
// return view;
|
|
//}
|
|
|
|
@Override
|
|
public boolean isInTouchMode() {
|
|
// WARNING: Please read the comment where mListSelectionHidden is declared
|
|
return (mHijackFocus && mListSelectionHidden) || super.isInTouchMode();
|
|
}
|
|
|
|
@Override
|
|
public boolean hasWindowFocus() {
|
|
return mHijackFocus || super.hasWindowFocus();
|
|
}
|
|
|
|
@Override
|
|
public boolean isFocused() {
|
|
return mHijackFocus || super.isFocused();
|
|
}
|
|
|
|
@Override
|
|
public boolean hasFocus() {
|
|
return mHijackFocus || super.hasFocus();
|
|
}
|
|
}
|
|
|
|
private class PopupDataSetObserver extends DataSetObserver {
|
|
@Override
|
|
public void onChanged() {
|
|
if (isShowing()) {
|
|
// Resize the popup to fit new content
|
|
show();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onInvalidated() {
|
|
dismiss();
|
|
}
|
|
}
|
|
|
|
private class ListSelectorHider implements Runnable {
|
|
public void run() {
|
|
clearListSelection();
|
|
}
|
|
}
|
|
|
|
private class ResizePopupRunnable implements Runnable {
|
|
public void run() {
|
|
if (mDropDownList != null && mDropDownList.getCount() > mDropDownList.getChildCount() &&
|
|
mDropDownList.getChildCount() <= mListItemExpandMaximum) {
|
|
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
|
|
show();
|
|
}
|
|
}
|
|
}
|
|
|
|
private class PopupTouchInterceptor implements OnTouchListener {
|
|
public boolean onTouch(View v, MotionEvent event) {
|
|
final int action = event.getAction();
|
|
final int x = (int) event.getX();
|
|
final int y = (int) event.getY();
|
|
|
|
if (action == MotionEvent.ACTION_DOWN &&
|
|
mPopup != null && mPopup.isShowing() &&
|
|
(x >= 0 && x < mPopup.getWidth() && y >= 0 && y < mPopup.getHeight())) {
|
|
mHandler.postDelayed(mResizePopupRunnable, EXPAND_LIST_TIMEOUT);
|
|
} else if (action == MotionEvent.ACTION_UP) {
|
|
mHandler.removeCallbacks(mResizePopupRunnable);
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private class PopupScrollListener implements ListView.OnScrollListener {
|
|
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
|
|
int totalItemCount) {
|
|
|
|
}
|
|
|
|
public void onScrollStateChanged(AbsListView view, int scrollState) {
|
|
if (scrollState == SCROLL_STATE_TOUCH_SCROLL &&
|
|
!isInputMethodNotNeeded() && mPopup.getContentView() != null) {
|
|
mHandler.removeCallbacks(mResizePopupRunnable);
|
|
mResizePopupRunnable.run();
|
|
}
|
|
}
|
|
}
|
|
}
|