changed location of ActionBarSherlock lib
This commit is contained in:
@@ -0,0 +1,291 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.actionbarsherlock.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.actionbarsherlock.R;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.view.NineViewGroup;
|
||||
import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
|
||||
import com.actionbarsherlock.internal.view.menu.ActionMenuView;
|
||||
|
||||
import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
|
||||
|
||||
public abstract class AbsActionBarView extends NineViewGroup {
|
||||
protected ActionMenuView mMenuView;
|
||||
protected ActionMenuPresenter mActionMenuPresenter;
|
||||
protected ActionBarContainer mSplitView;
|
||||
protected boolean mSplitActionBar;
|
||||
protected boolean mSplitWhenNarrow;
|
||||
protected int mContentHeight;
|
||||
|
||||
final Context mContext;
|
||||
|
||||
protected Animator mVisibilityAnim;
|
||||
protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
|
||||
|
||||
private static final /*Time*/Interpolator sAlphaInterpolator = new DecelerateInterpolator();
|
||||
|
||||
private static final int FADE_DURATION = 200;
|
||||
|
||||
public AbsActionBarView(Context context) {
|
||||
super(context);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public AbsActionBarView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
public AbsActionBarView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/*
|
||||
* Must be public so we can dispatch pre-2.2 via ActionBarImpl.
|
||||
*/
|
||||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
} else if (mMenuView != null) {
|
||||
mMenuView.onConfigurationChanged(newConfig);
|
||||
}
|
||||
|
||||
// Action bar can change size on configuration changes.
|
||||
// Reread the desired height from the theme-specified style.
|
||||
TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
|
||||
R.attr.actionBarStyle, 0);
|
||||
setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
|
||||
a.recycle();
|
||||
if (mSplitWhenNarrow) {
|
||||
setSplitActionBar(getResources_getBoolean(getContext(),
|
||||
R.bool.abs__split_action_bar_is_narrow));
|
||||
}
|
||||
if (mActionMenuPresenter != null) {
|
||||
mActionMenuPresenter.onConfigurationChanged(newConfig);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the bar should be split right now, no questions asked.
|
||||
* @param split true if the bar should split
|
||||
*/
|
||||
public void setSplitActionBar(boolean split) {
|
||||
mSplitActionBar = split;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the bar should split if we enter a narrow screen configuration.
|
||||
* @param splitWhenNarrow true if the bar should check to split after a config change
|
||||
*/
|
||||
public void setSplitWhenNarrow(boolean splitWhenNarrow) {
|
||||
mSplitWhenNarrow = splitWhenNarrow;
|
||||
}
|
||||
|
||||
public void setContentHeight(int height) {
|
||||
mContentHeight = height;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public int getContentHeight() {
|
||||
return mContentHeight;
|
||||
}
|
||||
|
||||
public void setSplitView(ActionBarContainer splitView) {
|
||||
mSplitView = splitView;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Current visibility or if animating, the visibility being animated to.
|
||||
*/
|
||||
public int getAnimatedVisibility() {
|
||||
if (mVisibilityAnim != null) {
|
||||
return mVisAnimListener.mFinalVisibility;
|
||||
}
|
||||
return getVisibility();
|
||||
}
|
||||
|
||||
public void animateToVisibility(int visibility) {
|
||||
if (mVisibilityAnim != null) {
|
||||
mVisibilityAnim.cancel();
|
||||
}
|
||||
if (visibility == VISIBLE) {
|
||||
if (getVisibility() != VISIBLE) {
|
||||
setAlpha(0);
|
||||
if (mSplitView != null && mMenuView != null) {
|
||||
mMenuView.setAlpha(0);
|
||||
}
|
||||
}
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
|
||||
anim.setDuration(FADE_DURATION);
|
||||
anim.setInterpolator(sAlphaInterpolator);
|
||||
if (mSplitView != null && mMenuView != null) {
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 1);
|
||||
splitAnim.setDuration(FADE_DURATION);
|
||||
set.addListener(mVisAnimListener.withFinalVisibility(visibility));
|
||||
set.play(anim).with(splitAnim);
|
||||
set.start();
|
||||
} else {
|
||||
anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
|
||||
anim.start();
|
||||
}
|
||||
} else {
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
|
||||
anim.setDuration(FADE_DURATION);
|
||||
anim.setInterpolator(sAlphaInterpolator);
|
||||
if (mSplitView != null && mMenuView != null) {
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
ObjectAnimator splitAnim = ObjectAnimator.ofFloat(mMenuView, "alpha", 0);
|
||||
splitAnim.setDuration(FADE_DURATION);
|
||||
set.addListener(mVisAnimListener.withFinalVisibility(visibility));
|
||||
set.play(anim).with(splitAnim);
|
||||
set.start();
|
||||
} else {
|
||||
anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
|
||||
anim.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
if (mVisibilityAnim != null) {
|
||||
mVisibilityAnim.end();
|
||||
}
|
||||
super.setVisibility(visibility);
|
||||
}
|
||||
|
||||
public boolean showOverflowMenu() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.showOverflowMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void postShowOverflowMenu() {
|
||||
post(new Runnable() {
|
||||
public void run() {
|
||||
showOverflowMenu();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public boolean hideOverflowMenu() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.hideOverflowMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isOverflowMenuShowing() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.isOverflowMenuShowing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isOverflowReserved() {
|
||||
return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved();
|
||||
}
|
||||
|
||||
public void dismissPopupMenus() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
mActionMenuPresenter.dismissPopupMenus();
|
||||
}
|
||||
}
|
||||
|
||||
protected int measureChildView(View child, int availableWidth, int childSpecHeight,
|
||||
int spacing) {
|
||||
child.measure(MeasureSpec.makeMeasureSpec(availableWidth, MeasureSpec.AT_MOST),
|
||||
childSpecHeight);
|
||||
|
||||
availableWidth -= child.getMeasuredWidth();
|
||||
availableWidth -= spacing;
|
||||
|
||||
return Math.max(0, availableWidth);
|
||||
}
|
||||
|
||||
protected int positionChild(View child, int x, int y, int contentHeight) {
|
||||
int childWidth = child.getMeasuredWidth();
|
||||
int childHeight = child.getMeasuredHeight();
|
||||
int childTop = y + (contentHeight - childHeight) / 2;
|
||||
|
||||
child.layout(x, childTop, x + childWidth, childTop + childHeight);
|
||||
|
||||
return childWidth;
|
||||
}
|
||||
|
||||
protected int positionChildInverse(View child, int x, int y, int contentHeight) {
|
||||
int childWidth = child.getMeasuredWidth();
|
||||
int childHeight = child.getMeasuredHeight();
|
||||
int childTop = y + (contentHeight - childHeight) / 2;
|
||||
|
||||
child.layout(x - childWidth, childTop, x, childTop + childHeight);
|
||||
|
||||
return childWidth;
|
||||
}
|
||||
|
||||
protected class VisibilityAnimListener implements Animator.AnimatorListener {
|
||||
private boolean mCanceled = false;
|
||||
int mFinalVisibility;
|
||||
|
||||
public VisibilityAnimListener withFinalVisibility(int visibility) {
|
||||
mFinalVisibility = visibility;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
setVisibility(VISIBLE);
|
||||
mVisibilityAnim = animation;
|
||||
mCanceled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (mCanceled) return;
|
||||
|
||||
mVisibilityAnim = null;
|
||||
setVisibility(mFinalVisibility);
|
||||
if (mSplitView != null && mMenuView != null) {
|
||||
mMenuView.setVisibility(mFinalVisibility);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.actionbarsherlock.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.actionbarsherlock.R;
|
||||
import com.actionbarsherlock.app.ActionBar;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.widget.NineFrameLayout;
|
||||
|
||||
/**
|
||||
* This class acts as a container for the action bar view and action mode context views.
|
||||
* It applies special styles as needed to help handle animated transitions between them.
|
||||
* @hide
|
||||
*/
|
||||
public class ActionBarContainer extends NineFrameLayout {
|
||||
private boolean mIsTransitioning;
|
||||
private View mTabContainer;
|
||||
private ActionBarView mActionBarView;
|
||||
|
||||
private Drawable mBackground;
|
||||
private Drawable mStackedBackground;
|
||||
private Drawable mSplitBackground;
|
||||
private boolean mIsSplit;
|
||||
private boolean mIsStacked;
|
||||
|
||||
public ActionBarContainer(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ActionBarContainer(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
setBackgroundDrawable(null);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.SherlockActionBar);
|
||||
mBackground = a.getDrawable(R.styleable.SherlockActionBar_background);
|
||||
mStackedBackground = a.getDrawable(
|
||||
R.styleable.SherlockActionBar_backgroundStacked);
|
||||
|
||||
if (getId() == R.id.abs__split_action_bar) {
|
||||
mIsSplit = true;
|
||||
mSplitBackground = a.getDrawable(
|
||||
R.styleable.SherlockActionBar_backgroundSplit);
|
||||
}
|
||||
a.recycle();
|
||||
|
||||
setWillNotDraw(mIsSplit ? mSplitBackground == null :
|
||||
mBackground == null && mStackedBackground == null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mActionBarView = (ActionBarView) findViewById(R.id.abs__action_bar);
|
||||
}
|
||||
|
||||
public void setPrimaryBackground(Drawable bg) {
|
||||
mBackground = bg;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setStackedBackground(Drawable bg) {
|
||||
mStackedBackground = bg;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setSplitBackground(Drawable bg) {
|
||||
mSplitBackground = bg;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the action bar into a "transitioning" state. While transitioning
|
||||
* the bar will block focus and touch from all of its descendants. This
|
||||
* prevents the user from interacting with the bar while it is animating
|
||||
* in or out.
|
||||
*
|
||||
* @param isTransitioning true if the bar is currently transitioning, false otherwise.
|
||||
*/
|
||||
public void setTransitioning(boolean isTransitioning) {
|
||||
mIsTransitioning = isTransitioning;
|
||||
setDescendantFocusability(isTransitioning ? FOCUS_BLOCK_DESCENDANTS
|
||||
: FOCUS_AFTER_DESCENDANTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onInterceptTouchEvent(MotionEvent ev) {
|
||||
return mIsTransitioning || super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
super.onTouchEvent(ev);
|
||||
|
||||
// An action bar always eats touch events.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onHoverEvent(MotionEvent ev) {
|
||||
super.onHoverEvent(ev);
|
||||
|
||||
// An action bar always eats hover events.
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setTabContainer(ScrollingTabContainerView tabView) {
|
||||
if (mTabContainer != null) {
|
||||
removeView(mTabContainer);
|
||||
}
|
||||
mTabContainer = tabView;
|
||||
if (tabView != null) {
|
||||
addView(tabView);
|
||||
final ViewGroup.LayoutParams lp = tabView.getLayoutParams();
|
||||
lp.width = LayoutParams.MATCH_PARENT;
|
||||
lp.height = LayoutParams.WRAP_CONTENT;
|
||||
tabView.setAllowCollapse(false);
|
||||
}
|
||||
}
|
||||
|
||||
public View getTabContainer() {
|
||||
return mTabContainer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw(Canvas canvas) {
|
||||
if (getWidth() == 0 || getHeight() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mIsSplit) {
|
||||
if (mSplitBackground != null) mSplitBackground.draw(canvas);
|
||||
} else {
|
||||
if (mBackground != null) {
|
||||
mBackground.draw(canvas);
|
||||
}
|
||||
if (mStackedBackground != null && mIsStacked) {
|
||||
mStackedBackground.draw(canvas);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//This causes the animation reflection to fail on pre-HC platforms
|
||||
//@Override
|
||||
//public android.view.ActionMode startActionModeForChild(View child, android.view.ActionMode.Callback callback) {
|
||||
// // No starting an action mode for an action bar child! (Where would it go?)
|
||||
// return null;
|
||||
//}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
if (mActionBarView == null) return;
|
||||
|
||||
final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams();
|
||||
final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 :
|
||||
mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin;
|
||||
|
||||
if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
|
||||
final int mode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
if (mode == MeasureSpec.AT_MOST) {
|
||||
final int maxHeight = MeasureSpec.getSize(heightMeasureSpec);
|
||||
setMeasuredDimension(getMeasuredWidth(),
|
||||
Math.min(actionBarViewHeight + mTabContainer.getMeasuredHeight(),
|
||||
maxHeight));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
|
||||
final boolean hasTabs = mTabContainer != null && mTabContainer.getVisibility() != GONE;
|
||||
|
||||
if (mTabContainer != null && mTabContainer.getVisibility() != GONE) {
|
||||
final int containerHeight = getMeasuredHeight();
|
||||
final int tabHeight = mTabContainer.getMeasuredHeight();
|
||||
|
||||
if ((mActionBarView.getDisplayOptions() & ActionBar.DISPLAY_SHOW_HOME) == 0) {
|
||||
// Not showing home, put tabs on top.
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
if (child == mTabContainer) continue;
|
||||
|
||||
if (!mActionBarView.isCollapsed()) {
|
||||
child.offsetTopAndBottom(tabHeight);
|
||||
}
|
||||
}
|
||||
mTabContainer.layout(l, 0, r, tabHeight);
|
||||
} else {
|
||||
mTabContainer.layout(l, containerHeight - tabHeight, r, containerHeight);
|
||||
}
|
||||
}
|
||||
|
||||
boolean needsInvalidate = false;
|
||||
if (mIsSplit) {
|
||||
if (mSplitBackground != null) {
|
||||
mSplitBackground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight());
|
||||
needsInvalidate = true;
|
||||
}
|
||||
} else {
|
||||
if (mBackground != null) {
|
||||
mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(),
|
||||
mActionBarView.getRight(), mActionBarView.getBottom());
|
||||
needsInvalidate = true;
|
||||
}
|
||||
if ((mIsStacked = hasTabs && mStackedBackground != null)) {
|
||||
mStackedBackground.setBounds(mTabContainer.getLeft(), mTabContainer.getTop(),
|
||||
mTabContainer.getRight(), mTabContainer.getBottom());
|
||||
needsInvalidate = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsInvalidate) {
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,518 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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 com.actionbarsherlock.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.actionbarsherlock.R;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Animator.AnimatorListener;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.AnimatorSet;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout;
|
||||
import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
|
||||
import com.actionbarsherlock.internal.view.menu.ActionMenuView;
|
||||
import com.actionbarsherlock.internal.view.menu.MenuBuilder;
|
||||
import com.actionbarsherlock.view.ActionMode;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public class ActionBarContextView extends AbsActionBarView implements AnimatorListener {
|
||||
//UNUSED private static final String TAG = "ActionBarContextView";
|
||||
|
||||
private CharSequence mTitle;
|
||||
private CharSequence mSubtitle;
|
||||
|
||||
private NineLinearLayout mClose;
|
||||
private View mCustomView;
|
||||
private LinearLayout mTitleLayout;
|
||||
private TextView mTitleView;
|
||||
private TextView mSubtitleView;
|
||||
private int mTitleStyleRes;
|
||||
private int mSubtitleStyleRes;
|
||||
private Drawable mSplitBackground;
|
||||
|
||||
private Animator mCurrentAnimation;
|
||||
private boolean mAnimateInOnLayout;
|
||||
private int mAnimationMode;
|
||||
|
||||
private static final int ANIMATE_IDLE = 0;
|
||||
private static final int ANIMATE_IN = 1;
|
||||
private static final int ANIMATE_OUT = 2;
|
||||
|
||||
public ActionBarContextView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public ActionBarContextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.actionModeStyle);
|
||||
}
|
||||
|
||||
public ActionBarContextView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockActionMode, defStyle, 0);
|
||||
setBackgroundDrawable(a.getDrawable(
|
||||
R.styleable.SherlockActionMode_background));
|
||||
mTitleStyleRes = a.getResourceId(
|
||||
R.styleable.SherlockActionMode_titleTextStyle, 0);
|
||||
mSubtitleStyleRes = a.getResourceId(
|
||||
R.styleable.SherlockActionMode_subtitleTextStyle, 0);
|
||||
|
||||
mContentHeight = a.getLayoutDimension(
|
||||
R.styleable.SherlockActionMode_height, 0);
|
||||
|
||||
mSplitBackground = a.getDrawable(
|
||||
R.styleable.SherlockActionMode_backgroundSplit);
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (mActionMenuPresenter != null) {
|
||||
mActionMenuPresenter.hideOverflowMenu();
|
||||
mActionMenuPresenter.hideSubMenus();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSplitActionBar(boolean split) {
|
||||
if (mSplitActionBar != split) {
|
||||
if (mActionMenuPresenter != null) {
|
||||
// Mode is already active; move everything over and adjust the menu itself.
|
||||
final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
if (!split) {
|
||||
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
|
||||
mMenuView.setBackgroundDrawable(null);
|
||||
final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
|
||||
if (oldParent != null) oldParent.removeView(mMenuView);
|
||||
addView(mMenuView, layoutParams);
|
||||
} else {
|
||||
// Allow full screen width in split mode.
|
||||
mActionMenuPresenter.setWidthLimit(
|
||||
getContext().getResources().getDisplayMetrics().widthPixels, true);
|
||||
// No limit to the item count; use whatever will fit.
|
||||
mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
|
||||
// Span the whole width
|
||||
layoutParams.width = LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = mContentHeight;
|
||||
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
|
||||
mMenuView.setBackgroundDrawable(mSplitBackground);
|
||||
final ViewGroup oldParent = (ViewGroup) mMenuView.getParent();
|
||||
if (oldParent != null) oldParent.removeView(mMenuView);
|
||||
mSplitView.addView(mMenuView, layoutParams);
|
||||
}
|
||||
}
|
||||
super.setSplitActionBar(split);
|
||||
}
|
||||
}
|
||||
|
||||
public void setContentHeight(int height) {
|
||||
mContentHeight = height;
|
||||
}
|
||||
|
||||
public void setCustomView(View view) {
|
||||
if (mCustomView != null) {
|
||||
removeView(mCustomView);
|
||||
}
|
||||
mCustomView = view;
|
||||
if (mTitleLayout != null) {
|
||||
removeView(mTitleLayout);
|
||||
mTitleLayout = null;
|
||||
}
|
||||
if (view != null) {
|
||||
addView(view);
|
||||
}
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
mTitle = title;
|
||||
initTitle();
|
||||
}
|
||||
|
||||
public void setSubtitle(CharSequence subtitle) {
|
||||
mSubtitle = subtitle;
|
||||
initTitle();
|
||||
}
|
||||
|
||||
public CharSequence getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public CharSequence getSubtitle() {
|
||||
return mSubtitle;
|
||||
}
|
||||
|
||||
private void initTitle() {
|
||||
if (mTitleLayout == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(getContext());
|
||||
inflater.inflate(R.layout.abs__action_bar_title_item, this);
|
||||
mTitleLayout = (LinearLayout) getChildAt(getChildCount() - 1);
|
||||
mTitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_title);
|
||||
mSubtitleView = (TextView) mTitleLayout.findViewById(R.id.abs__action_bar_subtitle);
|
||||
if (mTitleStyleRes != 0) {
|
||||
mTitleView.setTextAppearance(mContext, mTitleStyleRes);
|
||||
}
|
||||
if (mSubtitleStyleRes != 0) {
|
||||
mSubtitleView.setTextAppearance(mContext, mSubtitleStyleRes);
|
||||
}
|
||||
}
|
||||
|
||||
mTitleView.setText(mTitle);
|
||||
mSubtitleView.setText(mSubtitle);
|
||||
|
||||
final boolean hasTitle = !TextUtils.isEmpty(mTitle);
|
||||
final boolean hasSubtitle = !TextUtils.isEmpty(mSubtitle);
|
||||
mSubtitleView.setVisibility(hasSubtitle ? VISIBLE : GONE);
|
||||
mTitleLayout.setVisibility(hasTitle || hasSubtitle ? VISIBLE : GONE);
|
||||
if (mTitleLayout.getParent() == null) {
|
||||
addView(mTitleLayout);
|
||||
}
|
||||
}
|
||||
|
||||
public void initForMode(final ActionMode mode) {
|
||||
if (mClose == null) {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
mClose = (NineLinearLayout)inflater.inflate(R.layout.abs__action_mode_close_item, this, false);
|
||||
addView(mClose);
|
||||
} else if (mClose.getParent() == null) {
|
||||
addView(mClose);
|
||||
}
|
||||
|
||||
View closeButton = mClose.findViewById(R.id.abs__action_mode_close_button);
|
||||
closeButton.setOnClickListener(new OnClickListener() {
|
||||
public void onClick(View v) {
|
||||
mode.finish();
|
||||
}
|
||||
});
|
||||
|
||||
final MenuBuilder menu = (MenuBuilder) mode.getMenu();
|
||||
if (mActionMenuPresenter != null) {
|
||||
mActionMenuPresenter.dismissPopupMenus();
|
||||
}
|
||||
mActionMenuPresenter = new ActionMenuPresenter(mContext);
|
||||
mActionMenuPresenter.setReserveOverflow(true);
|
||||
|
||||
final LayoutParams layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.MATCH_PARENT);
|
||||
if (!mSplitActionBar) {
|
||||
menu.addMenuPresenter(mActionMenuPresenter);
|
||||
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
|
||||
mMenuView.setBackgroundDrawable(null);
|
||||
addView(mMenuView, layoutParams);
|
||||
} else {
|
||||
// Allow full screen width in split mode.
|
||||
mActionMenuPresenter.setWidthLimit(
|
||||
getContext().getResources().getDisplayMetrics().widthPixels, true);
|
||||
// No limit to the item count; use whatever will fit.
|
||||
mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE);
|
||||
// Span the whole width
|
||||
layoutParams.width = LayoutParams.MATCH_PARENT;
|
||||
layoutParams.height = mContentHeight;
|
||||
menu.addMenuPresenter(mActionMenuPresenter);
|
||||
mMenuView = (ActionMenuView) mActionMenuPresenter.getMenuView(this);
|
||||
mMenuView.setBackgroundDrawable(mSplitBackground);
|
||||
mSplitView.addView(mMenuView, layoutParams);
|
||||
}
|
||||
|
||||
mAnimateInOnLayout = true;
|
||||
}
|
||||
|
||||
public void closeMode() {
|
||||
if (mAnimationMode == ANIMATE_OUT) {
|
||||
// Called again during close; just finish what we were doing.
|
||||
return;
|
||||
}
|
||||
if (mClose == null) {
|
||||
killMode();
|
||||
return;
|
||||
}
|
||||
|
||||
finishAnimation();
|
||||
mAnimationMode = ANIMATE_OUT;
|
||||
mCurrentAnimation = makeOutAnimation();
|
||||
mCurrentAnimation.start();
|
||||
}
|
||||
|
||||
private void finishAnimation() {
|
||||
final Animator a = mCurrentAnimation;
|
||||
if (a != null) {
|
||||
mCurrentAnimation = null;
|
||||
a.end();
|
||||
}
|
||||
}
|
||||
|
||||
public void killMode() {
|
||||
finishAnimation();
|
||||
removeAllViews();
|
||||
if (mSplitView != null) {
|
||||
mSplitView.removeView(mMenuView);
|
||||
}
|
||||
mCustomView = null;
|
||||
mMenuView = null;
|
||||
mAnimateInOnLayout = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean showOverflowMenu() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.showOverflowMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hideOverflowMenu() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.hideOverflowMenu();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOverflowMenuShowing() {
|
||||
if (mActionMenuPresenter != null) {
|
||||
return mActionMenuPresenter.isOverflowMenuShowing();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||
// Used by custom views if they don't supply layout params. Everything else
|
||||
// added to an ActionBarContextView should have them already.
|
||||
return new MarginLayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new MarginLayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
if (widthMode != MeasureSpec.EXACTLY) {
|
||||
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
|
||||
"with android:layout_width=\"match_parent\" (or fill_parent)");
|
||||
}
|
||||
|
||||
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
||||
if (heightMode == MeasureSpec.UNSPECIFIED) {
|
||||
throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
|
||||
"with android:layout_height=\"wrap_content\"");
|
||||
}
|
||||
|
||||
final int contentWidth = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
int maxHeight = mContentHeight > 0 ?
|
||||
mContentHeight : MeasureSpec.getSize(heightMeasureSpec);
|
||||
|
||||
final int verticalPadding = getPaddingTop() + getPaddingBottom();
|
||||
int availableWidth = contentWidth - getPaddingLeft() - getPaddingRight();
|
||||
final int height = maxHeight - verticalPadding;
|
||||
final int childSpecHeight = MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST);
|
||||
|
||||
if (mClose != null) {
|
||||
availableWidth = measureChildView(mClose, availableWidth, childSpecHeight, 0);
|
||||
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
|
||||
availableWidth -= lp.leftMargin + lp.rightMargin;
|
||||
}
|
||||
|
||||
if (mMenuView != null && mMenuView.getParent() == this) {
|
||||
availableWidth = measureChildView(mMenuView, availableWidth,
|
||||
childSpecHeight, 0);
|
||||
}
|
||||
|
||||
if (mTitleLayout != null && mCustomView == null) {
|
||||
availableWidth = measureChildView(mTitleLayout, availableWidth, childSpecHeight, 0);
|
||||
}
|
||||
|
||||
if (mCustomView != null) {
|
||||
ViewGroup.LayoutParams lp = mCustomView.getLayoutParams();
|
||||
final int customWidthMode = lp.width != LayoutParams.WRAP_CONTENT ?
|
||||
MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
|
||||
final int customWidth = lp.width >= 0 ?
|
||||
Math.min(lp.width, availableWidth) : availableWidth;
|
||||
final int customHeightMode = lp.height != LayoutParams.WRAP_CONTENT ?
|
||||
MeasureSpec.EXACTLY : MeasureSpec.AT_MOST;
|
||||
final int customHeight = lp.height >= 0 ?
|
||||
Math.min(lp.height, height) : height;
|
||||
mCustomView.measure(MeasureSpec.makeMeasureSpec(customWidth, customWidthMode),
|
||||
MeasureSpec.makeMeasureSpec(customHeight, customHeightMode));
|
||||
}
|
||||
|
||||
if (mContentHeight <= 0) {
|
||||
int measuredHeight = 0;
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
View v = getChildAt(i);
|
||||
int paddedViewHeight = v.getMeasuredHeight() + verticalPadding;
|
||||
if (paddedViewHeight > measuredHeight) {
|
||||
measuredHeight = paddedViewHeight;
|
||||
}
|
||||
}
|
||||
setMeasuredDimension(contentWidth, measuredHeight);
|
||||
} else {
|
||||
setMeasuredDimension(contentWidth, maxHeight);
|
||||
}
|
||||
}
|
||||
|
||||
private Animator makeInAnimation() {
|
||||
mClose.setTranslationX(-mClose.getWidth() -
|
||||
((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
|
||||
ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX", 0);
|
||||
buttonAnimator.setDuration(200);
|
||||
buttonAnimator.addListener(this);
|
||||
buttonAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
AnimatorSet.Builder b = set.play(buttonAnimator);
|
||||
|
||||
if (mMenuView != null) {
|
||||
final int count = mMenuView.getChildCount();
|
||||
if (count > 0) {
|
||||
for (int i = count - 1, j = 0; i >= 0; i--, j++) {
|
||||
AnimatorProxy child = AnimatorProxy.wrap(mMenuView.getChildAt(i));
|
||||
child.setScaleY(0);
|
||||
ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1);
|
||||
a.setDuration(100);
|
||||
a.setStartDelay(j * 70);
|
||||
b.with(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
private Animator makeOutAnimation() {
|
||||
ObjectAnimator buttonAnimator = ObjectAnimator.ofFloat(mClose, "translationX",
|
||||
-mClose.getWidth() - ((MarginLayoutParams) mClose.getLayoutParams()).leftMargin);
|
||||
buttonAnimator.setDuration(200);
|
||||
buttonAnimator.addListener(this);
|
||||
buttonAnimator.setInterpolator(new DecelerateInterpolator());
|
||||
|
||||
AnimatorSet set = new AnimatorSet();
|
||||
AnimatorSet.Builder b = set.play(buttonAnimator);
|
||||
|
||||
if (mMenuView != null) {
|
||||
final int count = mMenuView.getChildCount();
|
||||
if (count > 0) {
|
||||
for (int i = 0; i < 0; i++) {
|
||||
AnimatorProxy child = AnimatorProxy.wrap(mMenuView.getChildAt(i));
|
||||
child.setScaleY(0);
|
||||
ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0);
|
||||
a.setDuration(100);
|
||||
a.setStartDelay(i * 70);
|
||||
b.with(a);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return set;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
int x = getPaddingLeft();
|
||||
final int y = getPaddingTop();
|
||||
final int contentHeight = b - t - getPaddingTop() - getPaddingBottom();
|
||||
|
||||
if (mClose != null && mClose.getVisibility() != GONE) {
|
||||
MarginLayoutParams lp = (MarginLayoutParams) mClose.getLayoutParams();
|
||||
x += lp.leftMargin;
|
||||
x += positionChild(mClose, x, y, contentHeight);
|
||||
x += lp.rightMargin;
|
||||
|
||||
if (mAnimateInOnLayout) {
|
||||
mAnimationMode = ANIMATE_IN;
|
||||
mCurrentAnimation = makeInAnimation();
|
||||
mCurrentAnimation.start();
|
||||
mAnimateInOnLayout = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (mTitleLayout != null && mCustomView == null) {
|
||||
x += positionChild(mTitleLayout, x, y, contentHeight);
|
||||
}
|
||||
|
||||
if (mCustomView != null) {
|
||||
x += positionChild(mCustomView, x, y, contentHeight);
|
||||
}
|
||||
|
||||
x = r - l - getPaddingRight();
|
||||
|
||||
if (mMenuView != null) {
|
||||
x -= positionChildInverse(mMenuView, x, y, contentHeight);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (mAnimationMode == ANIMATE_OUT) {
|
||||
killMode();
|
||||
}
|
||||
mAnimationMode = ANIMATE_IDLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldDelayChildPressedState() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
|
||||
if (event.getEventType() == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
|
||||
// Action mode started
|
||||
//TODO event.setSource(this);
|
||||
event.setClassName(getClass().getName());
|
||||
event.setPackageName(getContext().getPackageName());
|
||||
event.setContentDescription(mTitle);
|
||||
} else {
|
||||
//TODO super.onInitializeAccessibilityEvent(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,40 @@
|
||||
package com.actionbarsherlock.internal.widget;
|
||||
|
||||
import java.util.Locale;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.Button;
|
||||
|
||||
public class CapitalizingButton extends Button {
|
||||
private static final boolean SANS_ICE_CREAM = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
private static final boolean IS_GINGERBREAD = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
|
||||
|
||||
private static final int[] R_styleable_Button = new int[] {
|
||||
android.R.attr.textAllCaps
|
||||
};
|
||||
private static final int R_styleable_Button_textAllCaps = 0;
|
||||
|
||||
private boolean mAllCaps;
|
||||
|
||||
public CapitalizingButton(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R_styleable_Button);
|
||||
mAllCaps = a.getBoolean(R_styleable_Button_textAllCaps, true);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void setTextCompat(CharSequence text) {
|
||||
if (SANS_ICE_CREAM && mAllCaps && text != null) {
|
||||
if (IS_GINGERBREAD) {
|
||||
setText(text.toString().toUpperCase(Locale.ROOT));
|
||||
} else {
|
||||
setText(text.toString().toUpperCase());
|
||||
}
|
||||
} else {
|
||||
setText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.actionbarsherlock.internal.widget;
|
||||
|
||||
import java.util.Locale;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class CapitalizingTextView extends TextView {
|
||||
private static final boolean SANS_ICE_CREAM = Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH;
|
||||
private static final boolean IS_GINGERBREAD = Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD;
|
||||
|
||||
private static final int[] R_styleable_TextView = new int[] {
|
||||
android.R.attr.textAllCaps
|
||||
};
|
||||
private static final int R_styleable_TextView_textAllCaps = 0;
|
||||
|
||||
private boolean mAllCaps;
|
||||
|
||||
public CapitalizingTextView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public CapitalizingTextView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R_styleable_TextView, defStyle, 0);
|
||||
mAllCaps = a.getBoolean(R_styleable_TextView_textAllCaps, true);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void setTextCompat(CharSequence text) {
|
||||
if (SANS_ICE_CREAM && mAllCaps && text != null) {
|
||||
if (IS_GINGERBREAD) {
|
||||
setText(text.toString().toUpperCase(Locale.ROOT));
|
||||
} else {
|
||||
setText(text.toString().toUpperCase());
|
||||
}
|
||||
} else {
|
||||
setText(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.actionbarsherlock.internal.widget;
|
||||
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.util.TypedValue;
|
||||
import android.widget.LinearLayout;
|
||||
import com.actionbarsherlock.R;
|
||||
|
||||
public class FakeDialogPhoneWindow extends LinearLayout {
|
||||
final TypedValue mMinWidthMajor = new TypedValue();
|
||||
final TypedValue mMinWidthMinor = new TypedValue();
|
||||
|
||||
public FakeDialogPhoneWindow(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SherlockTheme);
|
||||
|
||||
a.getValue(R.styleable.SherlockTheme_windowMinWidthMajor, mMinWidthMajor);
|
||||
a.getValue(R.styleable.SherlockTheme_windowMinWidthMinor, mMinWidthMinor);
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
/* Stolen from PhoneWindow */
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
|
||||
final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
|
||||
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
int width = getMeasuredWidth();
|
||||
boolean measure = false;
|
||||
|
||||
widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
|
||||
|
||||
final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor;
|
||||
|
||||
if (tv.type != TypedValue.TYPE_NULL) {
|
||||
final int min;
|
||||
if (tv.type == TypedValue.TYPE_DIMENSION) {
|
||||
min = (int)tv.getDimension(metrics);
|
||||
} else if (tv.type == TypedValue.TYPE_FRACTION) {
|
||||
min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
|
||||
} else {
|
||||
min = 0;
|
||||
}
|
||||
|
||||
if (width < min) {
|
||||
widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
|
||||
measure = true;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Support height?
|
||||
|
||||
if (measure) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* 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 com.actionbarsherlock.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.DataSetObserver;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.SpinnerAdapter;
|
||||
|
||||
/**
|
||||
* An abstract base class for spinner widgets. SDK users will probably not
|
||||
* need to use this class.
|
||||
*
|
||||
* @attr ref android.R.styleable#AbsSpinner_entries
|
||||
*/
|
||||
public abstract class IcsAbsSpinner extends IcsAdapterView<SpinnerAdapter> {
|
||||
private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
|
||||
|
||||
SpinnerAdapter mAdapter;
|
||||
|
||||
int mHeightMeasureSpec;
|
||||
int mWidthMeasureSpec;
|
||||
boolean mBlockLayoutRequests;
|
||||
|
||||
int mSelectionLeftPadding = 0;
|
||||
int mSelectionTopPadding = 0;
|
||||
int mSelectionRightPadding = 0;
|
||||
int mSelectionBottomPadding = 0;
|
||||
final Rect mSpinnerPadding = new Rect();
|
||||
|
||||
final RecycleBin mRecycler = new RecycleBin();
|
||||
private DataSetObserver mDataSetObserver;
|
||||
|
||||
/** Temporary frame to hold a child View's frame rectangle */
|
||||
private Rect mTouchFrame;
|
||||
|
||||
public IcsAbsSpinner(Context context) {
|
||||
super(context);
|
||||
initAbsSpinner();
|
||||
}
|
||||
|
||||
public IcsAbsSpinner(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public IcsAbsSpinner(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
initAbsSpinner();
|
||||
|
||||
/*
|
||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||
com.android.internal.R.styleable.AbsSpinner, defStyle, 0);
|
||||
|
||||
CharSequence[] entries = a.getTextArray(R.styleable.AbsSpinner_entries);
|
||||
if (entries != null) {
|
||||
ArrayAdapter<CharSequence> adapter =
|
||||
new ArrayAdapter<CharSequence>(context,
|
||||
R.layout.simple_spinner_item, entries);
|
||||
adapter.setDropDownViewResource(R.layout.simple_spinner_dropdown_item);
|
||||
setAdapter(adapter);
|
||||
}
|
||||
|
||||
a.recycle();
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Common code for different constructor flavors
|
||||
*/
|
||||
private void initAbsSpinner() {
|
||||
setFocusable(true);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* The Adapter is used to provide the data which backs this Spinner.
|
||||
* It also provides methods to transform spinner items based on their position
|
||||
* relative to the selected item.
|
||||
* @param adapter The SpinnerAdapter to use for this Spinner
|
||||
*/
|
||||
@Override
|
||||
public void setAdapter(SpinnerAdapter adapter) {
|
||||
if (null != mAdapter) {
|
||||
mAdapter.unregisterDataSetObserver(mDataSetObserver);
|
||||
resetList();
|
||||
}
|
||||
|
||||
mAdapter = adapter;
|
||||
|
||||
mOldSelectedPosition = INVALID_POSITION;
|
||||
mOldSelectedRowId = INVALID_ROW_ID;
|
||||
|
||||
if (mAdapter != null) {
|
||||
mOldItemCount = mItemCount;
|
||||
mItemCount = mAdapter.getCount();
|
||||
checkFocus();
|
||||
|
||||
mDataSetObserver = new AdapterDataSetObserver();
|
||||
mAdapter.registerDataSetObserver(mDataSetObserver);
|
||||
|
||||
int position = mItemCount > 0 ? 0 : INVALID_POSITION;
|
||||
|
||||
setSelectedPositionInt(position);
|
||||
setNextSelectedPositionInt(position);
|
||||
|
||||
if (mItemCount == 0) {
|
||||
// Nothing selected
|
||||
checkSelectionChanged();
|
||||
}
|
||||
|
||||
} else {
|
||||
checkFocus();
|
||||
resetList();
|
||||
// Nothing selected
|
||||
checkSelectionChanged();
|
||||
}
|
||||
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear out all children from the list
|
||||
*/
|
||||
void resetList() {
|
||||
mDataChanged = false;
|
||||
mNeedSync = false;
|
||||
|
||||
removeAllViewsInLayout();
|
||||
mOldSelectedPosition = INVALID_POSITION;
|
||||
mOldSelectedRowId = INVALID_ROW_ID;
|
||||
|
||||
setSelectedPositionInt(INVALID_POSITION);
|
||||
setNextSelectedPositionInt(INVALID_POSITION);
|
||||
invalidate();
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.view.View#measure(int, int)
|
||||
*
|
||||
* Figure out the dimensions of this Spinner. The width comes from
|
||||
* the widthMeasureSpec as Spinnners can't have their width set to
|
||||
* UNSPECIFIED. The height is based on the height of the selected item
|
||||
* plus padding.
|
||||
*/
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
int widthSize;
|
||||
int heightSize;
|
||||
|
||||
final int mPaddingLeft = getPaddingLeft();
|
||||
final int mPaddingTop = getPaddingTop();
|
||||
final int mPaddingRight = getPaddingRight();
|
||||
final int mPaddingBottom = getPaddingBottom();
|
||||
|
||||
mSpinnerPadding.left = mPaddingLeft > mSelectionLeftPadding ? mPaddingLeft
|
||||
: mSelectionLeftPadding;
|
||||
mSpinnerPadding.top = mPaddingTop > mSelectionTopPadding ? mPaddingTop
|
||||
: mSelectionTopPadding;
|
||||
mSpinnerPadding.right = mPaddingRight > mSelectionRightPadding ? mPaddingRight
|
||||
: mSelectionRightPadding;
|
||||
mSpinnerPadding.bottom = mPaddingBottom > mSelectionBottomPadding ? mPaddingBottom
|
||||
: mSelectionBottomPadding;
|
||||
|
||||
if (mDataChanged) {
|
||||
handleDataChanged();
|
||||
}
|
||||
|
||||
int preferredHeight = 0;
|
||||
int preferredWidth = 0;
|
||||
boolean needsMeasuring = true;
|
||||
|
||||
int selectedPosition = getSelectedItemPosition();
|
||||
if (selectedPosition >= 0 && mAdapter != null && selectedPosition < mAdapter.getCount()) {
|
||||
// Try looking in the recycler. (Maybe we were measured once already)
|
||||
View view = mRecycler.get(selectedPosition);
|
||||
if (view == null) {
|
||||
// Make a new one
|
||||
view = mAdapter.getView(selectedPosition, null, this);
|
||||
}
|
||||
|
||||
if (view != null) {
|
||||
// Put in recycler for re-measuring and/or layout
|
||||
mRecycler.put(selectedPosition, view);
|
||||
}
|
||||
|
||||
if (view != null) {
|
||||
if (view.getLayoutParams() == null) {
|
||||
mBlockLayoutRequests = true;
|
||||
view.setLayoutParams(generateDefaultLayoutParams());
|
||||
mBlockLayoutRequests = false;
|
||||
}
|
||||
measureChild(view, widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
preferredHeight = getChildHeight(view) + mSpinnerPadding.top + mSpinnerPadding.bottom;
|
||||
preferredWidth = getChildWidth(view) + mSpinnerPadding.left + mSpinnerPadding.right;
|
||||
|
||||
needsMeasuring = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (needsMeasuring) {
|
||||
// No views -- just use padding
|
||||
preferredHeight = mSpinnerPadding.top + mSpinnerPadding.bottom;
|
||||
if (widthMode == MeasureSpec.UNSPECIFIED) {
|
||||
preferredWidth = mSpinnerPadding.left + mSpinnerPadding.right;
|
||||
}
|
||||
}
|
||||
|
||||
preferredHeight = Math.max(preferredHeight, getSuggestedMinimumHeight());
|
||||
preferredWidth = Math.max(preferredWidth, getSuggestedMinimumWidth());
|
||||
|
||||
if (IS_HONEYCOMB) {
|
||||
heightSize = resolveSizeAndState(preferredHeight, heightMeasureSpec, 0);
|
||||
widthSize = resolveSizeAndState(preferredWidth, widthMeasureSpec, 0);
|
||||
} else {
|
||||
heightSize = resolveSize(preferredHeight, heightMeasureSpec);
|
||||
widthSize = resolveSize(preferredWidth, widthMeasureSpec);
|
||||
}
|
||||
|
||||
setMeasuredDimension(widthSize, heightSize);
|
||||
mHeightMeasureSpec = heightMeasureSpec;
|
||||
mWidthMeasureSpec = widthMeasureSpec;
|
||||
}
|
||||
|
||||
int getChildHeight(View child) {
|
||||
return child.getMeasuredHeight();
|
||||
}
|
||||
|
||||
int getChildWidth(View child) {
|
||||
return child.getMeasuredWidth();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
|
||||
return new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
}
|
||||
|
||||
void recycleAllViews() {
|
||||
final int childCount = getChildCount();
|
||||
final IcsAbsSpinner.RecycleBin recycleBin = mRecycler;
|
||||
final int position = mFirstPosition;
|
||||
|
||||
// All views go in recycler
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
View v = getChildAt(i);
|
||||
int index = position + i;
|
||||
recycleBin.put(index, v);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Jump directly to a specific item in the adapter data.
|
||||
*/
|
||||
public void setSelection(int position, boolean animate) {
|
||||
// Animate only if requested position is already on screen somewhere
|
||||
boolean shouldAnimate = animate && mFirstPosition <= position &&
|
||||
position <= mFirstPosition + getChildCount() - 1;
|
||||
setSelectionInt(position, shouldAnimate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSelection(int position) {
|
||||
setNextSelectedPositionInt(position);
|
||||
requestLayout();
|
||||
invalidate();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Makes the item at the supplied position selected.
|
||||
*
|
||||
* @param position Position to select
|
||||
* @param animate Should the transition be animated
|
||||
*
|
||||
*/
|
||||
void setSelectionInt(int position, boolean animate) {
|
||||
if (position != mOldSelectedPosition) {
|
||||
mBlockLayoutRequests = true;
|
||||
int delta = position - mSelectedPosition;
|
||||
setNextSelectedPositionInt(position);
|
||||
layout(delta, animate);
|
||||
mBlockLayoutRequests = false;
|
||||
}
|
||||
}
|
||||
|
||||
abstract void layout(int delta, boolean animate);
|
||||
|
||||
@Override
|
||||
public View getSelectedView() {
|
||||
if (mItemCount > 0 && mSelectedPosition >= 0) {
|
||||
return getChildAt(mSelectedPosition - mFirstPosition);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override to prevent spamming ourselves with layout requests
|
||||
* as we place views
|
||||
*
|
||||
* @see android.view.View#requestLayout()
|
||||
*/
|
||||
@Override
|
||||
public void requestLayout() {
|
||||
if (!mBlockLayoutRequests) {
|
||||
super.requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpinnerAdapter getAdapter() {
|
||||
return mAdapter;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mItemCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Maps a point to a position in the list.
|
||||
*
|
||||
* @param x X in local coordinate
|
||||
* @param y Y in local coordinate
|
||||
* @return The position of the item which contains the specified point, or
|
||||
* {@link #INVALID_POSITION} if the point does not intersect an item.
|
||||
*/
|
||||
public int pointToPosition(int x, int y) {
|
||||
Rect frame = mTouchFrame;
|
||||
if (frame == null) {
|
||||
mTouchFrame = new Rect();
|
||||
frame = mTouchFrame;
|
||||
}
|
||||
|
||||
final int count = getChildCount();
|
||||
for (int i = count - 1; i >= 0; i--) {
|
||||
View child = getChildAt(i);
|
||||
if (child.getVisibility() == View.VISIBLE) {
|
||||
child.getHitRect(frame);
|
||||
if (frame.contains(x, y)) {
|
||||
return mFirstPosition + i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return INVALID_POSITION;
|
||||
}
|
||||
|
||||
static class SavedState extends BaseSavedState {
|
||||
long selectedId;
|
||||
int position;
|
||||
|
||||
/**
|
||||
* Constructor called from {@link AbsSpinner#onSaveInstanceState()}
|
||||
*/
|
||||
SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor called from {@link #CREATOR}
|
||||
*/
|
||||
private SavedState(Parcel in) {
|
||||
super(in);
|
||||
selectedId = in.readLong();
|
||||
position = in.readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel out, int flags) {
|
||||
super.writeToParcel(out, flags);
|
||||
out.writeLong(selectedId);
|
||||
out.writeInt(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbsSpinner.SavedState{"
|
||||
+ Integer.toHexString(System.identityHashCode(this))
|
||||
+ " selectedId=" + selectedId
|
||||
+ " position=" + position + "}";
|
||||
}
|
||||
|
||||
public static final Parcelable.Creator<SavedState> CREATOR
|
||||
= new Parcelable.Creator<SavedState>() {
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Parcelable onSaveInstanceState() {
|
||||
Parcelable superState = super.onSaveInstanceState();
|
||||
SavedState ss = new SavedState(superState);
|
||||
ss.selectedId = getSelectedItemId();
|
||||
if (ss.selectedId >= 0) {
|
||||
ss.position = getSelectedItemPosition();
|
||||
} else {
|
||||
ss.position = INVALID_POSITION;
|
||||
}
|
||||
return ss;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRestoreInstanceState(Parcelable state) {
|
||||
SavedState ss = (SavedState) state;
|
||||
|
||||
super.onRestoreInstanceState(ss.getSuperState());
|
||||
|
||||
if (ss.selectedId >= 0) {
|
||||
mDataChanged = true;
|
||||
mNeedSync = true;
|
||||
mSyncRowId = ss.selectedId;
|
||||
mSyncPosition = ss.position;
|
||||
mSyncMode = SYNC_SELECTED_POSITION;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
class RecycleBin {
|
||||
private final SparseArray<View> mScrapHeap = new SparseArray<View>();
|
||||
|
||||
public void put(int position, View v) {
|
||||
mScrapHeap.put(position, v);
|
||||
}
|
||||
|
||||
View get(int position) {
|
||||
// System.out.print("Looking for " + position);
|
||||
View result = mScrapHeap.get(position);
|
||||
if (result != null) {
|
||||
// System.out.println(" HIT");
|
||||
mScrapHeap.delete(position);
|
||||
} else {
|
||||
// System.out.println(" MISS");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
final SparseArray<View> scrapHeap = mScrapHeap;
|
||||
final int count = scrapHeap.size();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View view = scrapHeap.valueAt(i);
|
||||
if (view != null) {
|
||||
removeDetachedView(view, true);
|
||||
}
|
||||
}
|
||||
scrapHeap.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,272 @@
|
||||
package com.actionbarsherlock.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.widget.NineLinearLayout;
|
||||
|
||||
/**
|
||||
* A simple extension of a regular linear layout that supports the divider API
|
||||
* of Android 4.0+. The dividers are added adjacent to the children by changing
|
||||
* their layout params. If you need to rely on the margins which fall in the
|
||||
* same orientation as the layout you should wrap the child in a simple
|
||||
* {@link android.widget.FrameLayout} so it can receive the margin.
|
||||
*/
|
||||
public class IcsLinearLayout extends NineLinearLayout {
|
||||
private static final int[] LinearLayout = new int[] {
|
||||
/* 0 */ android.R.attr.divider,
|
||||
/* 1 */ android.R.attr.showDividers,
|
||||
/* 2 */ android.R.attr.dividerPadding,
|
||||
};
|
||||
private static final int LinearLayout_divider = 0;
|
||||
private static final int LinearLayout_showDividers = 1;
|
||||
private static final int LinearLayout_dividerPadding = 2;
|
||||
|
||||
/**
|
||||
* Don't show any dividers.
|
||||
*/
|
||||
public static final int SHOW_DIVIDER_NONE = 0;
|
||||
/**
|
||||
* Show a divider at the beginning of the group.
|
||||
*/
|
||||
public static final int SHOW_DIVIDER_BEGINNING = 1;
|
||||
/**
|
||||
* Show dividers between each item in the group.
|
||||
*/
|
||||
public static final int SHOW_DIVIDER_MIDDLE = 2;
|
||||
/**
|
||||
* Show a divider at the end of the group.
|
||||
*/
|
||||
public static final int SHOW_DIVIDER_END = 4;
|
||||
|
||||
|
||||
private Drawable mDivider;
|
||||
private int mDividerWidth;
|
||||
private int mDividerHeight;
|
||||
private int mShowDividers;
|
||||
private int mDividerPadding;
|
||||
|
||||
|
||||
public IcsLinearLayout(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, /*com.android.internal.R.styleable.*/LinearLayout);
|
||||
|
||||
setDividerDrawable(a.getDrawable(/*com.android.internal.R.styleable.*/LinearLayout_divider));
|
||||
mShowDividers = a.getInt(/*com.android.internal.R.styleable.*/LinearLayout_showDividers, SHOW_DIVIDER_NONE);
|
||||
mDividerPadding = a.getDimensionPixelSize(/*com.android.internal.R.styleable.*/LinearLayout_dividerPadding, 0);
|
||||
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set how dividers should be shown between items in this layout
|
||||
*
|
||||
* @param showDividers One or more of {@link #SHOW_DIVIDER_BEGINNING},
|
||||
* {@link #SHOW_DIVIDER_MIDDLE}, or {@link #SHOW_DIVIDER_END},
|
||||
* or {@link #SHOW_DIVIDER_NONE} to show no dividers.
|
||||
*/
|
||||
public void setShowDividers(int showDividers) {
|
||||
if (showDividers != mShowDividers) {
|
||||
requestLayout();
|
||||
invalidate(); //XXX This is required if you are toggling a divider off
|
||||
}
|
||||
mShowDividers = showDividers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A flag set indicating how dividers should be shown around items.
|
||||
* @see #setShowDividers(int)
|
||||
*/
|
||||
public int getShowDividers() {
|
||||
return mShowDividers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a drawable to be used as a divider between items.
|
||||
* @param divider Drawable that will divide each item.
|
||||
* @see #setShowDividers(int)
|
||||
*/
|
||||
public void setDividerDrawable(Drawable divider) {
|
||||
if (divider == mDivider) {
|
||||
return;
|
||||
}
|
||||
mDivider = divider;
|
||||
if (divider != null) {
|
||||
mDividerWidth = divider.getIntrinsicWidth();
|
||||
mDividerHeight = divider.getIntrinsicHeight();
|
||||
} else {
|
||||
mDividerWidth = 0;
|
||||
mDividerHeight = 0;
|
||||
}
|
||||
setWillNotDraw(divider == null);
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set padding displayed on both ends of dividers.
|
||||
*
|
||||
* @param padding Padding value in pixels that will be applied to each end
|
||||
*
|
||||
* @see #setShowDividers(int)
|
||||
* @see #setDividerDrawable(Drawable)
|
||||
* @see #getDividerPadding()
|
||||
*/
|
||||
public void setDividerPadding(int padding) {
|
||||
mDividerPadding = padding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the padding size used to inset dividers in pixels
|
||||
*
|
||||
* @see #setShowDividers(int)
|
||||
* @see #setDividerDrawable(Drawable)
|
||||
* @see #setDividerPadding(int)
|
||||
*/
|
||||
public int getDividerPadding() {
|
||||
return mDividerPadding;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the current divider drawable.
|
||||
*
|
||||
* @hide Used internally by framework.
|
||||
*/
|
||||
public int getDividerWidth() {
|
||||
return mDividerWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) {
|
||||
final int index = indexOfChild(child);
|
||||
final int orientation = getOrientation();
|
||||
final LayoutParams params = (LayoutParams) child.getLayoutParams();
|
||||
if (hasDividerBeforeChildAt(index)) {
|
||||
if (orientation == VERTICAL) {
|
||||
//Account for the divider by pushing everything up
|
||||
params.topMargin = mDividerHeight;
|
||||
} else {
|
||||
//Account for the divider by pushing everything left
|
||||
params.leftMargin = mDividerWidth;
|
||||
}
|
||||
}
|
||||
|
||||
final int count = getChildCount();
|
||||
if (index == count - 1) {
|
||||
if (hasDividerBeforeChildAt(count)) {
|
||||
if (orientation == VERTICAL) {
|
||||
params.bottomMargin = mDividerHeight;
|
||||
} else {
|
||||
params.rightMargin = mDividerWidth;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.measureChildWithMargins(child, parentWidthMeasureSpec, widthUsed, parentHeightMeasureSpec, heightUsed);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mDivider != null) {
|
||||
if (getOrientation() == VERTICAL) {
|
||||
drawDividersVertical(canvas);
|
||||
} else {
|
||||
drawDividersHorizontal(canvas);
|
||||
}
|
||||
}
|
||||
super.onDraw(canvas);
|
||||
}
|
||||
|
||||
void drawDividersVertical(Canvas canvas) {
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
if (child != null && child.getVisibility() != GONE) {
|
||||
if (hasDividerBeforeChildAt(i)) {
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
final int top = child.getTop() - lp.topMargin/* - mDividerHeight*/;
|
||||
drawHorizontalDivider(canvas, top);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDividerBeforeChildAt(count)) {
|
||||
final View child = getChildAt(count - 1);
|
||||
int bottom = 0;
|
||||
if (child == null) {
|
||||
bottom = getHeight() - getPaddingBottom() - mDividerHeight;
|
||||
} else {
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
bottom = child.getBottom()/* + lp.bottomMargin*/;
|
||||
}
|
||||
drawHorizontalDivider(canvas, bottom);
|
||||
}
|
||||
}
|
||||
|
||||
void drawDividersHorizontal(Canvas canvas) {
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
final View child = getChildAt(i);
|
||||
|
||||
if (child != null && child.getVisibility() != GONE) {
|
||||
if (hasDividerBeforeChildAt(i)) {
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
final int left = child.getLeft() - lp.leftMargin/* - mDividerWidth*/;
|
||||
drawVerticalDivider(canvas, left);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDividerBeforeChildAt(count)) {
|
||||
final View child = getChildAt(count - 1);
|
||||
int right = 0;
|
||||
if (child == null) {
|
||||
right = getWidth() - getPaddingRight() - mDividerWidth;
|
||||
} else {
|
||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
||||
right = child.getRight()/* + lp.rightMargin*/;
|
||||
}
|
||||
drawVerticalDivider(canvas, right);
|
||||
}
|
||||
}
|
||||
|
||||
void drawHorizontalDivider(Canvas canvas, int top) {
|
||||
mDivider.setBounds(getPaddingLeft() + mDividerPadding, top,
|
||||
getWidth() - getPaddingRight() - mDividerPadding, top + mDividerHeight);
|
||||
mDivider.draw(canvas);
|
||||
}
|
||||
|
||||
void drawVerticalDivider(Canvas canvas, int left) {
|
||||
mDivider.setBounds(left, getPaddingTop() + mDividerPadding,
|
||||
left + mDividerWidth, getHeight() - getPaddingBottom() - mDividerPadding);
|
||||
mDivider.draw(canvas);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines where to position dividers between children.
|
||||
*
|
||||
* @param childIndex Index of child to check for preceding divider
|
||||
* @return true if there should be a divider before the child at childIndex
|
||||
* @hide Pending API consideration. Currently only used internally by the system.
|
||||
*/
|
||||
protected boolean hasDividerBeforeChildAt(int childIndex) {
|
||||
if (childIndex == 0) {
|
||||
return (mShowDividers & SHOW_DIVIDER_BEGINNING) != 0;
|
||||
} else if (childIndex == getChildCount()) {
|
||||
return (mShowDividers & SHOW_DIVIDER_END) != 0;
|
||||
} else if ((mShowDividers & SHOW_DIVIDER_MIDDLE) != 0) {
|
||||
boolean hasVisibleViewBefore = false;
|
||||
for (int i = childIndex - 1; i >= 0; i--) {
|
||||
if (getChildAt(i).getVisibility() != GONE) {
|
||||
hasVisibleViewBefore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return hasVisibleViewBefore;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,644 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,703 @@
|
||||
/*
|
||||
* Copyright (C) 2007 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 com.actionbarsherlock.internal.widget;
|
||||
|
||||
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
|
||||
import com.actionbarsherlock.R;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.res.TypedArray;
|
||||
import android.database.DataSetObserver;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.PopupWindow;
|
||||
import android.widget.SpinnerAdapter;
|
||||
|
||||
|
||||
/**
|
||||
* A view that displays one child at a time and lets the user pick among them.
|
||||
* The items in the Spinner come from the {@link Adapter} associated with
|
||||
* this view.
|
||||
*
|
||||
* <p>See the <a href="{@docRoot}resources/tutorials/views/hello-spinner.html">Spinner
|
||||
* tutorial</a>.</p>
|
||||
*
|
||||
* @attr ref android.R.styleable#Spinner_prompt
|
||||
*/
|
||||
public class IcsSpinner extends IcsAbsSpinner implements OnClickListener {
|
||||
//private static final String TAG = "Spinner";
|
||||
|
||||
// Only measure this many items to get a decent max width.
|
||||
private static final int MAX_ITEMS_MEASURED = 15;
|
||||
|
||||
/**
|
||||
* Use a dialog window for selecting spinner options.
|
||||
*/
|
||||
//public static final int MODE_DIALOG = 0;
|
||||
|
||||
/**
|
||||
* Use a dropdown anchored to the Spinner for selecting spinner options.
|
||||
*/
|
||||
public static final int MODE_DROPDOWN = 1;
|
||||
|
||||
/**
|
||||
* Use the theme-supplied value to select the dropdown mode.
|
||||
*/
|
||||
//private static final int MODE_THEME = -1;
|
||||
|
||||
private SpinnerPopup mPopup;
|
||||
private DropDownAdapter mTempAdapter;
|
||||
int mDropDownWidth;
|
||||
|
||||
private int mGravity;
|
||||
private boolean mDisableChildrenWhenDisabled;
|
||||
|
||||
private Rect mTempRect = new Rect();
|
||||
|
||||
public IcsSpinner(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, R.attr.actionDropDownStyle);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new spinner with the given context's theme, the supplied attribute set,
|
||||
* and default style.
|
||||
*
|
||||
* @param context The Context the view is running in, through which it can
|
||||
* access the current theme, resources, etc.
|
||||
* @param attrs The attributes of the XML tag that is inflating the view.
|
||||
* @param defStyle The default style to apply to this view. If 0, no style
|
||||
* will be applied (beyond what is included in the theme). This may
|
||||
* either be an attribute resource, whose value will be retrieved
|
||||
* from the current theme, or an explicit style resource.
|
||||
*/
|
||||
public IcsSpinner(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.SherlockSpinner, defStyle, 0);
|
||||
|
||||
|
||||
DropdownPopup popup = new DropdownPopup(context, attrs, defStyle);
|
||||
|
||||
mDropDownWidth = a.getLayoutDimension(
|
||||
R.styleable.SherlockSpinner_android_dropDownWidth,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
popup.setBackgroundDrawable(a.getDrawable(
|
||||
R.styleable.SherlockSpinner_android_popupBackground));
|
||||
final int verticalOffset = a.getDimensionPixelOffset(
|
||||
R.styleable.SherlockSpinner_android_dropDownVerticalOffset, 0);
|
||||
if (verticalOffset != 0) {
|
||||
popup.setVerticalOffset(verticalOffset);
|
||||
}
|
||||
|
||||
final int horizontalOffset = a.getDimensionPixelOffset(
|
||||
R.styleable.SherlockSpinner_android_dropDownHorizontalOffset, 0);
|
||||
if (horizontalOffset != 0) {
|
||||
popup.setHorizontalOffset(horizontalOffset);
|
||||
}
|
||||
|
||||
mPopup = popup;
|
||||
|
||||
mGravity = a.getInt(R.styleable.SherlockSpinner_android_gravity, Gravity.CENTER);
|
||||
|
||||
mPopup.setPromptText(a.getString(R.styleable.SherlockSpinner_android_prompt));
|
||||
|
||||
mDisableChildrenWhenDisabled = true;
|
||||
|
||||
a.recycle();
|
||||
|
||||
// Base constructor can call setAdapter before we initialize mPopup.
|
||||
// Finish setting things up if this happened.
|
||||
if (mTempAdapter != null) {
|
||||
mPopup.setAdapter(mTempAdapter);
|
||||
mTempAdapter = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setEnabled(boolean enabled) {
|
||||
super.setEnabled(enabled);
|
||||
if (mDisableChildrenWhenDisabled) {
|
||||
final int count = getChildCount();
|
||||
for (int i = 0; i < count; i++) {
|
||||
getChildAt(i).setEnabled(enabled);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes how the selected item view is positioned. Currently only the horizontal component
|
||||
* is used. The default is determined by the current theme.
|
||||
*
|
||||
* @param gravity See {@link android.view.Gravity}
|
||||
*
|
||||
* @attr ref android.R.styleable#Spinner_gravity
|
||||
*/
|
||||
public void setGravity(int gravity) {
|
||||
if (mGravity != gravity) {
|
||||
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) {
|
||||
gravity |= Gravity.LEFT;
|
||||
}
|
||||
mGravity = gravity;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdapter(SpinnerAdapter adapter) {
|
||||
super.setAdapter(adapter);
|
||||
|
||||
if (mPopup != null) {
|
||||
mPopup.setAdapter(new DropDownAdapter(adapter));
|
||||
} else {
|
||||
mTempAdapter = new DropDownAdapter(adapter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBaseline() {
|
||||
View child = null;
|
||||
|
||||
if (getChildCount() > 0) {
|
||||
child = getChildAt(0);
|
||||
} else if (mAdapter != null && mAdapter.getCount() > 0) {
|
||||
child = makeAndAddView(0);
|
||||
mRecycler.put(0, child);
|
||||
removeAllViewsInLayout();
|
||||
}
|
||||
|
||||
if (child != null) {
|
||||
final int childBaseline = child.getBaseline();
|
||||
return childBaseline >= 0 ? child.getTop() + childBaseline : -1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
|
||||
if (mPopup != null && mPopup.isShowing()) {
|
||||
mPopup.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A spinner does not support item click events. Calling this method
|
||||
* will raise an exception.</p>
|
||||
*
|
||||
* @param l this listener will be ignored
|
||||
*/
|
||||
@Override
|
||||
public void setOnItemClickListener(OnItemClickListener l) {
|
||||
throw new RuntimeException("setOnItemClickListener cannot be used with a spinner.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
if (mPopup != null && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {
|
||||
final int measuredWidth = getMeasuredWidth();
|
||||
setMeasuredDimension(Math.min(Math.max(measuredWidth,
|
||||
measureContentWidth(getAdapter(), getBackground())),
|
||||
MeasureSpec.getSize(widthMeasureSpec)),
|
||||
getMeasuredHeight());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see android.view.View#onLayout(boolean,int,int,int,int)
|
||||
*
|
||||
* Creates and positions all views
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
super.onLayout(changed, l, t, r, b);
|
||||
mInLayout = true;
|
||||
layout(0, false);
|
||||
mInLayout = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and positions all views for this Spinner.
|
||||
*
|
||||
* @param delta Change in the selected position. +1 moves selection is moving to the right,
|
||||
* so views are scrolling to the left. -1 means selection is moving to the left.
|
||||
*/
|
||||
@Override
|
||||
void layout(int delta, boolean animate) {
|
||||
int childrenLeft = mSpinnerPadding.left;
|
||||
int childrenWidth = getRight() - getLeft() - mSpinnerPadding.left - mSpinnerPadding.right;
|
||||
|
||||
if (mDataChanged) {
|
||||
handleDataChanged();
|
||||
}
|
||||
|
||||
// Handle the empty set by removing all views
|
||||
if (mItemCount == 0) {
|
||||
resetList();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mNextSelectedPosition >= 0) {
|
||||
setSelectedPositionInt(mNextSelectedPosition);
|
||||
}
|
||||
|
||||
recycleAllViews();
|
||||
|
||||
// Clear out old views
|
||||
removeAllViewsInLayout();
|
||||
|
||||
// Make selected view and position it
|
||||
mFirstPosition = mSelectedPosition;
|
||||
View sel = makeAndAddView(mSelectedPosition);
|
||||
int width = sel.getMeasuredWidth();
|
||||
int selectedOffset = childrenLeft;
|
||||
switch (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
|
||||
case Gravity.CENTER_HORIZONTAL:
|
||||
selectedOffset = childrenLeft + (childrenWidth / 2) - (width / 2);
|
||||
break;
|
||||
case Gravity.RIGHT:
|
||||
selectedOffset = childrenLeft + childrenWidth - width;
|
||||
break;
|
||||
}
|
||||
sel.offsetLeftAndRight(selectedOffset);
|
||||
|
||||
// Flush any cached views that did not get reused above
|
||||
mRecycler.clear();
|
||||
|
||||
invalidate();
|
||||
|
||||
checkSelectionChanged();
|
||||
|
||||
mDataChanged = false;
|
||||
mNeedSync = false;
|
||||
setNextSelectedPositionInt(mSelectedPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a view, either by pulling an existing view from the recycler or
|
||||
* by getting a new one from the adapter. If we are animating, make sure
|
||||
* there is enough information in the view's layout parameters to animate
|
||||
* from the old to new positions.
|
||||
*
|
||||
* @param position Position in the spinner for the view to obtain
|
||||
* @return A view that has been added to the spinner
|
||||
*/
|
||||
private View makeAndAddView(int position) {
|
||||
|
||||
View child;
|
||||
|
||||
if (!mDataChanged) {
|
||||
child = mRecycler.get(position);
|
||||
if (child != null) {
|
||||
// Position the view
|
||||
setUpChild(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
}
|
||||
|
||||
// Nothing found in the recycler -- ask the adapter for a view
|
||||
child = mAdapter.getView(position, null, this);
|
||||
|
||||
// Position the view
|
||||
setUpChild(child);
|
||||
|
||||
return child;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper for makeAndAddView to set the position of a view
|
||||
* and fill out its layout paramters.
|
||||
*
|
||||
* @param child The view to position
|
||||
*/
|
||||
private void setUpChild(View child) {
|
||||
|
||||
// Respect layout params that are already in the view. Otherwise
|
||||
// make some up...
|
||||
ViewGroup.LayoutParams lp = child.getLayoutParams();
|
||||
if (lp == null) {
|
||||
lp = generateDefaultLayoutParams();
|
||||
}
|
||||
|
||||
addViewInLayout(child, 0, lp);
|
||||
|
||||
child.setSelected(hasFocus());
|
||||
if (mDisableChildrenWhenDisabled) {
|
||||
child.setEnabled(isEnabled());
|
||||
}
|
||||
|
||||
// Get measure specs
|
||||
int childHeightSpec = ViewGroup.getChildMeasureSpec(mHeightMeasureSpec,
|
||||
mSpinnerPadding.top + mSpinnerPadding.bottom, lp.height);
|
||||
int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec,
|
||||
mSpinnerPadding.left + mSpinnerPadding.right, lp.width);
|
||||
|
||||
// Measure child
|
||||
child.measure(childWidthSpec, childHeightSpec);
|
||||
|
||||
int childLeft;
|
||||
int childRight;
|
||||
|
||||
// Position vertically based on gravity setting
|
||||
int childTop = mSpinnerPadding.top
|
||||
+ ((getMeasuredHeight() - mSpinnerPadding.bottom -
|
||||
mSpinnerPadding.top - child.getMeasuredHeight()) / 2);
|
||||
int childBottom = childTop + child.getMeasuredHeight();
|
||||
|
||||
int width = child.getMeasuredWidth();
|
||||
childLeft = 0;
|
||||
childRight = childLeft + width;
|
||||
|
||||
child.layout(childLeft, childTop, childRight, childBottom);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean performClick() {
|
||||
boolean handled = super.performClick();
|
||||
|
||||
if (!handled) {
|
||||
handled = true;
|
||||
|
||||
if (!mPopup.isShowing()) {
|
||||
mPopup.show();
|
||||
}
|
||||
}
|
||||
|
||||
return handled;
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setSelection(which);
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prompt to display when the dialog is shown.
|
||||
* @param prompt the prompt to set
|
||||
*/
|
||||
public void setPrompt(CharSequence prompt) {
|
||||
mPopup.setPromptText(prompt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prompt to display when the dialog is shown.
|
||||
* @param promptId the resource ID of the prompt to display when the dialog is shown
|
||||
*/
|
||||
public void setPromptId(int promptId) {
|
||||
setPrompt(getContext().getText(promptId));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The prompt to display when the dialog is shown
|
||||
*/
|
||||
public CharSequence getPrompt() {
|
||||
return mPopup.getHintText();
|
||||
}
|
||||
|
||||
int measureContentWidth(SpinnerAdapter adapter, Drawable background) {
|
||||
if (adapter == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int width = 0;
|
||||
View itemView = null;
|
||||
int itemType = 0;
|
||||
final int widthMeasureSpec =
|
||||
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
final int heightMeasureSpec =
|
||||
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
||||
|
||||
// Make sure the number of items we'll measure is capped. If it's a huge data set
|
||||
// with wildly varying sizes, oh well.
|
||||
int start = Math.max(0, getSelectedItemPosition());
|
||||
final int end = Math.min(adapter.getCount(), start + MAX_ITEMS_MEASURED);
|
||||
final int count = end - start;
|
||||
start = Math.max(0, start - (MAX_ITEMS_MEASURED - count));
|
||||
for (int i = start; i < end; i++) {
|
||||
final int positionType = adapter.getItemViewType(i);
|
||||
if (positionType != itemType) {
|
||||
itemType = positionType;
|
||||
itemView = null;
|
||||
}
|
||||
itemView = adapter.getView(i, itemView, this);
|
||||
if (itemView.getLayoutParams() == null) {
|
||||
itemView.setLayoutParams(new ViewGroup.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||
}
|
||||
itemView.measure(widthMeasureSpec, heightMeasureSpec);
|
||||
width = Math.max(width, itemView.getMeasuredWidth());
|
||||
}
|
||||
|
||||
// Add background padding to measured width
|
||||
if (background != null) {
|
||||
background.getPadding(mTempRect);
|
||||
width += mTempRect.left + mTempRect.right;
|
||||
}
|
||||
|
||||
return width;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Wrapper class for an Adapter. Transforms the embedded Adapter instance
|
||||
* into a ListAdapter.</p>
|
||||
*/
|
||||
private static class DropDownAdapter implements ListAdapter, SpinnerAdapter {
|
||||
private SpinnerAdapter mAdapter;
|
||||
private ListAdapter mListAdapter;
|
||||
|
||||
/**
|
||||
* <p>Creates a new ListAdapter wrapper for the specified adapter.</p>
|
||||
*
|
||||
* @param adapter the Adapter to transform into a ListAdapter
|
||||
*/
|
||||
public DropDownAdapter(SpinnerAdapter adapter) {
|
||||
this.mAdapter = adapter;
|
||||
if (adapter instanceof ListAdapter) {
|
||||
this.mListAdapter = (ListAdapter) adapter;
|
||||
}
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mAdapter == null ? 0 : mAdapter.getCount();
|
||||
}
|
||||
|
||||
public Object getItem(int position) {
|
||||
return mAdapter == null ? null : mAdapter.getItem(position);
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return mAdapter == null ? -1 : mAdapter.getItemId(position);
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
return getDropDownView(position, convertView, parent);
|
||||
}
|
||||
|
||||
public View getDropDownView(int position, View convertView, ViewGroup parent) {
|
||||
return mAdapter == null ? null :
|
||||
mAdapter.getDropDownView(position, convertView, parent);
|
||||
}
|
||||
|
||||
public boolean hasStableIds() {
|
||||
return mAdapter != null && mAdapter.hasStableIds();
|
||||
}
|
||||
|
||||
public void registerDataSetObserver(DataSetObserver observer) {
|
||||
if (mAdapter != null) {
|
||||
mAdapter.registerDataSetObserver(observer);
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterDataSetObserver(DataSetObserver observer) {
|
||||
if (mAdapter != null) {
|
||||
mAdapter.unregisterDataSetObserver(observer);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
|
||||
* Otherwise, return true.
|
||||
*/
|
||||
public boolean areAllItemsEnabled() {
|
||||
final ListAdapter adapter = mListAdapter;
|
||||
if (adapter != null) {
|
||||
return adapter.areAllItemsEnabled();
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the wrapped SpinnerAdapter is also a ListAdapter, delegate this call.
|
||||
* Otherwise, return true.
|
||||
*/
|
||||
public boolean isEnabled(int position) {
|
||||
final ListAdapter adapter = mListAdapter;
|
||||
if (adapter != null) {
|
||||
return adapter.isEnabled(position);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public int getItemViewType(int position) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getViewTypeCount() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return getCount() == 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements some sort of popup selection interface for selecting a spinner option.
|
||||
* Allows for different spinner modes.
|
||||
*/
|
||||
private interface SpinnerPopup {
|
||||
public void setAdapter(ListAdapter adapter);
|
||||
|
||||
/**
|
||||
* Show the popup
|
||||
*/
|
||||
public void show();
|
||||
|
||||
/**
|
||||
* Dismiss the popup
|
||||
*/
|
||||
public void dismiss();
|
||||
|
||||
/**
|
||||
* @return true if the popup is showing, false otherwise.
|
||||
*/
|
||||
public boolean isShowing();
|
||||
|
||||
/**
|
||||
* Set hint text to be displayed to the user. This should provide
|
||||
* a description of the choice being made.
|
||||
* @param hintText Hint text to set.
|
||||
*/
|
||||
public void setPromptText(CharSequence hintText);
|
||||
public CharSequence getHintText();
|
||||
}
|
||||
|
||||
/*
|
||||
private class DialogPopup implements SpinnerPopup, DialogInterface.OnClickListener {
|
||||
private AlertDialog mPopup;
|
||||
private ListAdapter mListAdapter;
|
||||
private CharSequence mPrompt;
|
||||
|
||||
public void dismiss() {
|
||||
mPopup.dismiss();
|
||||
mPopup = null;
|
||||
}
|
||||
|
||||
public boolean isShowing() {
|
||||
return mPopup != null ? mPopup.isShowing() : false;
|
||||
}
|
||||
|
||||
public void setAdapter(ListAdapter adapter) {
|
||||
mListAdapter = adapter;
|
||||
}
|
||||
|
||||
public void setPromptText(CharSequence hintText) {
|
||||
mPrompt = hintText;
|
||||
}
|
||||
|
||||
public CharSequence getHintText() {
|
||||
return mPrompt;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||
if (mPrompt != null) {
|
||||
builder.setTitle(mPrompt);
|
||||
}
|
||||
mPopup = builder.setSingleChoiceItems(mListAdapter,
|
||||
getSelectedItemPosition(), this).show();
|
||||
}
|
||||
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
setSelection(which);
|
||||
dismiss();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private class DropdownPopup extends IcsListPopupWindow implements SpinnerPopup {
|
||||
private CharSequence mHintText;
|
||||
private ListAdapter mAdapter;
|
||||
|
||||
public DropdownPopup(Context context, AttributeSet attrs, int defStyleRes) {
|
||||
super(context, attrs, 0, defStyleRes);
|
||||
|
||||
setAnchorView(IcsSpinner.this);
|
||||
setModal(true);
|
||||
setPromptPosition(POSITION_PROMPT_ABOVE);
|
||||
setOnItemClickListener(new OnItemClickListener() {
|
||||
@SuppressWarnings("rawtypes")
|
||||
public void onItemClick(AdapterView parent, View v, int position, long id) {
|
||||
IcsSpinner.this.setSelection(position);
|
||||
dismiss();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdapter(ListAdapter adapter) {
|
||||
super.setAdapter(adapter);
|
||||
mAdapter = adapter;
|
||||
}
|
||||
|
||||
public CharSequence getHintText() {
|
||||
return mHintText;
|
||||
}
|
||||
|
||||
public void setPromptText(CharSequence hintText) {
|
||||
// Hint text is ignored for dropdowns, but maintain it here.
|
||||
mHintText = hintText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show() {
|
||||
final int spinnerPaddingLeft = IcsSpinner.this.getPaddingLeft();
|
||||
if (mDropDownWidth == WRAP_CONTENT) {
|
||||
final int spinnerWidth = IcsSpinner.this.getWidth();
|
||||
final int spinnerPaddingRight = IcsSpinner.this.getPaddingRight();
|
||||
setContentWidth(Math.max(
|
||||
measureContentWidth((SpinnerAdapter) mAdapter, getBackground()),
|
||||
spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight));
|
||||
} else if (mDropDownWidth == MATCH_PARENT) {
|
||||
final int spinnerWidth = IcsSpinner.this.getWidth();
|
||||
final int spinnerPaddingRight = IcsSpinner.this.getPaddingRight();
|
||||
setContentWidth(spinnerWidth - spinnerPaddingLeft - spinnerPaddingRight);
|
||||
} else {
|
||||
setContentWidth(mDropDownWidth);
|
||||
}
|
||||
final Drawable background = getBackground();
|
||||
int bgOffset = 0;
|
||||
if (background != null) {
|
||||
background.getPadding(mTempRect);
|
||||
bgOffset = -mTempRect.left;
|
||||
}
|
||||
setHorizontalOffset(bgOffset + spinnerPaddingLeft);
|
||||
setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
|
||||
super.show();
|
||||
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
|
||||
setSelection(IcsSpinner.this.getSelectedItemPosition());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package com.actionbarsherlock.internal.widget;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
final class IcsView {
|
||||
//No instances
|
||||
private IcsView() {}
|
||||
|
||||
/**
|
||||
* Return only the state bits of {@link #getMeasuredWidthAndState()}
|
||||
* and {@link #getMeasuredHeightAndState()}, combined into one integer.
|
||||
* The width component is in the regular bits {@link #MEASURED_STATE_MASK}
|
||||
* and the height component is at the shifted bits
|
||||
* {@link #MEASURED_HEIGHT_STATE_SHIFT}>>{@link #MEASURED_STATE_MASK}.
|
||||
*/
|
||||
public static int getMeasuredStateInt(View child) {
|
||||
return (child.getMeasuredWidth()&View.MEASURED_STATE_MASK)
|
||||
| ((child.getMeasuredHeight()>>View.MEASURED_HEIGHT_STATE_SHIFT)
|
||||
& (View.MEASURED_STATE_MASK>>View.MEASURED_HEIGHT_STATE_SHIFT));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,545 @@
|
||||
/*
|
||||
* Copyright (C) 2011 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 com.actionbarsherlock.internal.widget;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.text.TextUtils.TruncateAt;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import com.actionbarsherlock.R;
|
||||
import com.actionbarsherlock.app.ActionBar;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
|
||||
import com.actionbarsherlock.internal.nineoldandroids.widget.NineHorizontalScrollView;
|
||||
|
||||
/**
|
||||
* This widget implements the dynamic action bar tab behavior that can change
|
||||
* across different configurations or circumstances.
|
||||
*/
|
||||
public class ScrollingTabContainerView extends NineHorizontalScrollView
|
||||
implements IcsAdapterView.OnItemSelectedListener {
|
||||
//UNUSED private static final String TAG = "ScrollingTabContainerView";
|
||||
Runnable mTabSelector;
|
||||
private TabClickListener mTabClickListener;
|
||||
|
||||
private IcsLinearLayout mTabLayout;
|
||||
private IcsSpinner mTabSpinner;
|
||||
private boolean mAllowCollapse;
|
||||
|
||||
private LayoutInflater mInflater;
|
||||
|
||||
int mMaxTabWidth;
|
||||
private int mContentHeight;
|
||||
private int mSelectedTabIndex;
|
||||
|
||||
protected Animator mVisibilityAnim;
|
||||
protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
|
||||
|
||||
private static final /*Time*/Interpolator sAlphaInterpolator = new DecelerateInterpolator();
|
||||
|
||||
private static final int FADE_DURATION = 200;
|
||||
|
||||
public ScrollingTabContainerView(Context context) {
|
||||
super(context);
|
||||
setHorizontalScrollBarEnabled(false);
|
||||
|
||||
TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
|
||||
R.attr.actionBarStyle, 0);
|
||||
setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
|
||||
a.recycle();
|
||||
|
||||
mInflater = LayoutInflater.from(context);
|
||||
|
||||
mTabLayout = createTabLayout();
|
||||
addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
|
||||
setFillViewport(lockedExpanded);
|
||||
|
||||
final int childCount = mTabLayout.getChildCount();
|
||||
if (childCount > 1 &&
|
||||
(widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
|
||||
if (childCount > 2) {
|
||||
mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
|
||||
} else {
|
||||
mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
|
||||
}
|
||||
} else {
|
||||
mMaxTabWidth = -1;
|
||||
}
|
||||
|
||||
heightMeasureSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY);
|
||||
|
||||
final boolean canCollapse = !lockedExpanded && mAllowCollapse;
|
||||
|
||||
if (canCollapse) {
|
||||
// See if we should expand
|
||||
mTabLayout.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec);
|
||||
if (mTabLayout.getMeasuredWidth() > MeasureSpec.getSize(widthMeasureSpec)) {
|
||||
performCollapse();
|
||||
} else {
|
||||
performExpand();
|
||||
}
|
||||
} else {
|
||||
performExpand();
|
||||
}
|
||||
|
||||
final int oldWidth = getMeasuredWidth();
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
final int newWidth = getMeasuredWidth();
|
||||
|
||||
if (lockedExpanded && oldWidth != newWidth) {
|
||||
// Recenter the tab display if we're at a new (scrollable) size.
|
||||
setTabSelected(mSelectedTabIndex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether this view is collapsed into a dropdown menu instead
|
||||
* of traditional tabs.
|
||||
* @return true if showing as a spinner
|
||||
*/
|
||||
private boolean isCollapsed() {
|
||||
return mTabSpinner != null && mTabSpinner.getParent() == this;
|
||||
}
|
||||
|
||||
public void setAllowCollapse(boolean allowCollapse) {
|
||||
mAllowCollapse = allowCollapse;
|
||||
}
|
||||
|
||||
private void performCollapse() {
|
||||
if (isCollapsed()) return;
|
||||
|
||||
if (mTabSpinner == null) {
|
||||
mTabSpinner = createSpinner();
|
||||
}
|
||||
removeView(mTabLayout);
|
||||
addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
if (mTabSpinner.getAdapter() == null) {
|
||||
mTabSpinner.setAdapter(new TabAdapter());
|
||||
}
|
||||
if (mTabSelector != null) {
|
||||
removeCallbacks(mTabSelector);
|
||||
mTabSelector = null;
|
||||
}
|
||||
mTabSpinner.setSelection(mSelectedTabIndex);
|
||||
}
|
||||
|
||||
private boolean performExpand() {
|
||||
if (!isCollapsed()) return false;
|
||||
|
||||
removeView(mTabSpinner);
|
||||
addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
setTabSelected(mTabSpinner.getSelectedItemPosition());
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setTabSelected(int position) {
|
||||
mSelectedTabIndex = position;
|
||||
final int tabCount = mTabLayout.getChildCount();
|
||||
for (int i = 0; i < tabCount; i++) {
|
||||
final View child = mTabLayout.getChildAt(i);
|
||||
final boolean isSelected = i == position;
|
||||
child.setSelected(isSelected);
|
||||
if (isSelected) {
|
||||
animateToTab(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setContentHeight(int contentHeight) {
|
||||
mContentHeight = contentHeight;
|
||||
requestLayout();
|
||||
}
|
||||
|
||||
private IcsLinearLayout createTabLayout() {
|
||||
final IcsLinearLayout tabLayout = (IcsLinearLayout) LayoutInflater.from(getContext())
|
||||
.inflate(R.layout.abs__action_bar_tab_bar_view, null);
|
||||
tabLayout.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
|
||||
return tabLayout;
|
||||
}
|
||||
|
||||
private IcsSpinner createSpinner() {
|
||||
final IcsSpinner spinner = new IcsSpinner(getContext(), null,
|
||||
R.attr.actionDropDownStyle);
|
||||
spinner.setLayoutParams(new LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
|
||||
spinner.setOnItemSelectedListener(this);
|
||||
return spinner;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
|
||||
// Action bar can change size on configuration changes.
|
||||
// Reread the desired height from the theme-specified style.
|
||||
TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
|
||||
R.attr.actionBarStyle, 0);
|
||||
setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void animateToVisibility(int visibility) {
|
||||
if (mVisibilityAnim != null) {
|
||||
mVisibilityAnim.cancel();
|
||||
}
|
||||
if (visibility == VISIBLE) {
|
||||
if (getVisibility() != VISIBLE) {
|
||||
setAlpha(0);
|
||||
}
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
|
||||
anim.setDuration(FADE_DURATION);
|
||||
anim.setInterpolator(sAlphaInterpolator);
|
||||
|
||||
anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
|
||||
anim.start();
|
||||
} else {
|
||||
ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
|
||||
anim.setDuration(FADE_DURATION);
|
||||
anim.setInterpolator(sAlphaInterpolator);
|
||||
|
||||
anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
|
||||
anim.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void animateToTab(final int position) {
|
||||
final View tabView = mTabLayout.getChildAt(position);
|
||||
if (mTabSelector != null) {
|
||||
removeCallbacks(mTabSelector);
|
||||
}
|
||||
mTabSelector = new Runnable() {
|
||||
public void run() {
|
||||
final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
|
||||
smoothScrollTo(scrollPos, 0);
|
||||
mTabSelector = null;
|
||||
}
|
||||
};
|
||||
post(mTabSelector);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
if (mTabSelector != null) {
|
||||
// Re-post the selector we saved
|
||||
post(mTabSelector);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
if (mTabSelector != null) {
|
||||
removeCallbacks(mTabSelector);
|
||||
}
|
||||
}
|
||||
|
||||
private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) {
|
||||
//Workaround for not being able to pass a defStyle on pre-3.0
|
||||
final TabView tabView = (TabView)mInflater.inflate(R.layout.abs__action_bar_tab, null);
|
||||
tabView.init(this, tab, forAdapter);
|
||||
|
||||
if (forAdapter) {
|
||||
tabView.setBackgroundDrawable(null);
|
||||
tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT,
|
||||
mContentHeight));
|
||||
} else {
|
||||
tabView.setFocusable(true);
|
||||
|
||||
if (mTabClickListener == null) {
|
||||
mTabClickListener = new TabClickListener();
|
||||
}
|
||||
tabView.setOnClickListener(mTabClickListener);
|
||||
}
|
||||
return tabView;
|
||||
}
|
||||
|
||||
public void addTab(ActionBar.Tab tab, boolean setSelected) {
|
||||
TabView tabView = createTabView(tab, false);
|
||||
mTabLayout.addView(tabView, new IcsLinearLayout.LayoutParams(0,
|
||||
LayoutParams.MATCH_PARENT, 1));
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (setSelected) {
|
||||
tabView.setSelected(true);
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
|
||||
final TabView tabView = createTabView(tab, false);
|
||||
mTabLayout.addView(tabView, position, new IcsLinearLayout.LayoutParams(
|
||||
0, LayoutParams.MATCH_PARENT, 1));
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (setSelected) {
|
||||
tabView.setSelected(true);
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTab(int position) {
|
||||
((TabView) mTabLayout.getChildAt(position)).update();
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeTabAt(int position) {
|
||||
mTabLayout.removeViewAt(position);
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
public void removeAllTabs() {
|
||||
mTabLayout.removeAllViews();
|
||||
if (mTabSpinner != null) {
|
||||
((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
|
||||
}
|
||||
if (mAllowCollapse) {
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onItemSelected(IcsAdapterView<?> parent, View view, int position, long id) {
|
||||
TabView tabView = (TabView) view;
|
||||
tabView.getTab().select();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(IcsAdapterView<?> parent) {
|
||||
}
|
||||
|
||||
public static class TabView extends LinearLayout {
|
||||
private ScrollingTabContainerView mParent;
|
||||
private ActionBar.Tab mTab;
|
||||
private CapitalizingTextView mTextView;
|
||||
private ImageView mIconView;
|
||||
private View mCustomView;
|
||||
|
||||
public TabView(Context context, AttributeSet attrs) {
|
||||
//TODO super(context, null, R.attr.actionBarTabStyle);
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public void init(ScrollingTabContainerView parent, ActionBar.Tab tab, boolean forList) {
|
||||
mParent = parent;
|
||||
mTab = tab;
|
||||
|
||||
if (forList) {
|
||||
setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
public void bindTab(ActionBar.Tab tab) {
|
||||
mTab = tab;
|
||||
update();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
|
||||
// Re-measure if we went beyond our maximum size.
|
||||
if (mParent.mMaxTabWidth > 0 && getMeasuredWidth() > mParent.mMaxTabWidth) {
|
||||
super.onMeasure(MeasureSpec.makeMeasureSpec(mParent.mMaxTabWidth, MeasureSpec.EXACTLY),
|
||||
heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
public void update() {
|
||||
final ActionBar.Tab tab = mTab;
|
||||
final View custom = tab.getCustomView();
|
||||
if (custom != null) {
|
||||
final ViewParent customParent = custom.getParent();
|
||||
if (customParent != this) {
|
||||
if (customParent != null) ((ViewGroup) customParent).removeView(custom);
|
||||
addView(custom);
|
||||
}
|
||||
mCustomView = custom;
|
||||
if (mTextView != null) mTextView.setVisibility(GONE);
|
||||
if (mIconView != null) {
|
||||
mIconView.setVisibility(GONE);
|
||||
mIconView.setImageDrawable(null);
|
||||
}
|
||||
} else {
|
||||
if (mCustomView != null) {
|
||||
removeView(mCustomView);
|
||||
mCustomView = null;
|
||||
}
|
||||
|
||||
final Drawable icon = tab.getIcon();
|
||||
final CharSequence text = tab.getText();
|
||||
|
||||
if (icon != null) {
|
||||
if (mIconView == null) {
|
||||
ImageView iconView = new ImageView(getContext());
|
||||
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
lp.gravity = Gravity.CENTER_VERTICAL;
|
||||
iconView.setLayoutParams(lp);
|
||||
addView(iconView, 0);
|
||||
mIconView = iconView;
|
||||
}
|
||||
mIconView.setImageDrawable(icon);
|
||||
mIconView.setVisibility(VISIBLE);
|
||||
} else if (mIconView != null) {
|
||||
mIconView.setVisibility(GONE);
|
||||
mIconView.setImageDrawable(null);
|
||||
}
|
||||
|
||||
if (text != null) {
|
||||
if (mTextView == null) {
|
||||
CapitalizingTextView textView = new CapitalizingTextView(getContext(), null,
|
||||
R.attr.actionBarTabTextStyle);
|
||||
textView.setEllipsize(TruncateAt.END);
|
||||
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
||||
LayoutParams.WRAP_CONTENT);
|
||||
lp.gravity = Gravity.CENTER_VERTICAL;
|
||||
textView.setLayoutParams(lp);
|
||||
addView(textView);
|
||||
mTextView = textView;
|
||||
}
|
||||
mTextView.setTextCompat(text);
|
||||
mTextView.setVisibility(VISIBLE);
|
||||
} else if (mTextView != null) {
|
||||
mTextView.setVisibility(GONE);
|
||||
mTextView.setText(null);
|
||||
}
|
||||
|
||||
if (mIconView != null) {
|
||||
mIconView.setContentDescription(tab.getContentDescription());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ActionBar.Tab getTab() {
|
||||
return mTab;
|
||||
}
|
||||
}
|
||||
|
||||
private class TabAdapter extends BaseAdapter {
|
||||
@Override
|
||||
public int getCount() {
|
||||
return mTabLayout.getChildCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getItem(int position) {
|
||||
return ((TabView) mTabLayout.getChildAt(position)).getTab();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getItemId(int position) {
|
||||
return position;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
if (convertView == null) {
|
||||
convertView = createTabView((ActionBar.Tab) getItem(position), true);
|
||||
} else {
|
||||
((TabView) convertView).bindTab((ActionBar.Tab) getItem(position));
|
||||
}
|
||||
return convertView;
|
||||
}
|
||||
}
|
||||
|
||||
private class TabClickListener implements OnClickListener {
|
||||
public void onClick(View view) {
|
||||
TabView tabView = (TabView) view;
|
||||
tabView.getTab().select();
|
||||
final int tabCount = mTabLayout.getChildCount();
|
||||
for (int i = 0; i < tabCount; i++) {
|
||||
final View child = mTabLayout.getChildAt(i);
|
||||
child.setSelected(child == view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected class VisibilityAnimListener implements Animator.AnimatorListener {
|
||||
private boolean mCanceled = false;
|
||||
private int mFinalVisibility;
|
||||
|
||||
public VisibilityAnimListener withFinalVisibility(int visibility) {
|
||||
mFinalVisibility = visibility;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
setVisibility(VISIBLE);
|
||||
mVisibilityAnim = animation;
|
||||
mCanceled = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (mCanceled) return;
|
||||
|
||||
mVisibilityAnim = null;
|
||||
setVisibility(mFinalVisibility);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animation) {
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user