removed gen folder
This commit is contained in:
parent
fb53604b3f
commit
5d1ddf976d
@ -1,5 +0,0 @@
|
|||||||
package com.actionbarsherlock;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package com.actionbarsherlock;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,278 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import android.view.animation.Interpolator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is the superclass for classes which provide basic support for animations which can be
|
|
||||||
* started, ended, and have <code>AnimatorListeners</code> added to them.
|
|
||||||
*/
|
|
||||||
public abstract class Animator implements Cloneable {
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The set of listeners to be sent events through the life of an animation.
|
|
||||||
*/
|
|
||||||
ArrayList<AnimatorListener> mListeners = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Starts this animation. If the animation has a nonzero startDelay, the animation will start
|
|
||||||
* running after that delay elapses. A non-delayed animation will have its initial
|
|
||||||
* value(s) set immediately, followed by calls to
|
|
||||||
* {@link AnimatorListener#onAnimationStart(Animator)} for any listeners of this animator.
|
|
||||||
*
|
|
||||||
* <p>The animation started by calling this method will be run on the thread that called
|
|
||||||
* this method. This thread should have a Looper on it (a runtime exception will be thrown if
|
|
||||||
* this is not the case). Also, if the animation will animate
|
|
||||||
* properties of objects in the view hierarchy, then the calling thread should be the UI
|
|
||||||
* thread for that view hierarchy.</p>
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public void start() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancels the animation. Unlike {@link #end()}, <code>cancel()</code> causes the animation to
|
|
||||||
* stop in its tracks, sending an
|
|
||||||
* {@link android.animation.Animator.AnimatorListener#onAnimationCancel(Animator)} to
|
|
||||||
* its listeners, followed by an
|
|
||||||
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} message.
|
|
||||||
*
|
|
||||||
* <p>This method must be called on the thread that is running the animation.</p>
|
|
||||||
*/
|
|
||||||
public void cancel() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends the animation. This causes the animation to assign the end value of the property being
|
|
||||||
* animated, then calling the
|
|
||||||
* {@link android.animation.Animator.AnimatorListener#onAnimationEnd(Animator)} method on
|
|
||||||
* its listeners.
|
|
||||||
*
|
|
||||||
* <p>This method must be called on the thread that is running the animation.</p>
|
|
||||||
*/
|
|
||||||
public void end() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount of time, in milliseconds, to delay starting the animation after
|
|
||||||
* {@link #start()} is called.
|
|
||||||
*
|
|
||||||
* @return the number of milliseconds to delay running the animation
|
|
||||||
*/
|
|
||||||
public abstract long getStartDelay();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The amount of time, in milliseconds, to delay starting the animation after
|
|
||||||
* {@link #start()} is called.
|
|
||||||
|
|
||||||
* @param startDelay The amount of the delay, in milliseconds
|
|
||||||
*/
|
|
||||||
public abstract void setStartDelay(long startDelay);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the length of the animation.
|
|
||||||
*
|
|
||||||
* @param duration The length of the animation, in milliseconds.
|
|
||||||
*/
|
|
||||||
public abstract Animator setDuration(long duration);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the length of the animation.
|
|
||||||
*
|
|
||||||
* @return The length of the animation, in milliseconds.
|
|
||||||
*/
|
|
||||||
public abstract long getDuration();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The time interpolator used in calculating the elapsed fraction of this animation. The
|
|
||||||
* interpolator determines whether the animation runs with linear or non-linear motion,
|
|
||||||
* such as acceleration and deceleration. The default value is
|
|
||||||
* {@link android.view.animation.AccelerateDecelerateInterpolator}
|
|
||||||
*
|
|
||||||
* @param value the interpolator to be used by this animation
|
|
||||||
*/
|
|
||||||
public abstract void setInterpolator(/*Time*/Interpolator value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this Animator is currently running (having been started and gone past any
|
|
||||||
* initial startDelay period and not yet ended).
|
|
||||||
*
|
|
||||||
* @return Whether the Animator is running.
|
|
||||||
*/
|
|
||||||
public abstract boolean isRunning();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether this Animator has been started and not yet ended. This state is a superset
|
|
||||||
* of the state of {@link #isRunning()}, because an Animator with a nonzero
|
|
||||||
* {@link #getStartDelay() startDelay} will return true for {@link #isStarted()} during the
|
|
||||||
* delay phase, whereas {@link #isRunning()} will return true only after the delay phase
|
|
||||||
* is complete.
|
|
||||||
*
|
|
||||||
* @return Whether the Animator has been started and not yet ended.
|
|
||||||
*/
|
|
||||||
public boolean isStarted() {
|
|
||||||
// Default method returns value for isRunning(). Subclasses should override to return a
|
|
||||||
// real value.
|
|
||||||
return isRunning();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a listener to the set of listeners that are sent events through the life of an
|
|
||||||
* animation, such as start, repeat, and end.
|
|
||||||
*
|
|
||||||
* @param listener the listener to be added to the current set of listeners for this animation.
|
|
||||||
*/
|
|
||||||
public void addListener(AnimatorListener listener) {
|
|
||||||
if (mListeners == null) {
|
|
||||||
mListeners = new ArrayList<AnimatorListener>();
|
|
||||||
}
|
|
||||||
mListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes a listener from the set listening to this animation.
|
|
||||||
*
|
|
||||||
* @param listener the listener to be removed from the current set of listeners for this
|
|
||||||
* animation.
|
|
||||||
*/
|
|
||||||
public void removeListener(AnimatorListener listener) {
|
|
||||||
if (mListeners == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mListeners.remove(listener);
|
|
||||||
if (mListeners.size() == 0) {
|
|
||||||
mListeners = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the set of {@link android.animation.Animator.AnimatorListener} objects that are currently
|
|
||||||
* listening for events on this <code>Animator</code> object.
|
|
||||||
*
|
|
||||||
* @return ArrayList<AnimatorListener> The set of listeners.
|
|
||||||
*/
|
|
||||||
public ArrayList<AnimatorListener> getListeners() {
|
|
||||||
return mListeners;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes all listeners from this object. This is equivalent to calling
|
|
||||||
* <code>getListeners()</code> followed by calling <code>clear()</code> on the
|
|
||||||
* returned list of listeners.
|
|
||||||
*/
|
|
||||||
public void removeAllListeners() {
|
|
||||||
if (mListeners != null) {
|
|
||||||
mListeners.clear();
|
|
||||||
mListeners = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Animator clone() {
|
|
||||||
try {
|
|
||||||
final Animator anim = (Animator) super.clone();
|
|
||||||
if (mListeners != null) {
|
|
||||||
ArrayList<AnimatorListener> oldListeners = mListeners;
|
|
||||||
anim.mListeners = new ArrayList<AnimatorListener>();
|
|
||||||
int numListeners = oldListeners.size();
|
|
||||||
for (int i = 0; i < numListeners; ++i) {
|
|
||||||
anim.mListeners.add(oldListeners.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return anim;
|
|
||||||
} catch (CloneNotSupportedException e) {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method tells the object to use appropriate information to extract
|
|
||||||
* starting values for the animation. For example, a AnimatorSet object will pass
|
|
||||||
* this call to its child objects to tell them to set up the values. A
|
|
||||||
* ObjectAnimator object will use the information it has about its target object
|
|
||||||
* and PropertyValuesHolder objects to get the start values for its properties.
|
|
||||||
* An ValueAnimator object will ignore the request since it does not have enough
|
|
||||||
* information (such as a target object) to gather these values.
|
|
||||||
*/
|
|
||||||
public void setupStartValues() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method tells the object to use appropriate information to extract
|
|
||||||
* ending values for the animation. For example, a AnimatorSet object will pass
|
|
||||||
* this call to its child objects to tell them to set up the values. A
|
|
||||||
* ObjectAnimator object will use the information it has about its target object
|
|
||||||
* and PropertyValuesHolder objects to get the start values for its properties.
|
|
||||||
* An ValueAnimator object will ignore the request since it does not have enough
|
|
||||||
* information (such as a target object) to gather these values.
|
|
||||||
*/
|
|
||||||
public void setupEndValues() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the target object whose property will be animated by this animation. Not all subclasses
|
|
||||||
* operate on target objects (for example, {@link ValueAnimator}, but this method
|
|
||||||
* is on the superclass for the convenience of dealing generically with those subclasses
|
|
||||||
* that do handle targets.
|
|
||||||
*
|
|
||||||
* @param target The object being animated
|
|
||||||
*/
|
|
||||||
public void setTarget(Object target) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>An animation listener receives notifications from an animation.
|
|
||||||
* Notifications indicate animation related events, such as the end or the
|
|
||||||
* repetition of the animation.</p>
|
|
||||||
*/
|
|
||||||
public static interface AnimatorListener {
|
|
||||||
/**
|
|
||||||
* <p>Notifies the start of the animation.</p>
|
|
||||||
*
|
|
||||||
* @param animation The started animation.
|
|
||||||
*/
|
|
||||||
void onAnimationStart(Animator animation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Notifies the end of the animation. This callback is not invoked
|
|
||||||
* for animations with repeat count set to INFINITE.</p>
|
|
||||||
*
|
|
||||||
* @param animation The animation which reached its end.
|
|
||||||
*/
|
|
||||||
void onAnimationEnd(Animator animation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Notifies the cancellation of the animation. This callback is not invoked
|
|
||||||
* for animations with repeat count set to INFINITE.</p>
|
|
||||||
*
|
|
||||||
* @param animation The animation which was canceled.
|
|
||||||
*/
|
|
||||||
void onAnimationCancel(Animator animation);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Notifies the repetition of the animation.</p>
|
|
||||||
*
|
|
||||||
* @param animation The animation which was repeated.
|
|
||||||
*/
|
|
||||||
void onAnimationRepeat(Animator animation);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This adapter class provides empty implementations of the methods from {@link android.animation.Animator.AnimatorListener}.
|
|
||||||
* Any custom listener that cares only about a subset of the methods of this listener can
|
|
||||||
* simply subclass this adapter class instead of implementing the interface directly.
|
|
||||||
*/
|
|
||||||
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onAnimationCancel(Animator animation) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onAnimationRepeat(Animator animation) {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@inheritDoc}
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void onAnimationStart(Animator animation) {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This evaluator can be used to perform type interpolation between <code>float</code> values.
|
|
||||||
*/
|
|
||||||
public class FloatEvaluator implements TypeEvaluator<Number> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function returns the result of linearly interpolating the start and end values, with
|
|
||||||
* <code>fraction</code> representing the proportion between the start and end values. The
|
|
||||||
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
|
|
||||||
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
|
|
||||||
* and <code>t</code> is <code>fraction</code>.
|
|
||||||
*
|
|
||||||
* @param fraction The fraction from the starting to the ending values
|
|
||||||
* @param startValue The start value; should be of type <code>float</code> or
|
|
||||||
* <code>Float</code>
|
|
||||||
* @param endValue The end value; should be of type <code>float</code> or <code>Float</code>
|
|
||||||
* @return A linear interpolation between the start and end values, given the
|
|
||||||
* <code>fraction</code> parameter.
|
|
||||||
*/
|
|
||||||
public Float evaluate(float fraction, Number startValue, Number endValue) {
|
|
||||||
float startFloat = startValue.floatValue();
|
|
||||||
return startFloat + fraction * (endValue.floatValue() - startFloat);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,136 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import android.view.animation.Interpolator;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.FloatKeyframe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class holds a collection of FloatKeyframe objects and is called by ValueAnimator to calculate
|
|
||||||
* values between those keyframes for a given animation. The class internal to the animation
|
|
||||||
* package because it is an implementation detail of how Keyframes are stored and used.
|
|
||||||
*
|
|
||||||
* <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclass for
|
|
||||||
* int, exists to speed up the getValue() method when there is no custom
|
|
||||||
* TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
|
|
||||||
* Object equivalents of these primitive types.</p>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
class FloatKeyframeSet extends KeyframeSet {
|
|
||||||
private float firstValue;
|
|
||||||
private float lastValue;
|
|
||||||
private float deltaValue;
|
|
||||||
private boolean firstTime = true;
|
|
||||||
|
|
||||||
public FloatKeyframeSet(FloatKeyframe... keyframes) {
|
|
||||||
super(keyframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValue(float fraction) {
|
|
||||||
return getFloatValue(fraction);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FloatKeyframeSet clone() {
|
|
||||||
ArrayList<Keyframe> keyframes = mKeyframes;
|
|
||||||
int numKeyframes = mKeyframes.size();
|
|
||||||
FloatKeyframe[] newKeyframes = new FloatKeyframe[numKeyframes];
|
|
||||||
for (int i = 0; i < numKeyframes; ++i) {
|
|
||||||
newKeyframes[i] = (FloatKeyframe) keyframes.get(i).clone();
|
|
||||||
}
|
|
||||||
FloatKeyframeSet newSet = new FloatKeyframeSet(newKeyframes);
|
|
||||||
return newSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getFloatValue(float fraction) {
|
|
||||||
if (mNumKeyframes == 2) {
|
|
||||||
if (firstTime) {
|
|
||||||
firstTime = false;
|
|
||||||
firstValue = ((FloatKeyframe) mKeyframes.get(0)).getFloatValue();
|
|
||||||
lastValue = ((FloatKeyframe) mKeyframes.get(1)).getFloatValue();
|
|
||||||
deltaValue = lastValue - firstValue;
|
|
||||||
}
|
|
||||||
if (mInterpolator != null) {
|
|
||||||
fraction = mInterpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
if (mEvaluator == null) {
|
|
||||||
return firstValue + fraction * deltaValue;
|
|
||||||
} else {
|
|
||||||
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).floatValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fraction <= 0f) {
|
|
||||||
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
|
|
||||||
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(1);
|
|
||||||
float prevValue = prevKeyframe.getFloatValue();
|
|
||||||
float nextValue = nextKeyframe.getFloatValue();
|
|
||||||
float prevFraction = prevKeyframe.getFraction();
|
|
||||||
float nextFraction = nextKeyframe.getFraction();
|
|
||||||
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
|
|
||||||
return mEvaluator == null ?
|
|
||||||
prevValue + intervalFraction * (nextValue - prevValue) :
|
|
||||||
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
|
|
||||||
floatValue();
|
|
||||||
} else if (fraction >= 1f) {
|
|
||||||
final FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 2);
|
|
||||||
final FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(mNumKeyframes - 1);
|
|
||||||
float prevValue = prevKeyframe.getFloatValue();
|
|
||||||
float nextValue = nextKeyframe.getFloatValue();
|
|
||||||
float prevFraction = prevKeyframe.getFraction();
|
|
||||||
float nextFraction = nextKeyframe.getFraction();
|
|
||||||
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
|
|
||||||
return mEvaluator == null ?
|
|
||||||
prevValue + intervalFraction * (nextValue - prevValue) :
|
|
||||||
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
|
|
||||||
floatValue();
|
|
||||||
}
|
|
||||||
FloatKeyframe prevKeyframe = (FloatKeyframe) mKeyframes.get(0);
|
|
||||||
for (int i = 1; i < mNumKeyframes; ++i) {
|
|
||||||
FloatKeyframe nextKeyframe = (FloatKeyframe) mKeyframes.get(i);
|
|
||||||
if (fraction < nextKeyframe.getFraction()) {
|
|
||||||
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
|
|
||||||
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
|
|
||||||
float prevValue = prevKeyframe.getFloatValue();
|
|
||||||
float nextValue = nextKeyframe.getFloatValue();
|
|
||||||
return mEvaluator == null ?
|
|
||||||
prevValue + intervalFraction * (nextValue - prevValue) :
|
|
||||||
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
|
|
||||||
floatValue();
|
|
||||||
}
|
|
||||||
prevKeyframe = nextKeyframe;
|
|
||||||
}
|
|
||||||
// shouldn't get here
|
|
||||||
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).floatValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This evaluator can be used to perform type interpolation between <code>int</code> values.
|
|
||||||
*/
|
|
||||||
public class IntEvaluator implements TypeEvaluator<Integer> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function returns the result of linearly interpolating the start and end values, with
|
|
||||||
* <code>fraction</code> representing the proportion between the start and end values. The
|
|
||||||
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
|
|
||||||
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
|
|
||||||
* and <code>t</code> is <code>fraction</code>.
|
|
||||||
*
|
|
||||||
* @param fraction The fraction from the starting to the ending values
|
|
||||||
* @param startValue The start value; should be of type <code>int</code> or
|
|
||||||
* <code>Integer</code>
|
|
||||||
* @param endValue The end value; should be of type <code>int</code> or <code>Integer</code>
|
|
||||||
* @return A linear interpolation between the start and end values, given the
|
|
||||||
* <code>fraction</code> parameter.
|
|
||||||
*/
|
|
||||||
public Integer evaluate(float fraction, Integer startValue, Integer endValue) {
|
|
||||||
int startInt = startValue;
|
|
||||||
return (int)(startInt + fraction * (endValue - startInt));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,135 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import android.view.animation.Interpolator;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.IntKeyframe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class holds a collection of IntKeyframe objects and is called by ValueAnimator to calculate
|
|
||||||
* values between those keyframes for a given animation. The class internal to the animation
|
|
||||||
* package because it is an implementation detail of how Keyframes are stored and used.
|
|
||||||
*
|
|
||||||
* <p>This type-specific subclass of KeyframeSet, along with the other type-specific subclass for
|
|
||||||
* float, exists to speed up the getValue() method when there is no custom
|
|
||||||
* TypeEvaluator set for the animation, so that values can be calculated without autoboxing to the
|
|
||||||
* Object equivalents of these primitive types.</p>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
class IntKeyframeSet extends KeyframeSet {
|
|
||||||
private int firstValue;
|
|
||||||
private int lastValue;
|
|
||||||
private int deltaValue;
|
|
||||||
private boolean firstTime = true;
|
|
||||||
|
|
||||||
public IntKeyframeSet(IntKeyframe... keyframes) {
|
|
||||||
super(keyframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object getValue(float fraction) {
|
|
||||||
return getIntValue(fraction);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IntKeyframeSet clone() {
|
|
||||||
ArrayList<Keyframe> keyframes = mKeyframes;
|
|
||||||
int numKeyframes = mKeyframes.size();
|
|
||||||
IntKeyframe[] newKeyframes = new IntKeyframe[numKeyframes];
|
|
||||||
for (int i = 0; i < numKeyframes; ++i) {
|
|
||||||
newKeyframes[i] = (IntKeyframe) keyframes.get(i).clone();
|
|
||||||
}
|
|
||||||
IntKeyframeSet newSet = new IntKeyframeSet(newKeyframes);
|
|
||||||
return newSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIntValue(float fraction) {
|
|
||||||
if (mNumKeyframes == 2) {
|
|
||||||
if (firstTime) {
|
|
||||||
firstTime = false;
|
|
||||||
firstValue = ((IntKeyframe) mKeyframes.get(0)).getIntValue();
|
|
||||||
lastValue = ((IntKeyframe) mKeyframes.get(1)).getIntValue();
|
|
||||||
deltaValue = lastValue - firstValue;
|
|
||||||
}
|
|
||||||
if (mInterpolator != null) {
|
|
||||||
fraction = mInterpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
if (mEvaluator == null) {
|
|
||||||
return firstValue + (int)(fraction * deltaValue);
|
|
||||||
} else {
|
|
||||||
return ((Number)mEvaluator.evaluate(fraction, firstValue, lastValue)).intValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (fraction <= 0f) {
|
|
||||||
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
|
|
||||||
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1);
|
|
||||||
int prevValue = prevKeyframe.getIntValue();
|
|
||||||
int nextValue = nextKeyframe.getIntValue();
|
|
||||||
float prevFraction = prevKeyframe.getFraction();
|
|
||||||
float nextFraction = nextKeyframe.getFraction();
|
|
||||||
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
|
|
||||||
return mEvaluator == null ?
|
|
||||||
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
|
|
||||||
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
|
|
||||||
intValue();
|
|
||||||
} else if (fraction >= 1f) {
|
|
||||||
final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2);
|
|
||||||
final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1);
|
|
||||||
int prevValue = prevKeyframe.getIntValue();
|
|
||||||
int nextValue = nextKeyframe.getIntValue();
|
|
||||||
float prevFraction = prevKeyframe.getFraction();
|
|
||||||
float nextFraction = nextKeyframe.getFraction();
|
|
||||||
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction);
|
|
||||||
return mEvaluator == null ?
|
|
||||||
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
|
|
||||||
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue();
|
|
||||||
}
|
|
||||||
IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0);
|
|
||||||
for (int i = 1; i < mNumKeyframes; ++i) {
|
|
||||||
IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i);
|
|
||||||
if (fraction < nextKeyframe.getFraction()) {
|
|
||||||
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
float intervalFraction = (fraction - prevKeyframe.getFraction()) /
|
|
||||||
(nextKeyframe.getFraction() - prevKeyframe.getFraction());
|
|
||||||
int prevValue = prevKeyframe.getIntValue();
|
|
||||||
int nextValue = nextKeyframe.getIntValue();
|
|
||||||
return mEvaluator == null ?
|
|
||||||
prevValue + (int)(intervalFraction * (nextValue - prevValue)) :
|
|
||||||
((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).
|
|
||||||
intValue();
|
|
||||||
}
|
|
||||||
prevKeyframe = nextKeyframe;
|
|
||||||
}
|
|
||||||
// shouldn't get here
|
|
||||||
return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,361 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
import android.view.animation.Interpolator;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class holds a time/value pair for an animation. The Keyframe class is used
|
|
||||||
* by {@link ValueAnimator} to define the values that the animation target will have over the course
|
|
||||||
* of the animation. As the time proceeds from one keyframe to the other, the value of the
|
|
||||||
* target object will animate between the value at the previous keyframe and the value at the
|
|
||||||
* next keyframe. Each keyframe also holds an optional {@link TimeInterpolator}
|
|
||||||
* object, which defines the time interpolation over the intervalue preceding the keyframe.
|
|
||||||
*
|
|
||||||
* <p>The Keyframe class itself is abstract. The type-specific factory methods will return
|
|
||||||
* a subclass of Keyframe specific to the type of value being stored. This is done to improve
|
|
||||||
* performance when dealing with the most common cases (e.g., <code>float</code> and
|
|
||||||
* <code>int</code> values). Other types will fall into a more general Keyframe class that
|
|
||||||
* treats its values as Objects. Unless your animation requires dealing with a custom type
|
|
||||||
* or a data structure that needs to be animated directly (and evaluated using an implementation
|
|
||||||
* of {@link TypeEvaluator}), you should stick to using float and int as animations using those
|
|
||||||
* types have lower runtime overhead than other types.</p>
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public abstract class Keyframe implements Cloneable {
|
|
||||||
/**
|
|
||||||
* The time at which mValue will hold true.
|
|
||||||
*/
|
|
||||||
float mFraction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The type of the value in this Keyframe. This type is determined at construction time,
|
|
||||||
* based on the type of the <code>value</code> object passed into the constructor.
|
|
||||||
*/
|
|
||||||
Class mValueType;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The optional time interpolator for the interval preceding this keyframe. A null interpolator
|
|
||||||
* (the default) results in linear interpolation over the interval.
|
|
||||||
*/
|
|
||||||
private /*Time*/Interpolator mInterpolator = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to indicate whether this keyframe has a valid value. This flag is used when an
|
|
||||||
* animation first starts, to populate placeholder keyframes with real values derived
|
|
||||||
* from the target object.
|
|
||||||
*/
|
|
||||||
boolean mHasValue = false;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a Keyframe object with the given time and value. The time defines the
|
|
||||||
* time, as a proportion of an overall animation's duration, at which the value will hold true
|
|
||||||
* for the animation. The value for the animation between keyframes will be calculated as
|
|
||||||
* an interpolation between the values at those keyframes.
|
|
||||||
*
|
|
||||||
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
|
|
||||||
* of time elapsed of the overall animation duration.
|
|
||||||
* @param value The value that the object will animate to as the animation time approaches
|
|
||||||
* the time in this keyframe, and the the value animated from as the time passes the time in
|
|
||||||
* this keyframe.
|
|
||||||
*/
|
|
||||||
public static Keyframe ofInt(float fraction, int value) {
|
|
||||||
return new IntKeyframe(fraction, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a Keyframe object with the given time. The value at this time will be derived
|
|
||||||
* from the target object when the animation first starts (note that this implies that keyframes
|
|
||||||
* with no initial value must be used as part of an {@link ObjectAnimator}).
|
|
||||||
* The time defines the
|
|
||||||
* time, as a proportion of an overall animation's duration, at which the value will hold true
|
|
||||||
* for the animation. The value for the animation between keyframes will be calculated as
|
|
||||||
* an interpolation between the values at those keyframes.
|
|
||||||
*
|
|
||||||
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
|
|
||||||
* of time elapsed of the overall animation duration.
|
|
||||||
*/
|
|
||||||
public static Keyframe ofInt(float fraction) {
|
|
||||||
return new IntKeyframe(fraction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a Keyframe object with the given time and value. The time defines the
|
|
||||||
* time, as a proportion of an overall animation's duration, at which the value will hold true
|
|
||||||
* for the animation. The value for the animation between keyframes will be calculated as
|
|
||||||
* an interpolation between the values at those keyframes.
|
|
||||||
*
|
|
||||||
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
|
|
||||||
* of time elapsed of the overall animation duration.
|
|
||||||
* @param value The value that the object will animate to as the animation time approaches
|
|
||||||
* the time in this keyframe, and the the value animated from as the time passes the time in
|
|
||||||
* this keyframe.
|
|
||||||
*/
|
|
||||||
public static Keyframe ofFloat(float fraction, float value) {
|
|
||||||
return new FloatKeyframe(fraction, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a Keyframe object with the given time. The value at this time will be derived
|
|
||||||
* from the target object when the animation first starts (note that this implies that keyframes
|
|
||||||
* with no initial value must be used as part of an {@link ObjectAnimator}).
|
|
||||||
* The time defines the
|
|
||||||
* time, as a proportion of an overall animation's duration, at which the value will hold true
|
|
||||||
* for the animation. The value for the animation between keyframes will be calculated as
|
|
||||||
* an interpolation between the values at those keyframes.
|
|
||||||
*
|
|
||||||
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
|
|
||||||
* of time elapsed of the overall animation duration.
|
|
||||||
*/
|
|
||||||
public static Keyframe ofFloat(float fraction) {
|
|
||||||
return new FloatKeyframe(fraction);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a Keyframe object with the given time and value. The time defines the
|
|
||||||
* time, as a proportion of an overall animation's duration, at which the value will hold true
|
|
||||||
* for the animation. The value for the animation between keyframes will be calculated as
|
|
||||||
* an interpolation between the values at those keyframes.
|
|
||||||
*
|
|
||||||
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
|
|
||||||
* of time elapsed of the overall animation duration.
|
|
||||||
* @param value The value that the object will animate to as the animation time approaches
|
|
||||||
* the time in this keyframe, and the the value animated from as the time passes the time in
|
|
||||||
* this keyframe.
|
|
||||||
*/
|
|
||||||
public static Keyframe ofObject(float fraction, Object value) {
|
|
||||||
return new ObjectKeyframe(fraction, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs a Keyframe object with the given time. The value at this time will be derived
|
|
||||||
* from the target object when the animation first starts (note that this implies that keyframes
|
|
||||||
* with no initial value must be used as part of an {@link ObjectAnimator}).
|
|
||||||
* The time defines the
|
|
||||||
* time, as a proportion of an overall animation's duration, at which the value will hold true
|
|
||||||
* for the animation. The value for the animation between keyframes will be calculated as
|
|
||||||
* an interpolation between the values at those keyframes.
|
|
||||||
*
|
|
||||||
* @param fraction The time, expressed as a value between 0 and 1, representing the fraction
|
|
||||||
* of time elapsed of the overall animation duration.
|
|
||||||
*/
|
|
||||||
public static Keyframe ofObject(float fraction) {
|
|
||||||
return new ObjectKeyframe(fraction, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Indicates whether this keyframe has a valid value. This method is called internally when
|
|
||||||
* an {@link ObjectAnimator} first starts; keyframes without values are assigned values at
|
|
||||||
* that time by deriving the value for the property from the target object.
|
|
||||||
*
|
|
||||||
* @return boolean Whether this object has a value assigned.
|
|
||||||
*/
|
|
||||||
public boolean hasValue() {
|
|
||||||
return mHasValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the value for this Keyframe.
|
|
||||||
*
|
|
||||||
* @return The value for this Keyframe.
|
|
||||||
*/
|
|
||||||
public abstract Object getValue();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the value for this Keyframe.
|
|
||||||
*
|
|
||||||
* @param value value for this Keyframe.
|
|
||||||
*/
|
|
||||||
public abstract void setValue(Object value);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the time for this keyframe, as a fraction of the overall animation duration.
|
|
||||||
*
|
|
||||||
* @return The time associated with this keyframe, as a fraction of the overall animation
|
|
||||||
* duration. This should be a value between 0 and 1.
|
|
||||||
*/
|
|
||||||
public float getFraction() {
|
|
||||||
return mFraction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the time for this keyframe, as a fraction of the overall animation duration.
|
|
||||||
*
|
|
||||||
* @param fraction time associated with this keyframe, as a fraction of the overall animation
|
|
||||||
* duration. This should be a value between 0 and 1.
|
|
||||||
*/
|
|
||||||
public void setFraction(float fraction) {
|
|
||||||
mFraction = fraction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
|
|
||||||
* that there is no interpolation, which is the same as linear interpolation.
|
|
||||||
*
|
|
||||||
* @return The optional interpolator for this Keyframe.
|
|
||||||
*/
|
|
||||||
public /*Time*/Interpolator getInterpolator() {
|
|
||||||
return mInterpolator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the optional interpolator for this Keyframe. A value of <code>null</code> indicates
|
|
||||||
* that there is no interpolation, which is the same as linear interpolation.
|
|
||||||
*
|
|
||||||
* @return The optional interpolator for this Keyframe.
|
|
||||||
*/
|
|
||||||
public void setInterpolator(/*Time*/Interpolator interpolator) {
|
|
||||||
mInterpolator = interpolator;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the type of keyframe. This information is used by ValueAnimator to determine the type of
|
|
||||||
* {@link TypeEvaluator} to use when calculating values between keyframes. The type is based
|
|
||||||
* on the type of Keyframe created.
|
|
||||||
*
|
|
||||||
* @return The type of the value stored in the Keyframe.
|
|
||||||
*/
|
|
||||||
public Class getType() {
|
|
||||||
return mValueType;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public abstract Keyframe clone();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This internal subclass is used for all types which are not int or float.
|
|
||||||
*/
|
|
||||||
static class ObjectKeyframe extends Keyframe {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value of the animation at the time mFraction.
|
|
||||||
*/
|
|
||||||
Object mValue;
|
|
||||||
|
|
||||||
ObjectKeyframe(float fraction, Object value) {
|
|
||||||
mFraction = fraction;
|
|
||||||
mValue = value;
|
|
||||||
mHasValue = (value != null);
|
|
||||||
mValueType = mHasValue ? value.getClass() : Object.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getValue() {
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(Object value) {
|
|
||||||
mValue = value;
|
|
||||||
mHasValue = (value != null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ObjectKeyframe clone() {
|
|
||||||
ObjectKeyframe kfClone = new ObjectKeyframe(getFraction(), mValue);
|
|
||||||
kfClone.setInterpolator(getInterpolator());
|
|
||||||
return kfClone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal subclass used when the keyframe value is of type int.
|
|
||||||
*/
|
|
||||||
static class IntKeyframe extends Keyframe {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The value of the animation at the time mFraction.
|
|
||||||
*/
|
|
||||||
int mValue;
|
|
||||||
|
|
||||||
IntKeyframe(float fraction, int value) {
|
|
||||||
mFraction = fraction;
|
|
||||||
mValue = value;
|
|
||||||
mValueType = int.class;
|
|
||||||
mHasValue = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
IntKeyframe(float fraction) {
|
|
||||||
mFraction = fraction;
|
|
||||||
mValueType = int.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getIntValue() {
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getValue() {
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(Object value) {
|
|
||||||
if (value != null && value.getClass() == Integer.class) {
|
|
||||||
mValue = ((Integer)value).intValue();
|
|
||||||
mHasValue = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public IntKeyframe clone() {
|
|
||||||
IntKeyframe kfClone = new IntKeyframe(getFraction(), mValue);
|
|
||||||
kfClone.setInterpolator(getInterpolator());
|
|
||||||
return kfClone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal subclass used when the keyframe value is of type float.
|
|
||||||
*/
|
|
||||||
static class FloatKeyframe extends Keyframe {
|
|
||||||
/**
|
|
||||||
* The value of the animation at the time mFraction.
|
|
||||||
*/
|
|
||||||
float mValue;
|
|
||||||
|
|
||||||
FloatKeyframe(float fraction, float value) {
|
|
||||||
mFraction = fraction;
|
|
||||||
mValue = value;
|
|
||||||
mValueType = float.class;
|
|
||||||
mHasValue = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
FloatKeyframe(float fraction) {
|
|
||||||
mFraction = fraction;
|
|
||||||
mValueType = float.class;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getFloatValue() {
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getValue() {
|
|
||||||
return mValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setValue(Object value) {
|
|
||||||
if (value != null && value.getClass() == Float.class) {
|
|
||||||
mValue = ((Float)value).floatValue();
|
|
||||||
mHasValue = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public FloatKeyframe clone() {
|
|
||||||
FloatKeyframe kfClone = new FloatKeyframe(getFraction(), mValue);
|
|
||||||
kfClone.setInterpolator(getInterpolator());
|
|
||||||
return kfClone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,227 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import android.view.animation.Interpolator;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.FloatKeyframe;
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.IntKeyframe;
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.animation.Keyframe.ObjectKeyframe;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class holds a collection of Keyframe objects and is called by ValueAnimator to calculate
|
|
||||||
* values between those keyframes for a given animation. The class internal to the animation
|
|
||||||
* package because it is an implementation detail of how Keyframes are stored and used.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
|
||||||
class KeyframeSet {
|
|
||||||
|
|
||||||
int mNumKeyframes;
|
|
||||||
|
|
||||||
Keyframe mFirstKeyframe;
|
|
||||||
Keyframe mLastKeyframe;
|
|
||||||
/*Time*/Interpolator mInterpolator; // only used in the 2-keyframe case
|
|
||||||
ArrayList<Keyframe> mKeyframes; // only used when there are not 2 keyframes
|
|
||||||
TypeEvaluator mEvaluator;
|
|
||||||
|
|
||||||
|
|
||||||
public KeyframeSet(Keyframe... keyframes) {
|
|
||||||
mNumKeyframes = keyframes.length;
|
|
||||||
mKeyframes = new ArrayList<Keyframe>();
|
|
||||||
mKeyframes.addAll(Arrays.asList(keyframes));
|
|
||||||
mFirstKeyframe = mKeyframes.get(0);
|
|
||||||
mLastKeyframe = mKeyframes.get(mNumKeyframes - 1);
|
|
||||||
mInterpolator = mLastKeyframe.getInterpolator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyframeSet ofInt(int... values) {
|
|
||||||
int numKeyframes = values.length;
|
|
||||||
IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
|
|
||||||
if (numKeyframes == 1) {
|
|
||||||
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
|
|
||||||
keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
|
|
||||||
} else {
|
|
||||||
keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
|
|
||||||
for (int i = 1; i < numKeyframes; ++i) {
|
|
||||||
keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new IntKeyframeSet(keyframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyframeSet ofFloat(float... values) {
|
|
||||||
int numKeyframes = values.length;
|
|
||||||
FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
|
|
||||||
if (numKeyframes == 1) {
|
|
||||||
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
|
|
||||||
keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
|
|
||||||
} else {
|
|
||||||
keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
|
|
||||||
for (int i = 1; i < numKeyframes; ++i) {
|
|
||||||
keyframes[i] = (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new FloatKeyframeSet(keyframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyframeSet ofKeyframe(Keyframe... keyframes) {
|
|
||||||
// if all keyframes of same primitive type, create the appropriate KeyframeSet
|
|
||||||
int numKeyframes = keyframes.length;
|
|
||||||
boolean hasFloat = false;
|
|
||||||
boolean hasInt = false;
|
|
||||||
boolean hasOther = false;
|
|
||||||
for (int i = 0; i < numKeyframes; ++i) {
|
|
||||||
if (keyframes[i] instanceof FloatKeyframe) {
|
|
||||||
hasFloat = true;
|
|
||||||
} else if (keyframes[i] instanceof IntKeyframe) {
|
|
||||||
hasInt = true;
|
|
||||||
} else {
|
|
||||||
hasOther = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (hasFloat && !hasInt && !hasOther) {
|
|
||||||
FloatKeyframe floatKeyframes[] = new FloatKeyframe[numKeyframes];
|
|
||||||
for (int i = 0; i < numKeyframes; ++i) {
|
|
||||||
floatKeyframes[i] = (FloatKeyframe) keyframes[i];
|
|
||||||
}
|
|
||||||
return new FloatKeyframeSet(floatKeyframes);
|
|
||||||
} else if (hasInt && !hasFloat && !hasOther) {
|
|
||||||
IntKeyframe intKeyframes[] = new IntKeyframe[numKeyframes];
|
|
||||||
for (int i = 0; i < numKeyframes; ++i) {
|
|
||||||
intKeyframes[i] = (IntKeyframe) keyframes[i];
|
|
||||||
}
|
|
||||||
return new IntKeyframeSet(intKeyframes);
|
|
||||||
} else {
|
|
||||||
return new KeyframeSet(keyframes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyframeSet ofObject(Object... values) {
|
|
||||||
int numKeyframes = values.length;
|
|
||||||
ObjectKeyframe keyframes[] = new ObjectKeyframe[Math.max(numKeyframes,2)];
|
|
||||||
if (numKeyframes == 1) {
|
|
||||||
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f);
|
|
||||||
keyframes[1] = (ObjectKeyframe) Keyframe.ofObject(1f, values[0]);
|
|
||||||
} else {
|
|
||||||
keyframes[0] = (ObjectKeyframe) Keyframe.ofObject(0f, values[0]);
|
|
||||||
for (int i = 1; i < numKeyframes; ++i) {
|
|
||||||
keyframes[i] = (ObjectKeyframe) Keyframe.ofObject((float) i / (numKeyframes - 1), values[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return new KeyframeSet(keyframes);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the TypeEvaluator to be used when calculating animated values. This object
|
|
||||||
* is required only for KeyframeSets that are not either IntKeyframeSet or FloatKeyframeSet,
|
|
||||||
* both of which assume their own evaluator to speed up calculations with those primitive
|
|
||||||
* types.
|
|
||||||
*
|
|
||||||
* @param evaluator The TypeEvaluator to be used to calculate animated values.
|
|
||||||
*/
|
|
||||||
public void setEvaluator(TypeEvaluator evaluator) {
|
|
||||||
mEvaluator = evaluator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public KeyframeSet clone() {
|
|
||||||
ArrayList<Keyframe> keyframes = mKeyframes;
|
|
||||||
int numKeyframes = mKeyframes.size();
|
|
||||||
Keyframe[] newKeyframes = new Keyframe[numKeyframes];
|
|
||||||
for (int i = 0; i < numKeyframes; ++i) {
|
|
||||||
newKeyframes[i] = keyframes.get(i).clone();
|
|
||||||
}
|
|
||||||
KeyframeSet newSet = new KeyframeSet(newKeyframes);
|
|
||||||
return newSet;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the animated value, given the elapsed fraction of the animation (interpolated by the
|
|
||||||
* animation's interpolator) and the evaluator used to calculate in-between values. This
|
|
||||||
* function maps the input fraction to the appropriate keyframe interval and a fraction
|
|
||||||
* between them and returns the interpolated value. Note that the input fraction may fall
|
|
||||||
* outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a
|
|
||||||
* spring interpolation that might send the fraction past 1.0). We handle this situation by
|
|
||||||
* just using the two keyframes at the appropriate end when the value is outside those bounds.
|
|
||||||
*
|
|
||||||
* @param fraction The elapsed fraction of the animation
|
|
||||||
* @return The animated value.
|
|
||||||
*/
|
|
||||||
public Object getValue(float fraction) {
|
|
||||||
|
|
||||||
// Special-case optimization for the common case of only two keyframes
|
|
||||||
if (mNumKeyframes == 2) {
|
|
||||||
if (mInterpolator != null) {
|
|
||||||
fraction = mInterpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(),
|
|
||||||
mLastKeyframe.getValue());
|
|
||||||
}
|
|
||||||
if (fraction <= 0f) {
|
|
||||||
final Keyframe nextKeyframe = mKeyframes.get(1);
|
|
||||||
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
final float prevFraction = mFirstKeyframe.getFraction();
|
|
||||||
float intervalFraction = (fraction - prevFraction) /
|
|
||||||
(nextKeyframe.getFraction() - prevFraction);
|
|
||||||
return mEvaluator.evaluate(intervalFraction, mFirstKeyframe.getValue(),
|
|
||||||
nextKeyframe.getValue());
|
|
||||||
} else if (fraction >= 1f) {
|
|
||||||
final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2);
|
|
||||||
final /*Time*/Interpolator interpolator = mLastKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
final float prevFraction = prevKeyframe.getFraction();
|
|
||||||
float intervalFraction = (fraction - prevFraction) /
|
|
||||||
(mLastKeyframe.getFraction() - prevFraction);
|
|
||||||
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
|
|
||||||
mLastKeyframe.getValue());
|
|
||||||
}
|
|
||||||
Keyframe prevKeyframe = mFirstKeyframe;
|
|
||||||
for (int i = 1; i < mNumKeyframes; ++i) {
|
|
||||||
Keyframe nextKeyframe = mKeyframes.get(i);
|
|
||||||
if (fraction < nextKeyframe.getFraction()) {
|
|
||||||
final /*Time*/Interpolator interpolator = nextKeyframe.getInterpolator();
|
|
||||||
if (interpolator != null) {
|
|
||||||
fraction = interpolator.getInterpolation(fraction);
|
|
||||||
}
|
|
||||||
final float prevFraction = prevKeyframe.getFraction();
|
|
||||||
float intervalFraction = (fraction - prevFraction) /
|
|
||||||
(nextKeyframe.getFraction() - prevFraction);
|
|
||||||
return mEvaluator.evaluate(intervalFraction, prevKeyframe.getValue(),
|
|
||||||
nextKeyframe.getValue());
|
|
||||||
}
|
|
||||||
prevKeyframe = nextKeyframe;
|
|
||||||
}
|
|
||||||
// shouldn't reach here
|
|
||||||
return mLastKeyframe.getValue();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String returnVal = " ";
|
|
||||||
for (int i = 0; i < mNumKeyframes; ++i) {
|
|
||||||
returnVal += mKeyframes.get(i).getValue() + " ";
|
|
||||||
}
|
|
||||||
return returnVal;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,491 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
//import android.util.Property;
|
|
||||||
|
|
||||||
//import java.lang.reflect.Method;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This subclass of {@link ValueAnimator} provides support for animating properties on target objects.
|
|
||||||
* The constructors of this class take parameters to define the target object that will be animated
|
|
||||||
* as well as the name of the property that will be animated. Appropriate set/get functions
|
|
||||||
* are then determined internally and the animation will call these functions as necessary to
|
|
||||||
* animate the property.
|
|
||||||
*
|
|
||||||
* @see #setPropertyName(String)
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
public final class ObjectAnimator extends ValueAnimator {
|
|
||||||
private static final boolean DBG = false;
|
|
||||||
|
|
||||||
// The target object on which the property exists, set in the constructor
|
|
||||||
private Object mTarget;
|
|
||||||
|
|
||||||
private String mPropertyName;
|
|
||||||
|
|
||||||
//private Property mProperty;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the name of the property that will be animated. This name is used to derive
|
|
||||||
* a setter function that will be called to set animated values.
|
|
||||||
* For example, a property name of <code>foo</code> will result
|
|
||||||
* in a call to the function <code>setFoo()</code> on the target object. If either
|
|
||||||
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
|
|
||||||
* also be derived and called.
|
|
||||||
*
|
|
||||||
* <p>For best performance of the mechanism that calls the setter function determined by the
|
|
||||||
* name of the property being animated, use <code>float</code> or <code>int</code> typed values,
|
|
||||||
* and make the setter function for those properties have a <code>void</code> return value. This
|
|
||||||
* will cause the code to take an optimized path for these constrained circumstances. Other
|
|
||||||
* property types and return types will work, but will have more overhead in processing
|
|
||||||
* the requests due to normal reflection mechanisms.</p>
|
|
||||||
*
|
|
||||||
* <p>Note that the setter function derived from this property name
|
|
||||||
* must take the same parameter type as the
|
|
||||||
* <code>valueFrom</code> and <code>valueTo</code> properties, otherwise the call to
|
|
||||||
* the setter function will fail.</p>
|
|
||||||
*
|
|
||||||
* <p>If this ObjectAnimator has been set up to animate several properties together,
|
|
||||||
* using more than one PropertyValuesHolder objects, then setting the propertyName simply
|
|
||||||
* sets the propertyName in the first of those PropertyValuesHolder objects.</p>
|
|
||||||
*
|
|
||||||
* @param propertyName The name of the property being animated. Should not be null.
|
|
||||||
*/
|
|
||||||
public void setPropertyName(String propertyName) {
|
|
||||||
// mValues could be null if this is being constructed piecemeal. Just record the
|
|
||||||
// propertyName to be used later when setValues() is called if so.
|
|
||||||
if (mValues != null) {
|
|
||||||
PropertyValuesHolder valuesHolder = mValues[0];
|
|
||||||
String oldName = valuesHolder.getPropertyName();
|
|
||||||
valuesHolder.setPropertyName(propertyName);
|
|
||||||
mValuesMap.remove(oldName);
|
|
||||||
mValuesMap.put(propertyName, valuesHolder);
|
|
||||||
}
|
|
||||||
mPropertyName = propertyName;
|
|
||||||
// New property/values/target should cause re-initialization prior to starting
|
|
||||||
mInitialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the property that will be animated. Property objects will take precedence over
|
|
||||||
* properties specified by the {@link #setPropertyName(String)} method. Animations should
|
|
||||||
* be set up to use one or the other, not both.
|
|
||||||
*
|
|
||||||
* @param property The property being animated. Should not be null.
|
|
||||||
*/
|
|
||||||
//public void setProperty(Property property) {
|
|
||||||
// // mValues could be null if this is being constructed piecemeal. Just record the
|
|
||||||
// // propertyName to be used later when setValues() is called if so.
|
|
||||||
// if (mValues != null) {
|
|
||||||
// PropertyValuesHolder valuesHolder = mValues[0];
|
|
||||||
// String oldName = valuesHolder.getPropertyName();
|
|
||||||
// valuesHolder.setProperty(property);
|
|
||||||
// mValuesMap.remove(oldName);
|
|
||||||
// mValuesMap.put(mPropertyName, valuesHolder);
|
|
||||||
// }
|
|
||||||
// if (mProperty != null) {
|
|
||||||
// mPropertyName = property.getName();
|
|
||||||
// }
|
|
||||||
// mProperty = property;
|
|
||||||
// // New property/values/target should cause re-initialization prior to starting
|
|
||||||
// mInitialized = false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the property that will be animated. This name will be used to derive
|
|
||||||
* a setter function that will be called to set animated values.
|
|
||||||
* For example, a property name of <code>foo</code> will result
|
|
||||||
* in a call to the function <code>setFoo()</code> on the target object. If either
|
|
||||||
* <code>valueFrom</code> or <code>valueTo</code> is null, then a getter function will
|
|
||||||
* also be derived and called.
|
|
||||||
*/
|
|
||||||
public String getPropertyName() {
|
|
||||||
return mPropertyName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new ObjectAnimator object. This default constructor is primarily for
|
|
||||||
* use internally; the other constructors which take parameters are more generally
|
|
||||||
* useful.
|
|
||||||
*/
|
|
||||||
public ObjectAnimator() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private utility constructor that initializes the target object and name of the
|
|
||||||
* property being animated.
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated. This object should
|
|
||||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
|
||||||
* the value of the <code>propertyName</code> parameter.
|
|
||||||
* @param propertyName The name of the property being animated.
|
|
||||||
*/
|
|
||||||
private ObjectAnimator(Object target, String propertyName) {
|
|
||||||
mTarget = target;
|
|
||||||
setPropertyName(propertyName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Private utility constructor that initializes the target object and property being animated.
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated.
|
|
||||||
* @param property The property being animated.
|
|
||||||
*/
|
|
||||||
//private <T> ObjectAnimator(T target, Property<T, ?> property) {
|
|
||||||
// mTarget = target;
|
|
||||||
// setProperty(property);
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs and returns an ObjectAnimator that animates between int values. A single
|
|
||||||
* value implies that that value is the one being animated to. Two values imply a starting
|
|
||||||
* and ending values. More than two values imply a starting value, values to animate through
|
|
||||||
* along the way, and an ending value (these values will be distributed evenly across
|
|
||||||
* the duration of the animation).
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated. This object should
|
|
||||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
|
||||||
* the value of the <code>propertyName</code> parameter.
|
|
||||||
* @param propertyName The name of the property being animated.
|
|
||||||
* @param values A set of values that the animation will animate between over time.
|
|
||||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
|
||||||
*/
|
|
||||||
public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
|
|
||||||
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
|
|
||||||
anim.setIntValues(values);
|
|
||||||
return anim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs and returns an ObjectAnimator that animates between int values. A single
|
|
||||||
* value implies that that value is the one being animated to. Two values imply a starting
|
|
||||||
* and ending values. More than two values imply a starting value, values to animate through
|
|
||||||
* along the way, and an ending value (these values will be distributed evenly across
|
|
||||||
* the duration of the animation).
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated.
|
|
||||||
* @param property The property being animated.
|
|
||||||
* @param values A set of values that the animation will animate between over time.
|
|
||||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
|
||||||
*/
|
|
||||||
//public static <T> ObjectAnimator ofInt(T target, Property<T, Integer> property, int... values) {
|
|
||||||
// ObjectAnimator anim = new ObjectAnimator(target, property);
|
|
||||||
// anim.setIntValues(values);
|
|
||||||
// return anim;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs and returns an ObjectAnimator that animates between float values. A single
|
|
||||||
* value implies that that value is the one being animated to. Two values imply a starting
|
|
||||||
* and ending values. More than two values imply a starting value, values to animate through
|
|
||||||
* along the way, and an ending value (these values will be distributed evenly across
|
|
||||||
* the duration of the animation).
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated. This object should
|
|
||||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
|
||||||
* the value of the <code>propertyName</code> parameter.
|
|
||||||
* @param propertyName The name of the property being animated.
|
|
||||||
* @param values A set of values that the animation will animate between over time.
|
|
||||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
|
||||||
*/
|
|
||||||
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
|
|
||||||
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
|
|
||||||
anim.setFloatValues(values);
|
|
||||||
return anim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs and returns an ObjectAnimator that animates between float values. A single
|
|
||||||
* value implies that that value is the one being animated to. Two values imply a starting
|
|
||||||
* and ending values. More than two values imply a starting value, values to animate through
|
|
||||||
* along the way, and an ending value (these values will be distributed evenly across
|
|
||||||
* the duration of the animation).
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated.
|
|
||||||
* @param property The property being animated.
|
|
||||||
* @param values A set of values that the animation will animate between over time.
|
|
||||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
|
||||||
*/
|
|
||||||
//public static <T> ObjectAnimator ofFloat(T target, Property<T, Float> property,
|
|
||||||
// float... values) {
|
|
||||||
// ObjectAnimator anim = new ObjectAnimator(target, property);
|
|
||||||
// anim.setFloatValues(values);
|
|
||||||
// return anim;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs and returns an ObjectAnimator that animates between Object values. A single
|
|
||||||
* value implies that that value is the one being animated to. Two values imply a starting
|
|
||||||
* and ending values. More than two values imply a starting value, values to animate through
|
|
||||||
* along the way, and an ending value (these values will be distributed evenly across
|
|
||||||
* the duration of the animation).
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated. This object should
|
|
||||||
* have a public method on it called <code>setName()</code>, where <code>name</code> is
|
|
||||||
* the value of the <code>propertyName</code> parameter.
|
|
||||||
* @param propertyName The name of the property being animated.
|
|
||||||
* @param evaluator A TypeEvaluator that will be called on each animation frame to
|
|
||||||
* provide the necessary interpolation between the Object values to derive the animated
|
|
||||||
* value.
|
|
||||||
* @param values A set of values that the animation will animate between over time.
|
|
||||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
|
||||||
*/
|
|
||||||
public static ObjectAnimator ofObject(Object target, String propertyName,
|
|
||||||
TypeEvaluator evaluator, Object... values) {
|
|
||||||
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
|
|
||||||
anim.setObjectValues(values);
|
|
||||||
anim.setEvaluator(evaluator);
|
|
||||||
return anim;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs and returns an ObjectAnimator that animates between Object values. A single
|
|
||||||
* value implies that that value is the one being animated to. Two values imply a starting
|
|
||||||
* and ending values. More than two values imply a starting value, values to animate through
|
|
||||||
* along the way, and an ending value (these values will be distributed evenly across
|
|
||||||
* the duration of the animation).
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated.
|
|
||||||
* @param property The property being animated.
|
|
||||||
* @param evaluator A TypeEvaluator that will be called on each animation frame to
|
|
||||||
* provide the necessary interpolation between the Object values to derive the animated
|
|
||||||
* value.
|
|
||||||
* @param values A set of values that the animation will animate between over time.
|
|
||||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
|
||||||
*/
|
|
||||||
//public static <T, V> ObjectAnimator ofObject(T target, Property<T, V> property,
|
|
||||||
// TypeEvaluator<V> evaluator, V... values) {
|
|
||||||
// ObjectAnimator anim = new ObjectAnimator(target, property);
|
|
||||||
// anim.setObjectValues(values);
|
|
||||||
// anim.setEvaluator(evaluator);
|
|
||||||
// return anim;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructs and returns an ObjectAnimator that animates between the sets of values specified
|
|
||||||
* in <code>PropertyValueHolder</code> objects. This variant should be used when animating
|
|
||||||
* several properties at once with the same ObjectAnimator, since PropertyValuesHolder allows
|
|
||||||
* you to associate a set of animation values with a property name.
|
|
||||||
*
|
|
||||||
* @param target The object whose property is to be animated. Depending on how the
|
|
||||||
* PropertyValuesObjects were constructed, the target object should either have the {@link
|
|
||||||
* android.util.Property} objects used to construct the PropertyValuesHolder objects or (if the
|
|
||||||
* PropertyValuesHOlder objects were created with property names) the target object should have
|
|
||||||
* public methods on it called <code>setName()</code>, where <code>name</code> is the name of
|
|
||||||
* the property passed in as the <code>propertyName</code> parameter for each of the
|
|
||||||
* PropertyValuesHolder objects.
|
|
||||||
* @param values A set of PropertyValuesHolder objects whose values will be animated between
|
|
||||||
* over time.
|
|
||||||
* @return An ObjectAnimator object that is set up to animate between the given values.
|
|
||||||
*/
|
|
||||||
public static ObjectAnimator ofPropertyValuesHolder(Object target,
|
|
||||||
PropertyValuesHolder... values) {
|
|
||||||
ObjectAnimator anim = new ObjectAnimator();
|
|
||||||
anim.mTarget = target;
|
|
||||||
anim.setValues(values);
|
|
||||||
return anim;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setIntValues(int... values) {
|
|
||||||
if (mValues == null || mValues.length == 0) {
|
|
||||||
// No values yet - this animator is being constructed piecemeal. Init the values with
|
|
||||||
// whatever the current propertyName is
|
|
||||||
//if (mProperty != null) {
|
|
||||||
// setValues(PropertyValuesHolder.ofInt(mProperty, values));
|
|
||||||
//} else {
|
|
||||||
setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
|
|
||||||
//}
|
|
||||||
} else {
|
|
||||||
super.setIntValues(values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFloatValues(float... values) {
|
|
||||||
if (mValues == null || mValues.length == 0) {
|
|
||||||
// No values yet - this animator is being constructed piecemeal. Init the values with
|
|
||||||
// whatever the current propertyName is
|
|
||||||
//if (mProperty != null) {
|
|
||||||
// setValues(PropertyValuesHolder.ofFloat(mProperty, values));
|
|
||||||
//} else {
|
|
||||||
setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
|
|
||||||
//}
|
|
||||||
} else {
|
|
||||||
super.setFloatValues(values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setObjectValues(Object... values) {
|
|
||||||
if (mValues == null || mValues.length == 0) {
|
|
||||||
// No values yet - this animator is being constructed piecemeal. Init the values with
|
|
||||||
// whatever the current propertyName is
|
|
||||||
//if (mProperty != null) {
|
|
||||||
// setValues(PropertyValuesHolder.ofObject(mProperty, (TypeEvaluator)null, values));
|
|
||||||
//} else {
|
|
||||||
setValues(PropertyValuesHolder.ofObject(mPropertyName, (TypeEvaluator)null, values));
|
|
||||||
//}
|
|
||||||
} else {
|
|
||||||
super.setObjectValues(values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void start() {
|
|
||||||
if (DBG) {
|
|
||||||
Log.d("ObjectAnimator", "Anim target, duration: " + mTarget + ", " + getDuration());
|
|
||||||
for (int i = 0; i < mValues.length; ++i) {
|
|
||||||
PropertyValuesHolder pvh = mValues[i];
|
|
||||||
ArrayList<Keyframe> keyframes = pvh.mKeyframeSet.mKeyframes;
|
|
||||||
Log.d("ObjectAnimator", " Values[" + i + "]: " +
|
|
||||||
pvh.getPropertyName() + ", " + keyframes.get(0).getValue() + ", " +
|
|
||||||
keyframes.get(pvh.mKeyframeSet.mNumKeyframes - 1).getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function is called immediately before processing the first animation
|
|
||||||
* frame of an animation. If there is a nonzero <code>startDelay</code>, the
|
|
||||||
* function is called after that delay ends.
|
|
||||||
* It takes care of the final initialization steps for the
|
|
||||||
* animation. This includes setting mEvaluator, if the user has not yet
|
|
||||||
* set it up, and the setter/getter methods, if the user did not supply
|
|
||||||
* them.
|
|
||||||
*
|
|
||||||
* <p>Overriders of this method should call the superclass method to cause
|
|
||||||
* internal mechanisms to be set up correctly.</p>
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
void initAnimation() {
|
|
||||||
if (!mInitialized) {
|
|
||||||
// mValueType may change due to setter/getter setup; do this before calling super.init(),
|
|
||||||
// which uses mValueType to set up the default type evaluator.
|
|
||||||
int numValues = mValues.length;
|
|
||||||
for (int i = 0; i < numValues; ++i) {
|
|
||||||
mValues[i].setupSetterAndGetter(mTarget);
|
|
||||||
}
|
|
||||||
super.initAnimation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the length of the animation. The default duration is 300 milliseconds.
|
|
||||||
*
|
|
||||||
* @param duration The length of the animation, in milliseconds.
|
|
||||||
* @return ObjectAnimator The object called with setDuration(). This return
|
|
||||||
* value makes it easier to compose statements together that construct and then set the
|
|
||||||
* duration, as in
|
|
||||||
* <code>ObjectAnimator.ofInt(target, propertyName, 0, 10).setDuration(500).start()</code>.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public ObjectAnimator setDuration(long duration) {
|
|
||||||
super.setDuration(duration);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The target object whose property will be animated by this animation
|
|
||||||
*
|
|
||||||
* @return The object being animated
|
|
||||||
*/
|
|
||||||
public Object getTarget() {
|
|
||||||
return mTarget;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the target object whose property will be animated by this animation
|
|
||||||
*
|
|
||||||
* @param target The object being animated
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setTarget(Object target) {
|
|
||||||
if (mTarget != target) {
|
|
||||||
final Object oldTarget = mTarget;
|
|
||||||
mTarget = target;
|
|
||||||
if (oldTarget != null && target != null && oldTarget.getClass() == target.getClass()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// New target type should cause re-initialization prior to starting
|
|
||||||
mInitialized = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupStartValues() {
|
|
||||||
initAnimation();
|
|
||||||
int numValues = mValues.length;
|
|
||||||
for (int i = 0; i < numValues; ++i) {
|
|
||||||
mValues[i].setupStartValue(mTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setupEndValues() {
|
|
||||||
initAnimation();
|
|
||||||
int numValues = mValues.length;
|
|
||||||
for (int i = 0; i < numValues; ++i) {
|
|
||||||
mValues[i].setupEndValue(mTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This method is called with the elapsed fraction of the animation during every
|
|
||||||
* animation frame. This function turns the elapsed fraction into an interpolated fraction
|
|
||||||
* and then into an animated value (from the evaluator. The function is called mostly during
|
|
||||||
* animation updates, but it is also called when the <code>end()</code>
|
|
||||||
* function is called, to set the final value on the property.
|
|
||||||
*
|
|
||||||
* <p>Overrides of this method must call the superclass to perform the calculation
|
|
||||||
* of the animated value.</p>
|
|
||||||
*
|
|
||||||
* @param fraction The elapsed fraction of the animation.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
void animateValue(float fraction) {
|
|
||||||
super.animateValue(fraction);
|
|
||||||
int numValues = mValues.length;
|
|
||||||
for (int i = 0; i < numValues; ++i) {
|
|
||||||
mValues[i].setAnimatedValue(mTarget);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ObjectAnimator clone() {
|
|
||||||
final ObjectAnimator anim = (ObjectAnimator) super.clone();
|
|
||||||
return anim;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
String returnVal = "ObjectAnimator@" + Integer.toHexString(hashCode()) + ", target " +
|
|
||||||
mTarget;
|
|
||||||
if (mValues != null) {
|
|
||||||
for (int i = 0; i < mValues.length; ++i) {
|
|
||||||
returnVal += "\n " + mValues[i].toString();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return returnVal;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.nineoldandroids.animation;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for use with the {@link ValueAnimator#setEvaluator(TypeEvaluator)} function. Evaluators
|
|
||||||
* allow developers to create animations on arbitrary property types, by allowing them to supply
|
|
||||||
* custom evaulators for types that are not automatically understood and used by the animation
|
|
||||||
* system.
|
|
||||||
*
|
|
||||||
* @see ValueAnimator#setEvaluator(TypeEvaluator)
|
|
||||||
*/
|
|
||||||
public interface TypeEvaluator<T> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function returns the result of linearly interpolating the start and end values, with
|
|
||||||
* <code>fraction</code> representing the proportion between the start and end values. The
|
|
||||||
* calculation is a simple parametric calculation: <code>result = x0 + t * (v1 - v0)</code>,
|
|
||||||
* where <code>x0</code> is <code>startValue</code>, <code>x1</code> is <code>endValue</code>,
|
|
||||||
* and <code>t</code> is <code>fraction</code>.
|
|
||||||
*
|
|
||||||
* @param fraction The fraction from the starting to the ending values
|
|
||||||
* @param startValue The start value.
|
|
||||||
* @param endValue The end value.
|
|
||||||
* @return A linear interpolation between the start and end values, given the
|
|
||||||
* <code>fraction</code> parameter.
|
|
||||||
*/
|
|
||||||
public T evaluate(float fraction, T startValue, T endValue);
|
|
||||||
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,79 +0,0 @@
|
|||||||
package com.actionbarsherlock.internal.nineoldandroids.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
|
|
||||||
|
|
||||||
public abstract class NineViewGroup extends ViewGroup {
|
|
||||||
private final AnimatorProxy mProxy;
|
|
||||||
|
|
||||||
public NineViewGroup(Context context) {
|
|
||||||
super(context);
|
|
||||||
mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
|
|
||||||
}
|
|
||||||
public NineViewGroup(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
|
|
||||||
}
|
|
||||||
public NineViewGroup(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setVisibility(int visibility) {
|
|
||||||
if (mProxy != null) {
|
|
||||||
if (visibility == GONE) {
|
|
||||||
clearAnimation();
|
|
||||||
} else if (visibility == VISIBLE) {
|
|
||||||
setAnimation(mProxy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.setVisibility(visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAlpha() {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
return mProxy.getAlpha();
|
|
||||||
} else {
|
|
||||||
return super.getAlpha();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setAlpha(float alpha) {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
mProxy.setAlpha(alpha);
|
|
||||||
} else {
|
|
||||||
super.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public float getTranslationX() {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
return mProxy.getTranslationX();
|
|
||||||
} else {
|
|
||||||
return super.getTranslationX();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setTranslationX(float translationX) {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
mProxy.setTranslationX(translationX);
|
|
||||||
} else {
|
|
||||||
super.setTranslationX(translationX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public float getTranslationY() {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
return mProxy.getTranslationY();
|
|
||||||
} else {
|
|
||||||
return super.getTranslationY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setTranslationY(float translationY) {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
mProxy.setTranslationY(translationY);
|
|
||||||
} else {
|
|
||||||
super.setTranslationY(translationY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,212 +0,0 @@
|
|||||||
package com.actionbarsherlock.internal.nineoldandroids.view.animation;
|
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
import android.graphics.Matrix;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.FloatMath;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.animation.Animation;
|
|
||||||
import android.view.animation.Transformation;
|
|
||||||
|
|
||||||
public final class AnimatorProxy extends Animation {
|
|
||||||
public static final boolean NEEDS_PROXY = Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB;
|
|
||||||
|
|
||||||
private static final WeakHashMap<View, AnimatorProxy> PROXIES =
|
|
||||||
new WeakHashMap<View, AnimatorProxy>();
|
|
||||||
|
|
||||||
public static AnimatorProxy wrap(View view) {
|
|
||||||
AnimatorProxy proxy = PROXIES.get(view);
|
|
||||||
if (proxy == null) {
|
|
||||||
proxy = new AnimatorProxy(view);
|
|
||||||
PROXIES.put(view, proxy);
|
|
||||||
}
|
|
||||||
return proxy;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final WeakReference<View> mView;
|
|
||||||
|
|
||||||
private float mAlpha = 1;
|
|
||||||
private float mScaleX = 1;
|
|
||||||
private float mScaleY = 1;
|
|
||||||
private float mTranslationX;
|
|
||||||
private float mTranslationY;
|
|
||||||
|
|
||||||
private final RectF mBefore = new RectF();
|
|
||||||
private final RectF mAfter = new RectF();
|
|
||||||
private final Matrix mTempMatrix = new Matrix();
|
|
||||||
|
|
||||||
private AnimatorProxy(View view) {
|
|
||||||
setDuration(0); //perform transformation immediately
|
|
||||||
setFillAfter(true); //persist transformation beyond duration
|
|
||||||
view.setAnimation(this);
|
|
||||||
mView = new WeakReference<View>(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAlpha() {
|
|
||||||
return mAlpha;
|
|
||||||
}
|
|
||||||
public void setAlpha(float alpha) {
|
|
||||||
if (mAlpha != alpha) {
|
|
||||||
mAlpha = alpha;
|
|
||||||
View view = mView.get();
|
|
||||||
if (view != null) {
|
|
||||||
view.invalidate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public float getScaleX() {
|
|
||||||
return mScaleX;
|
|
||||||
}
|
|
||||||
public void setScaleX(float scaleX) {
|
|
||||||
if (mScaleX != scaleX) {
|
|
||||||
prepareForUpdate();
|
|
||||||
mScaleX = scaleX;
|
|
||||||
invalidateAfterUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public float getScaleY() {
|
|
||||||
return mScaleY;
|
|
||||||
}
|
|
||||||
public void setScaleY(float scaleY) {
|
|
||||||
if (mScaleY != scaleY) {
|
|
||||||
prepareForUpdate();
|
|
||||||
mScaleY = scaleY;
|
|
||||||
invalidateAfterUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public int getScrollX() {
|
|
||||||
View view = mView.get();
|
|
||||||
if (view == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return view.getScrollX();
|
|
||||||
}
|
|
||||||
public void setScrollX(int value) {
|
|
||||||
View view = mView.get();
|
|
||||||
if (view != null) {
|
|
||||||
view.scrollTo(value, view.getScrollY());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public int getScrollY() {
|
|
||||||
View view = mView.get();
|
|
||||||
if (view == null) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
return view.getScrollY();
|
|
||||||
}
|
|
||||||
public void setScrollY(int value) {
|
|
||||||
View view = mView.get();
|
|
||||||
if (view != null) {
|
|
||||||
view.scrollTo(view.getScrollY(), value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getTranslationX() {
|
|
||||||
return mTranslationX;
|
|
||||||
}
|
|
||||||
public void setTranslationX(float translationX) {
|
|
||||||
if (mTranslationX != translationX) {
|
|
||||||
prepareForUpdate();
|
|
||||||
mTranslationX = translationX;
|
|
||||||
invalidateAfterUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public float getTranslationY() {
|
|
||||||
return mTranslationY;
|
|
||||||
}
|
|
||||||
public void setTranslationY(float translationY) {
|
|
||||||
if (mTranslationY != translationY) {
|
|
||||||
prepareForUpdate();
|
|
||||||
mTranslationY = translationY;
|
|
||||||
invalidateAfterUpdate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void prepareForUpdate() {
|
|
||||||
View view = mView.get();
|
|
||||||
if (view != null) {
|
|
||||||
computeRect(mBefore, view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void invalidateAfterUpdate() {
|
|
||||||
View view = mView.get();
|
|
||||||
if (view == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
View parent = (View)view.getParent();
|
|
||||||
if (parent == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
view.setAnimation(this);
|
|
||||||
|
|
||||||
final RectF after = mAfter;
|
|
||||||
computeRect(after, view);
|
|
||||||
after.union(mBefore);
|
|
||||||
|
|
||||||
parent.invalidate(
|
|
||||||
(int) FloatMath.floor(after.left),
|
|
||||||
(int) FloatMath.floor(after.top),
|
|
||||||
(int) FloatMath.ceil(after.right),
|
|
||||||
(int) FloatMath.ceil(after.bottom));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void computeRect(final RectF r, View view) {
|
|
||||||
// compute current rectangle according to matrix transformation
|
|
||||||
final float w = view.getWidth();
|
|
||||||
final float h = view.getHeight();
|
|
||||||
|
|
||||||
// use a rectangle at 0,0 to make sure we don't run into issues with scaling
|
|
||||||
r.set(0, 0, w, h);
|
|
||||||
|
|
||||||
final Matrix m = mTempMatrix;
|
|
||||||
m.reset();
|
|
||||||
transformMatrix(m, view);
|
|
||||||
mTempMatrix.mapRect(r);
|
|
||||||
|
|
||||||
r.offset(view.getLeft(), view.getTop());
|
|
||||||
|
|
||||||
// Straighten coords if rotations flipped them
|
|
||||||
if (r.right < r.left) {
|
|
||||||
final float f = r.right;
|
|
||||||
r.right = r.left;
|
|
||||||
r.left = f;
|
|
||||||
}
|
|
||||||
if (r.bottom < r.top) {
|
|
||||||
final float f = r.top;
|
|
||||||
r.top = r.bottom;
|
|
||||||
r.bottom = f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void transformMatrix(Matrix m, View view) {
|
|
||||||
final float w = view.getWidth();
|
|
||||||
final float h = view.getHeight();
|
|
||||||
|
|
||||||
final float sX = mScaleX;
|
|
||||||
final float sY = mScaleY;
|
|
||||||
if ((sX != 1.0f) || (sY != 1.0f)) {
|
|
||||||
final float deltaSX = ((sX * w) - w) / 2f;
|
|
||||||
final float deltaSY = ((sY * h) - h) / 2f;
|
|
||||||
m.postScale(sX, sY);
|
|
||||||
m.postTranslate(-deltaSX, -deltaSY);
|
|
||||||
}
|
|
||||||
m.postTranslate(mTranslationX, mTranslationY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void applyTransformation(float interpolatedTime, Transformation t) {
|
|
||||||
View view = mView.get();
|
|
||||||
if (view != null) {
|
|
||||||
t.setAlpha(mAlpha);
|
|
||||||
transformMatrix(t.getMatrix(), view);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void reset() {
|
|
||||||
/* Do nothing. */
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package com.actionbarsherlock.internal.nineoldandroids.widget;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
|
|
||||||
|
|
||||||
public class NineFrameLayout extends FrameLayout {
|
|
||||||
private final AnimatorProxy mProxy;
|
|
||||||
|
|
||||||
public NineFrameLayout(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setVisibility(int visibility) {
|
|
||||||
if (mProxy != null) {
|
|
||||||
if (visibility == GONE) {
|
|
||||||
clearAnimation();
|
|
||||||
} else if (visibility == VISIBLE) {
|
|
||||||
setAnimation(mProxy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.setVisibility(visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAlpha() {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
return mProxy.getAlpha();
|
|
||||||
} else {
|
|
||||||
return super.getAlpha();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setAlpha(float alpha) {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
mProxy.setAlpha(alpha);
|
|
||||||
} else {
|
|
||||||
super.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public float getTranslationY() {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
return mProxy.getTranslationY();
|
|
||||||
} else {
|
|
||||||
return super.getTranslationY();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setTranslationY(float translationY) {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
mProxy.setTranslationY(translationY);
|
|
||||||
} else {
|
|
||||||
super.setTranslationY(translationY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package com.actionbarsherlock.internal.nineoldandroids.widget;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.widget.HorizontalScrollView;
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
|
|
||||||
|
|
||||||
public class NineHorizontalScrollView extends HorizontalScrollView {
|
|
||||||
private final AnimatorProxy mProxy;
|
|
||||||
|
|
||||||
public NineHorizontalScrollView(Context context) {
|
|
||||||
super(context);
|
|
||||||
mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setVisibility(int visibility) {
|
|
||||||
if (mProxy != null) {
|
|
||||||
if (visibility == GONE) {
|
|
||||||
clearAnimation();
|
|
||||||
} else if (visibility == VISIBLE) {
|
|
||||||
setAnimation(mProxy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.setVisibility(visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAlpha() {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
return mProxy.getAlpha();
|
|
||||||
} else {
|
|
||||||
return super.getAlpha();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setAlpha(float alpha) {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
mProxy.setAlpha(alpha);
|
|
||||||
} else {
|
|
||||||
super.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
package com.actionbarsherlock.internal.nineoldandroids.widget;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.internal.nineoldandroids.view.animation.AnimatorProxy;
|
|
||||||
|
|
||||||
public class NineLinearLayout extends LinearLayout {
|
|
||||||
private final AnimatorProxy mProxy;
|
|
||||||
|
|
||||||
public NineLinearLayout(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
mProxy = AnimatorProxy.NEEDS_PROXY ? AnimatorProxy.wrap(this) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setVisibility(int visibility) {
|
|
||||||
if (mProxy != null) {
|
|
||||||
if (visibility == GONE) {
|
|
||||||
clearAnimation();
|
|
||||||
} else if (visibility == VISIBLE) {
|
|
||||||
setAnimation(mProxy);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.setVisibility(visibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
public float getAlpha() {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
return mProxy.getAlpha();
|
|
||||||
} else {
|
|
||||||
return super.getAlpha();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setAlpha(float alpha) {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
mProxy.setAlpha(alpha);
|
|
||||||
} else {
|
|
||||||
super.setAlpha(alpha);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public float getTranslationX() {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
return mProxy.getTranslationX();
|
|
||||||
} else {
|
|
||||||
return super.getTranslationX();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void setTranslationX(float translationX) {
|
|
||||||
if (AnimatorProxy.NEEDS_PROXY) {
|
|
||||||
mProxy.setTranslationX(translationX);
|
|
||||||
} else {
|
|
||||||
super.setTranslationX(translationX);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,264 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.PackageManager;
|
|
||||||
import android.content.pm.ResolveInfo;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.view.Menu;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
import com.actionbarsherlock.view.SubMenu;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public class ActionMenu implements Menu {
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
private boolean mIsQwerty;
|
|
||||||
|
|
||||||
private ArrayList<ActionMenuItem> mItems;
|
|
||||||
|
|
||||||
public ActionMenu(Context context) {
|
|
||||||
mContext = context;
|
|
||||||
mItems = new ArrayList<ActionMenuItem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Context getContext() {
|
|
||||||
return mContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem add(CharSequence title) {
|
|
||||||
return add(0, 0, 0, title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem add(int titleRes) {
|
|
||||||
return add(0, 0, 0, titleRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem add(int groupId, int itemId, int order, int titleRes) {
|
|
||||||
return add(groupId, itemId, order, mContext.getResources().getString(titleRes));
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
|
|
||||||
ActionMenuItem item = new ActionMenuItem(getContext(),
|
|
||||||
groupId, itemId, 0, order, title);
|
|
||||||
mItems.add(order, item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int addIntentOptions(int groupId, int itemId, int order,
|
|
||||||
ComponentName caller, Intent[] specifics, Intent intent, int flags,
|
|
||||||
MenuItem[] outSpecificItems) {
|
|
||||||
PackageManager pm = mContext.getPackageManager();
|
|
||||||
final List<ResolveInfo> lri =
|
|
||||||
pm.queryIntentActivityOptions(caller, specifics, intent, 0);
|
|
||||||
final int N = lri != null ? lri.size() : 0;
|
|
||||||
|
|
||||||
if ((flags & FLAG_APPEND_TO_GROUP) == 0) {
|
|
||||||
removeGroup(groupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i<N; i++) {
|
|
||||||
final ResolveInfo ri = lri.get(i);
|
|
||||||
Intent rintent = new Intent(
|
|
||||||
ri.specificIndex < 0 ? intent : specifics[ri.specificIndex]);
|
|
||||||
rintent.setComponent(new ComponentName(
|
|
||||||
ri.activityInfo.applicationInfo.packageName,
|
|
||||||
ri.activityInfo.name));
|
|
||||||
final MenuItem item = add(groupId, itemId, order, ri.loadLabel(pm))
|
|
||||||
.setIcon(ri.loadIcon(pm))
|
|
||||||
.setIntent(rintent);
|
|
||||||
if (outSpecificItems != null && ri.specificIndex >= 0) {
|
|
||||||
outSpecificItems[ri.specificIndex] = item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return N;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu addSubMenu(CharSequence title) {
|
|
||||||
// TODO Implement submenus
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu addSubMenu(int titleRes) {
|
|
||||||
// TODO Implement submenus
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu addSubMenu(int groupId, int itemId, int order,
|
|
||||||
CharSequence title) {
|
|
||||||
// TODO Implement submenus
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
|
|
||||||
// TODO Implement submenus
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clear() {
|
|
||||||
mItems.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void close() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private int findItemIndex(int id) {
|
|
||||||
final ArrayList<ActionMenuItem> items = mItems;
|
|
||||||
final int itemCount = items.size();
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
if (items.get(i).getItemId() == id) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem findItem(int id) {
|
|
||||||
return mItems.get(findItemIndex(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem getItem(int index) {
|
|
||||||
return mItems.get(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasVisibleItems() {
|
|
||||||
final ArrayList<ActionMenuItem> items = mItems;
|
|
||||||
final int itemCount = items.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
if (items.get(i).isVisible()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private ActionMenuItem findItemWithShortcut(int keyCode, KeyEvent event) {
|
|
||||||
// TODO Make this smarter.
|
|
||||||
final boolean qwerty = mIsQwerty;
|
|
||||||
final ArrayList<ActionMenuItem> items = mItems;
|
|
||||||
final int itemCount = items.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
ActionMenuItem item = items.get(i);
|
|
||||||
final char shortcut = qwerty ? item.getAlphabeticShortcut() :
|
|
||||||
item.getNumericShortcut();
|
|
||||||
if (keyCode == shortcut) {
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShortcutKey(int keyCode, KeyEvent event) {
|
|
||||||
return findItemWithShortcut(keyCode, event) != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean performIdentifierAction(int id, int flags) {
|
|
||||||
final int index = findItemIndex(id);
|
|
||||||
if (index < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return mItems.get(index).invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
|
|
||||||
ActionMenuItem item = findItemWithShortcut(keyCode, event);
|
|
||||||
if (item == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return item.invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeGroup(int groupId) {
|
|
||||||
final ArrayList<ActionMenuItem> items = mItems;
|
|
||||||
int itemCount = items.size();
|
|
||||||
int i = 0;
|
|
||||||
while (i < itemCount) {
|
|
||||||
if (items.get(i).getGroupId() == groupId) {
|
|
||||||
items.remove(i);
|
|
||||||
itemCount--;
|
|
||||||
} else {
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeItem(int id) {
|
|
||||||
mItems.remove(findItemIndex(id));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupCheckable(int group, boolean checkable,
|
|
||||||
boolean exclusive) {
|
|
||||||
final ArrayList<ActionMenuItem> items = mItems;
|
|
||||||
final int itemCount = items.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
ActionMenuItem item = items.get(i);
|
|
||||||
if (item.getGroupId() == group) {
|
|
||||||
item.setCheckable(checkable);
|
|
||||||
item.setExclusiveCheckable(exclusive);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupEnabled(int group, boolean enabled) {
|
|
||||||
final ArrayList<ActionMenuItem> items = mItems;
|
|
||||||
final int itemCount = items.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
ActionMenuItem item = items.get(i);
|
|
||||||
if (item.getGroupId() == group) {
|
|
||||||
item.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setGroupVisible(int group, boolean visible) {
|
|
||||||
final ArrayList<ActionMenuItem> items = mItems;
|
|
||||||
final int itemCount = items.size();
|
|
||||||
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
ActionMenuItem item = items.get(i);
|
|
||||||
if (item.getGroupId() == group) {
|
|
||||||
item.setVisible(visible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setQwertyMode(boolean isQwerty) {
|
|
||||||
mIsQwerty = isQwerty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int size() {
|
|
||||||
return mItems.size();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,278 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.view.ActionProvider;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
import com.actionbarsherlock.view.SubMenu;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public class ActionMenuItem implements MenuItem {
|
|
||||||
private final int mId;
|
|
||||||
private final int mGroup;
|
|
||||||
//UNUSED private final int mCategoryOrder;
|
|
||||||
private final int mOrdering;
|
|
||||||
|
|
||||||
private CharSequence mTitle;
|
|
||||||
private CharSequence mTitleCondensed;
|
|
||||||
private Intent mIntent;
|
|
||||||
private char mShortcutNumericChar;
|
|
||||||
private char mShortcutAlphabeticChar;
|
|
||||||
|
|
||||||
private Drawable mIconDrawable;
|
|
||||||
//UNUSED private int mIconResId = NO_ICON;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
|
|
||||||
private MenuItem.OnMenuItemClickListener mClickListener;
|
|
||||||
|
|
||||||
//UNUSED private static final int NO_ICON = 0;
|
|
||||||
|
|
||||||
private int mFlags = ENABLED;
|
|
||||||
private static final int CHECKABLE = 0x00000001;
|
|
||||||
private static final int CHECKED = 0x00000002;
|
|
||||||
private static final int EXCLUSIVE = 0x00000004;
|
|
||||||
private static final int HIDDEN = 0x00000008;
|
|
||||||
private static final int ENABLED = 0x00000010;
|
|
||||||
|
|
||||||
public ActionMenuItem(Context context, int group, int id, int categoryOrder, int ordering,
|
|
||||||
CharSequence title) {
|
|
||||||
mContext = context;
|
|
||||||
mId = id;
|
|
||||||
mGroup = group;
|
|
||||||
//UNUSED mCategoryOrder = categoryOrder;
|
|
||||||
mOrdering = ordering;
|
|
||||||
mTitle = title;
|
|
||||||
}
|
|
||||||
|
|
||||||
public char getAlphabeticShortcut() {
|
|
||||||
return mShortcutAlphabeticChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGroupId() {
|
|
||||||
return mGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Drawable getIcon() {
|
|
||||||
return mIconDrawable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Intent getIntent() {
|
|
||||||
return mIntent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getItemId() {
|
|
||||||
return mId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContextMenuInfo getMenuInfo() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public char getNumericShortcut() {
|
|
||||||
return mShortcutNumericChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOrder() {
|
|
||||||
return mOrdering;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu getSubMenu() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CharSequence getTitle() {
|
|
||||||
return mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public CharSequence getTitleCondensed() {
|
|
||||||
return mTitleCondensed;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSubMenu() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCheckable() {
|
|
||||||
return (mFlags & CHECKABLE) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isChecked() {
|
|
||||||
return (mFlags & CHECKED) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return (mFlags & ENABLED) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVisible() {
|
|
||||||
return (mFlags & HIDDEN) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setAlphabeticShortcut(char alphaChar) {
|
|
||||||
mShortcutAlphabeticChar = alphaChar;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setCheckable(boolean checkable) {
|
|
||||||
mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionMenuItem setExclusiveCheckable(boolean exclusive) {
|
|
||||||
mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setChecked(boolean checked) {
|
|
||||||
mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setEnabled(boolean enabled) {
|
|
||||||
mFlags = (mFlags & ~ENABLED) | (enabled ? ENABLED : 0);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setIcon(Drawable icon) {
|
|
||||||
mIconDrawable = icon;
|
|
||||||
//UNUSED mIconResId = NO_ICON;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setIcon(int iconRes) {
|
|
||||||
//UNUSED mIconResId = iconRes;
|
|
||||||
mIconDrawable = mContext.getResources().getDrawable(iconRes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setIntent(Intent intent) {
|
|
||||||
mIntent = intent;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setNumericShortcut(char numericChar) {
|
|
||||||
mShortcutNumericChar = numericChar;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
|
|
||||||
mClickListener = menuItemClickListener;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setShortcut(char numericChar, char alphaChar) {
|
|
||||||
mShortcutNumericChar = numericChar;
|
|
||||||
mShortcutAlphabeticChar = alphaChar;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setTitle(CharSequence title) {
|
|
||||||
mTitle = title;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setTitle(int title) {
|
|
||||||
mTitle = mContext.getResources().getString(title);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setTitleCondensed(CharSequence title) {
|
|
||||||
mTitleCondensed = title;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setVisible(boolean visible) {
|
|
||||||
mFlags = (mFlags & HIDDEN) | (visible ? 0 : HIDDEN);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean invoke() {
|
|
||||||
if (mClickListener != null && mClickListener.onMenuItemClick(this)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIntent != null) {
|
|
||||||
mContext.startActivity(mIntent);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShowAsAction(int show) {
|
|
||||||
// Do nothing. ActionMenuItems always show as action buttons.
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setActionView(View actionView) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
public View getActionView() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setActionView(int resId) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActionProvider getActionProvider() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setActionProvider(ActionProvider actionProvider) {
|
|
||||||
throw new UnsupportedOperationException();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setShowAsActionFlags(int actionEnum) {
|
|
||||||
setShowAsAction(actionEnum);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean expandActionView() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean collapseActionView() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isActionViewExpanded() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
|
|
||||||
// No need to save the listener; ActionMenuItem does not support collapsing items.
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,295 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Rect;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.R;
|
|
||||||
import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
|
|
||||||
import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
|
|
||||||
import com.actionbarsherlock.internal.widget.CapitalizingButton;
|
|
||||||
|
|
||||||
import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public class ActionMenuItemView extends LinearLayout
|
|
||||||
implements MenuView.ItemView, View.OnClickListener, View.OnLongClickListener,
|
|
||||||
ActionMenuView.ActionMenuChildView, View_HasStateListenerSupport {
|
|
||||||
//UNUSED private static final String TAG = "ActionMenuItemView";
|
|
||||||
|
|
||||||
private MenuItemImpl mItemData;
|
|
||||||
private CharSequence mTitle;
|
|
||||||
private MenuBuilder.ItemInvoker mItemInvoker;
|
|
||||||
|
|
||||||
private ImageButton mImageButton;
|
|
||||||
private CapitalizingButton mTextButton;
|
|
||||||
private boolean mAllowTextWithIcon;
|
|
||||||
private boolean mExpandedFormat;
|
|
||||||
private int mMinWidth;
|
|
||||||
|
|
||||||
private final Set<View_OnAttachStateChangeListener> mListeners = new HashSet<View_OnAttachStateChangeListener>();
|
|
||||||
|
|
||||||
public ActionMenuItemView(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionMenuItemView(Context context, AttributeSet attrs) {
|
|
||||||
this(context, attrs, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionMenuItemView(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
//TODO super(context, attrs, defStyle);
|
|
||||||
super(context, attrs);
|
|
||||||
mAllowTextWithIcon = getResources_getBoolean(context,
|
|
||||||
R.bool.abs__config_allowActionMenuItemTextWithIcon);
|
|
||||||
TypedArray a = context.obtainStyledAttributes(attrs,
|
|
||||||
R.styleable.SherlockActionMenuItemView, 0, 0);
|
|
||||||
mMinWidth = a.getDimensionPixelSize(
|
|
||||||
R.styleable.SherlockActionMenuItemView_android_minWidth, 0);
|
|
||||||
a.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
|
|
||||||
mListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
|
|
||||||
mListeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow();
|
|
||||||
for (View_OnAttachStateChangeListener listener : mListeners) {
|
|
||||||
listener.onViewAttachedToWindow(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow();
|
|
||||||
for (View_OnAttachStateChangeListener listener : mListeners) {
|
|
||||||
listener.onViewDetachedFromWindow(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFinishInflate() {
|
|
||||||
|
|
||||||
mImageButton = (ImageButton) findViewById(R.id.abs__imageButton);
|
|
||||||
mTextButton = (CapitalizingButton) findViewById(R.id.abs__textButton);
|
|
||||||
mImageButton.setOnClickListener(this);
|
|
||||||
mTextButton.setOnClickListener(this);
|
|
||||||
mImageButton.setOnLongClickListener(this);
|
|
||||||
setOnClickListener(this);
|
|
||||||
setOnLongClickListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItemImpl getItemData() {
|
|
||||||
return mItemData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize(MenuItemImpl itemData, int menuType) {
|
|
||||||
mItemData = itemData;
|
|
||||||
|
|
||||||
setIcon(itemData.getIcon());
|
|
||||||
setTitle(itemData.getTitleForItemView(this)); // Title only takes effect if there is no icon
|
|
||||||
setId(itemData.getItemId());
|
|
||||||
|
|
||||||
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
|
|
||||||
setEnabled(itemData.isEnabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
super.setEnabled(enabled);
|
|
||||||
mImageButton.setEnabled(enabled);
|
|
||||||
mTextButton.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onClick(View v) {
|
|
||||||
if (mItemInvoker != null) {
|
|
||||||
mItemInvoker.invokeItem(mItemData);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setItemInvoker(MenuBuilder.ItemInvoker invoker) {
|
|
||||||
mItemInvoker = invoker;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean prefersCondensedTitle() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCheckable(boolean checkable) {
|
|
||||||
// TODO Support checkable action items
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChecked(boolean checked) {
|
|
||||||
// TODO Support checkable action items
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpandedFormat(boolean expandedFormat) {
|
|
||||||
if (mExpandedFormat != expandedFormat) {
|
|
||||||
mExpandedFormat = expandedFormat;
|
|
||||||
if (mItemData != null) {
|
|
||||||
mItemData.actionFormatChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateTextButtonVisibility() {
|
|
||||||
boolean visible = !TextUtils.isEmpty(mTextButton.getText());
|
|
||||||
visible &= mImageButton.getDrawable() == null ||
|
|
||||||
(mItemData.showsTextAsAction() && (mAllowTextWithIcon || mExpandedFormat));
|
|
||||||
|
|
||||||
mTextButton.setVisibility(visible ? VISIBLE : GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIcon(Drawable icon) {
|
|
||||||
mImageButton.setImageDrawable(icon);
|
|
||||||
if (icon != null) {
|
|
||||||
mImageButton.setVisibility(VISIBLE);
|
|
||||||
} else {
|
|
||||||
mImageButton.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTextButtonVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasText() {
|
|
||||||
return mTextButton.getVisibility() != GONE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShortcut(boolean showShortcut, char shortcutKey) {
|
|
||||||
// Action buttons don't show text for shortcut keys.
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(CharSequence title) {
|
|
||||||
mTitle = title;
|
|
||||||
|
|
||||||
mTextButton.setTextCompat(mTitle);
|
|
||||||
|
|
||||||
setContentDescription(mTitle);
|
|
||||||
updateTextButtonVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
|
||||||
onPopulateAccessibilityEvent(event);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
|
||||||
super.onPopulateAccessibilityEvent(event);
|
|
||||||
}
|
|
||||||
final CharSequence cdesc = getContentDescription();
|
|
||||||
if (!TextUtils.isEmpty(cdesc)) {
|
|
||||||
event.getText().add(cdesc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean dispatchHoverEvent(MotionEvent event) {
|
|
||||||
// Don't allow children to hover; we want this to be treated as a single component.
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
|
||||||
return onHoverEvent(event);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean showsIcon() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean needsDividerBefore() {
|
|
||||||
return hasText() && mItemData.getIcon() == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean needsDividerAfter() {
|
|
||||||
return hasText();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onLongClick(View v) {
|
|
||||||
if (hasText()) {
|
|
||||||
// Don't show the cheat sheet for items that already show text.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int[] screenPos = new int[2];
|
|
||||||
final Rect displayFrame = new Rect();
|
|
||||||
getLocationOnScreen(screenPos);
|
|
||||||
getWindowVisibleDisplayFrame(displayFrame);
|
|
||||||
|
|
||||||
final Context context = getContext();
|
|
||||||
final int width = getWidth();
|
|
||||||
final int height = getHeight();
|
|
||||||
final int midy = screenPos[1] + height / 2;
|
|
||||||
final int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
|
|
||||||
|
|
||||||
Toast cheatSheet = Toast.makeText(context, mItemData.getTitle(), Toast.LENGTH_SHORT);
|
|
||||||
if (midy < displayFrame.height()) {
|
|
||||||
// Show along the top; follow action buttons
|
|
||||||
cheatSheet.setGravity(Gravity.TOP | Gravity.RIGHT,
|
|
||||||
screenWidth - screenPos[0] - width / 2, height);
|
|
||||||
} else {
|
|
||||||
// Show along the bottom center
|
|
||||||
cheatSheet.setGravity(Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, height);
|
|
||||||
}
|
|
||||||
cheatSheet.show();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
|
|
||||||
final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
|
|
||||||
final int specSize = MeasureSpec.getSize(widthMeasureSpec);
|
|
||||||
final int oldMeasuredWidth = getMeasuredWidth();
|
|
||||||
final int targetWidth = widthMode == MeasureSpec.AT_MOST ? Math.min(specSize, mMinWidth)
|
|
||||||
: mMinWidth;
|
|
||||||
|
|
||||||
if (widthMode != MeasureSpec.EXACTLY && mMinWidth > 0 && oldMeasuredWidth < targetWidth) {
|
|
||||||
// Remeasure at exactly the minimum width.
|
|
||||||
super.onMeasure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
|
|
||||||
heightMeasureSpec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,714 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getInteger;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.util.SparseBooleanArray;
|
|
||||||
import android.view.SoundEffectConstants;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.MeasureSpec;
|
|
||||||
import android.view.ViewConfiguration;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import com.actionbarsherlock.R;
|
|
||||||
import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
|
|
||||||
import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
|
|
||||||
import com.actionbarsherlock.internal.view.menu.ActionMenuView.ActionMenuChildView;
|
|
||||||
import com.actionbarsherlock.view.ActionProvider;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* MenuPresenter for building action menus as seen in the action bar and action modes.
|
|
||||||
*/
|
|
||||||
public class ActionMenuPresenter extends BaseMenuPresenter
|
|
||||||
implements ActionProvider.SubUiVisibilityListener {
|
|
||||||
//UNUSED private static final String TAG = "ActionMenuPresenter";
|
|
||||||
|
|
||||||
private View mOverflowButton;
|
|
||||||
private boolean mReserveOverflow;
|
|
||||||
private boolean mReserveOverflowSet;
|
|
||||||
private int mWidthLimit;
|
|
||||||
private int mActionItemWidthLimit;
|
|
||||||
private int mMaxItems;
|
|
||||||
private boolean mMaxItemsSet;
|
|
||||||
private boolean mStrictWidthLimit;
|
|
||||||
private boolean mWidthLimitSet;
|
|
||||||
private boolean mExpandedActionViewsExclusive;
|
|
||||||
|
|
||||||
private int mMinCellSize;
|
|
||||||
|
|
||||||
// Group IDs that have been added as actions - used temporarily, allocated here for reuse.
|
|
||||||
private final SparseBooleanArray mActionButtonGroups = new SparseBooleanArray();
|
|
||||||
|
|
||||||
private View mScrapActionButtonView;
|
|
||||||
|
|
||||||
private OverflowPopup mOverflowPopup;
|
|
||||||
private ActionButtonSubmenu mActionButtonPopup;
|
|
||||||
|
|
||||||
private OpenOverflowRunnable mPostedOpenRunnable;
|
|
||||||
|
|
||||||
final PopupPresenterCallback mPopupPresenterCallback = new PopupPresenterCallback();
|
|
||||||
int mOpenSubMenuId;
|
|
||||||
|
|
||||||
public ActionMenuPresenter(Context context) {
|
|
||||||
super(context, R.layout.abs__action_menu_layout,
|
|
||||||
R.layout.abs__action_menu_item_layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initForMenu(Context context, MenuBuilder menu) {
|
|
||||||
super.initForMenu(context, menu);
|
|
||||||
|
|
||||||
final Resources res = context.getResources();
|
|
||||||
|
|
||||||
if (!mReserveOverflowSet) {
|
|
||||||
mReserveOverflow = reserveOverflow(mContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mWidthLimitSet) {
|
|
||||||
mWidthLimit = res.getDisplayMetrics().widthPixels / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Measure for initial configuration
|
|
||||||
if (!mMaxItemsSet) {
|
|
||||||
mMaxItems = getResources_getInteger(context, R.integer.abs__max_action_buttons);
|
|
||||||
}
|
|
||||||
|
|
||||||
int width = mWidthLimit;
|
|
||||||
if (mReserveOverflow) {
|
|
||||||
if (mOverflowButton == null) {
|
|
||||||
mOverflowButton = new OverflowMenuButton(mSystemContext);
|
|
||||||
final int spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
|
||||||
mOverflowButton.measure(spec, spec);
|
|
||||||
}
|
|
||||||
width -= mOverflowButton.getMeasuredWidth();
|
|
||||||
} else {
|
|
||||||
mOverflowButton = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
mActionItemWidthLimit = width;
|
|
||||||
|
|
||||||
mMinCellSize = (int) (ActionMenuView.MIN_CELL_SIZE * res.getDisplayMetrics().density);
|
|
||||||
|
|
||||||
// Drop a scrap view as it may no longer reflect the proper context/config.
|
|
||||||
mScrapActionButtonView = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean reserveOverflow(Context context) {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
|
|
||||||
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB);
|
|
||||||
} else {
|
|
||||||
return !HasPermanentMenuKey.get(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class HasPermanentMenuKey {
|
|
||||||
public static boolean get(Context context) {
|
|
||||||
return ViewConfiguration.get(context).hasPermanentMenuKey();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
|
||||||
if (!mMaxItemsSet) {
|
|
||||||
mMaxItems = getResources_getInteger(mContext,
|
|
||||||
R.integer.abs__max_action_buttons);
|
|
||||||
if (mMenu != null) {
|
|
||||||
mMenu.onItemsChanged(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWidthLimit(int width, boolean strict) {
|
|
||||||
mWidthLimit = width;
|
|
||||||
mStrictWidthLimit = strict;
|
|
||||||
mWidthLimitSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setReserveOverflow(boolean reserveOverflow) {
|
|
||||||
mReserveOverflow = reserveOverflow;
|
|
||||||
mReserveOverflowSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setItemLimit(int itemCount) {
|
|
||||||
mMaxItems = itemCount;
|
|
||||||
mMaxItemsSet = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExpandedActionViewsExclusive(boolean isExclusive) {
|
|
||||||
mExpandedActionViewsExclusive = isExclusive;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuView getMenuView(ViewGroup root) {
|
|
||||||
MenuView result = super.getMenuView(root);
|
|
||||||
((ActionMenuView) result).setPresenter(this);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
|
|
||||||
View actionView = item.getActionView();
|
|
||||||
if (actionView == null || item.hasCollapsibleActionView()) {
|
|
||||||
if (!(convertView instanceof ActionMenuItemView)) {
|
|
||||||
convertView = null;
|
|
||||||
}
|
|
||||||
actionView = super.getItemView(item, convertView, parent);
|
|
||||||
}
|
|
||||||
actionView.setVisibility(item.isActionViewExpanded() ? View.GONE : View.VISIBLE);
|
|
||||||
|
|
||||||
final ActionMenuView menuParent = (ActionMenuView) parent;
|
|
||||||
final ViewGroup.LayoutParams lp = actionView.getLayoutParams();
|
|
||||||
if (!menuParent.checkLayoutParams(lp)) {
|
|
||||||
actionView.setLayoutParams(menuParent.generateLayoutParams(lp));
|
|
||||||
}
|
|
||||||
return actionView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bindItemView(MenuItemImpl item, MenuView.ItemView itemView) {
|
|
||||||
itemView.initialize(item, 0);
|
|
||||||
|
|
||||||
final ActionMenuView menuView = (ActionMenuView) mMenuView;
|
|
||||||
ActionMenuItemView actionItemView = (ActionMenuItemView) itemView;
|
|
||||||
actionItemView.setItemInvoker(menuView);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
|
|
||||||
return item.isActionButton();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateMenuView(boolean cleared) {
|
|
||||||
super.updateMenuView(cleared);
|
|
||||||
|
|
||||||
if (mMenu != null) {
|
|
||||||
final ArrayList<MenuItemImpl> actionItems = mMenu.getActionItems();
|
|
||||||
final int count = actionItems.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
final ActionProvider provider = actionItems.get(i).getActionProvider();
|
|
||||||
if (provider != null) {
|
|
||||||
provider.setSubUiVisibilityListener(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final ArrayList<MenuItemImpl> nonActionItems = mMenu != null ?
|
|
||||||
mMenu.getNonActionItems() : null;
|
|
||||||
|
|
||||||
boolean hasOverflow = false;
|
|
||||||
if (mReserveOverflow && nonActionItems != null) {
|
|
||||||
final int count = nonActionItems.size();
|
|
||||||
if (count == 1) {
|
|
||||||
hasOverflow = !nonActionItems.get(0).isActionViewExpanded();
|
|
||||||
} else {
|
|
||||||
hasOverflow = count > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hasOverflow) {
|
|
||||||
if (mOverflowButton == null) {
|
|
||||||
mOverflowButton = new OverflowMenuButton(mSystemContext);
|
|
||||||
}
|
|
||||||
ViewGroup parent = (ViewGroup) mOverflowButton.getParent();
|
|
||||||
if (parent != mMenuView) {
|
|
||||||
if (parent != null) {
|
|
||||||
parent.removeView(mOverflowButton);
|
|
||||||
}
|
|
||||||
ActionMenuView menuView = (ActionMenuView) mMenuView;
|
|
||||||
menuView.addView(mOverflowButton, menuView.generateOverflowButtonLayoutParams());
|
|
||||||
}
|
|
||||||
} else if (mOverflowButton != null && mOverflowButton.getParent() == mMenuView) {
|
|
||||||
((ViewGroup) mMenuView).removeView(mOverflowButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
((ActionMenuView) mMenuView).setOverflowReserved(mReserveOverflow);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean filterLeftoverView(ViewGroup parent, int childIndex) {
|
|
||||||
if (parent.getChildAt(childIndex) == mOverflowButton) return false;
|
|
||||||
return super.filterLeftoverView(parent, childIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
|
|
||||||
if (!subMenu.hasVisibleItems()) return false;
|
|
||||||
|
|
||||||
SubMenuBuilder topSubMenu = subMenu;
|
|
||||||
while (topSubMenu.getParentMenu() != mMenu) {
|
|
||||||
topSubMenu = (SubMenuBuilder) topSubMenu.getParentMenu();
|
|
||||||
}
|
|
||||||
View anchor = findViewForItem(topSubMenu.getItem());
|
|
||||||
if (anchor == null) {
|
|
||||||
if (mOverflowButton == null) return false;
|
|
||||||
anchor = mOverflowButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
mOpenSubMenuId = subMenu.getItem().getItemId();
|
|
||||||
mActionButtonPopup = new ActionButtonSubmenu(mContext, subMenu);
|
|
||||||
mActionButtonPopup.setAnchorView(anchor);
|
|
||||||
mActionButtonPopup.show();
|
|
||||||
super.onSubMenuSelected(subMenu);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private View findViewForItem(MenuItem item) {
|
|
||||||
final ViewGroup parent = (ViewGroup) mMenuView;
|
|
||||||
if (parent == null) return null;
|
|
||||||
|
|
||||||
final int count = parent.getChildCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
final View child = parent.getChildAt(i);
|
|
||||||
if (child instanceof MenuView.ItemView &&
|
|
||||||
((MenuView.ItemView) child).getItemData() == item) {
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display the overflow menu if one is present.
|
|
||||||
* @return true if the overflow menu was shown, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean showOverflowMenu() {
|
|
||||||
if (mReserveOverflow && !isOverflowMenuShowing() && mMenu != null && mMenuView != null &&
|
|
||||||
mPostedOpenRunnable == null && !mMenu.getNonActionItems().isEmpty()) {
|
|
||||||
OverflowPopup popup = new OverflowPopup(mContext, mMenu, mOverflowButton, true);
|
|
||||||
mPostedOpenRunnable = new OpenOverflowRunnable(popup);
|
|
||||||
// Post this for later; we might still need a layout for the anchor to be right.
|
|
||||||
((View) mMenuView).post(mPostedOpenRunnable);
|
|
||||||
|
|
||||||
// ActionMenuPresenter uses null as a callback argument here
|
|
||||||
// to indicate overflow is opening.
|
|
||||||
super.onSubMenuSelected(null);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hide the overflow menu if it is currently showing.
|
|
||||||
*
|
|
||||||
* @return true if the overflow menu was hidden, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean hideOverflowMenu() {
|
|
||||||
if (mPostedOpenRunnable != null && mMenuView != null) {
|
|
||||||
((View) mMenuView).removeCallbacks(mPostedOpenRunnable);
|
|
||||||
mPostedOpenRunnable = null;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuPopupHelper popup = mOverflowPopup;
|
|
||||||
if (popup != null) {
|
|
||||||
popup.dismiss();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dismiss all popup menus - overflow and submenus.
|
|
||||||
* @return true if popups were dismissed, false otherwise. (This can be because none were open.)
|
|
||||||
*/
|
|
||||||
public boolean dismissPopupMenus() {
|
|
||||||
boolean result = hideOverflowMenu();
|
|
||||||
result |= hideSubMenus();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dismiss all submenu popups.
|
|
||||||
*
|
|
||||||
* @return true if popups were dismissed, false otherwise. (This can be because none were open.)
|
|
||||||
*/
|
|
||||||
public boolean hideSubMenus() {
|
|
||||||
if (mActionButtonPopup != null) {
|
|
||||||
mActionButtonPopup.dismiss();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if the overflow menu is currently showing
|
|
||||||
*/
|
|
||||||
public boolean isOverflowMenuShowing() {
|
|
||||||
return mOverflowPopup != null && mOverflowPopup.isShowing();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return true if space has been reserved in the action menu for an overflow item.
|
|
||||||
*/
|
|
||||||
public boolean isOverflowReserved() {
|
|
||||||
return mReserveOverflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean flagActionItems() {
|
|
||||||
final ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
|
|
||||||
final int itemsSize = visibleItems.size();
|
|
||||||
int maxActions = mMaxItems;
|
|
||||||
int widthLimit = mActionItemWidthLimit;
|
|
||||||
final int querySpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
|
|
||||||
final ViewGroup parent = (ViewGroup) mMenuView;
|
|
||||||
|
|
||||||
int requiredItems = 0;
|
|
||||||
int requestedItems = 0;
|
|
||||||
int firstActionWidth = 0;
|
|
||||||
boolean hasOverflow = false;
|
|
||||||
for (int i = 0; i < itemsSize; i++) {
|
|
||||||
MenuItemImpl item = visibleItems.get(i);
|
|
||||||
if (item.requiresActionButton()) {
|
|
||||||
requiredItems++;
|
|
||||||
} else if (item.requestsActionButton()) {
|
|
||||||
requestedItems++;
|
|
||||||
} else {
|
|
||||||
hasOverflow = true;
|
|
||||||
}
|
|
||||||
if (mExpandedActionViewsExclusive && item.isActionViewExpanded()) {
|
|
||||||
// Overflow everything if we have an expanded action view and we're
|
|
||||||
// space constrained.
|
|
||||||
maxActions = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reserve a spot for the overflow item if needed.
|
|
||||||
if (mReserveOverflow &&
|
|
||||||
(hasOverflow || requiredItems + requestedItems > maxActions)) {
|
|
||||||
maxActions--;
|
|
||||||
}
|
|
||||||
maxActions -= requiredItems;
|
|
||||||
|
|
||||||
final SparseBooleanArray seenGroups = mActionButtonGroups;
|
|
||||||
seenGroups.clear();
|
|
||||||
|
|
||||||
int cellSize = 0;
|
|
||||||
int cellsRemaining = 0;
|
|
||||||
if (mStrictWidthLimit) {
|
|
||||||
cellsRemaining = widthLimit / mMinCellSize;
|
|
||||||
final int cellSizeRemaining = widthLimit % mMinCellSize;
|
|
||||||
cellSize = mMinCellSize + cellSizeRemaining / cellsRemaining;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Flag as many more requested items as will fit.
|
|
||||||
for (int i = 0; i < itemsSize; i++) {
|
|
||||||
MenuItemImpl item = visibleItems.get(i);
|
|
||||||
|
|
||||||
if (item.requiresActionButton()) {
|
|
||||||
View v = getItemView(item, mScrapActionButtonView, parent);
|
|
||||||
if (mScrapActionButtonView == null) {
|
|
||||||
mScrapActionButtonView = v;
|
|
||||||
}
|
|
||||||
if (mStrictWidthLimit) {
|
|
||||||
cellsRemaining -= ActionMenuView.measureChildForCells(v,
|
|
||||||
cellSize, cellsRemaining, querySpec, 0);
|
|
||||||
} else {
|
|
||||||
v.measure(querySpec, querySpec);
|
|
||||||
}
|
|
||||||
final int measuredWidth = v.getMeasuredWidth();
|
|
||||||
widthLimit -= measuredWidth;
|
|
||||||
if (firstActionWidth == 0) {
|
|
||||||
firstActionWidth = measuredWidth;
|
|
||||||
}
|
|
||||||
final int groupId = item.getGroupId();
|
|
||||||
if (groupId != 0) {
|
|
||||||
seenGroups.put(groupId, true);
|
|
||||||
}
|
|
||||||
item.setIsActionButton(true);
|
|
||||||
} else if (item.requestsActionButton()) {
|
|
||||||
// Items in a group with other items that already have an action slot
|
|
||||||
// can break the max actions rule, but not the width limit.
|
|
||||||
final int groupId = item.getGroupId();
|
|
||||||
final boolean inGroup = seenGroups.get(groupId);
|
|
||||||
boolean isAction = (maxActions > 0 || inGroup) && widthLimit > 0 &&
|
|
||||||
(!mStrictWidthLimit || cellsRemaining > 0);
|
|
||||||
|
|
||||||
if (isAction) {
|
|
||||||
View v = getItemView(item, mScrapActionButtonView, parent);
|
|
||||||
if (mScrapActionButtonView == null) {
|
|
||||||
mScrapActionButtonView = v;
|
|
||||||
}
|
|
||||||
if (mStrictWidthLimit) {
|
|
||||||
final int cells = ActionMenuView.measureChildForCells(v,
|
|
||||||
cellSize, cellsRemaining, querySpec, 0);
|
|
||||||
cellsRemaining -= cells;
|
|
||||||
if (cells == 0) {
|
|
||||||
isAction = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
v.measure(querySpec, querySpec);
|
|
||||||
}
|
|
||||||
final int measuredWidth = v.getMeasuredWidth();
|
|
||||||
widthLimit -= measuredWidth;
|
|
||||||
if (firstActionWidth == 0) {
|
|
||||||
firstActionWidth = measuredWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mStrictWidthLimit) {
|
|
||||||
isAction &= widthLimit >= 0;
|
|
||||||
} else {
|
|
||||||
// Did this push the entire first item past the limit?
|
|
||||||
isAction &= widthLimit + firstActionWidth > 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAction && groupId != 0) {
|
|
||||||
seenGroups.put(groupId, true);
|
|
||||||
} else if (inGroup) {
|
|
||||||
// We broke the width limit. Demote the whole group, they all overflow now.
|
|
||||||
seenGroups.put(groupId, false);
|
|
||||||
for (int j = 0; j < i; j++) {
|
|
||||||
MenuItemImpl areYouMyGroupie = visibleItems.get(j);
|
|
||||||
if (areYouMyGroupie.getGroupId() == groupId) {
|
|
||||||
// Give back the action slot
|
|
||||||
if (areYouMyGroupie.isActionButton()) maxActions++;
|
|
||||||
areYouMyGroupie.setIsActionButton(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isAction) maxActions--;
|
|
||||||
|
|
||||||
item.setIsActionButton(isAction);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
|
||||||
dismissPopupMenus();
|
|
||||||
super.onCloseMenu(menu, allMenusAreClosing);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Parcelable onSaveInstanceState() {
|
|
||||||
SavedState state = new SavedState();
|
|
||||||
state.openSubMenuId = mOpenSubMenuId;
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRestoreInstanceState(Parcelable state) {
|
|
||||||
SavedState saved = (SavedState) state;
|
|
||||||
if (saved.openSubMenuId > 0) {
|
|
||||||
MenuItem item = mMenu.findItem(saved.openSubMenuId);
|
|
||||||
if (item != null) {
|
|
||||||
SubMenuBuilder subMenu = (SubMenuBuilder) item.getSubMenu();
|
|
||||||
onSubMenuSelected(subMenu);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSubUiVisibilityChanged(boolean isVisible) {
|
|
||||||
if (isVisible) {
|
|
||||||
// Not a submenu, but treat it like one.
|
|
||||||
super.onSubMenuSelected(null);
|
|
||||||
} else {
|
|
||||||
mMenu.close(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class SavedState implements Parcelable {
|
|
||||||
public int openSubMenuId;
|
|
||||||
|
|
||||||
SavedState() {
|
|
||||||
}
|
|
||||||
|
|
||||||
SavedState(Parcel in) {
|
|
||||||
openSubMenuId = in.readInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int describeContents() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void writeToParcel(Parcel dest, int flags) {
|
|
||||||
dest.writeInt(openSubMenuId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
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];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OverflowMenuButton extends ImageButton implements ActionMenuChildView, View_HasStateListenerSupport {
|
|
||||||
private final Set<View_OnAttachStateChangeListener> mListeners = new HashSet<View_OnAttachStateChangeListener>();
|
|
||||||
|
|
||||||
public OverflowMenuButton(Context context) {
|
|
||||||
super(context, null, R.attr.actionOverflowButtonStyle);
|
|
||||||
|
|
||||||
setClickable(true);
|
|
||||||
setFocusable(true);
|
|
||||||
setVisibility(VISIBLE);
|
|
||||||
setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean performClick() {
|
|
||||||
if (super.performClick()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
playSoundEffect(SoundEffectConstants.CLICK);
|
|
||||||
showOverflowMenu();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean needsDividerBefore() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean needsDividerAfter() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAttachedToWindow() {
|
|
||||||
super.onAttachedToWindow();
|
|
||||||
for (View_OnAttachStateChangeListener listener : mListeners) {
|
|
||||||
listener.onViewAttachedToWindow(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow();
|
|
||||||
for (View_OnAttachStateChangeListener listener : mListeners) {
|
|
||||||
listener.onViewDetachedFromWindow(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mOverflowPopup != null) mOverflowPopup.dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
|
|
||||||
mListeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeOnAttachStateChangeListener(View_OnAttachStateChangeListener listener) {
|
|
||||||
mListeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OverflowPopup extends MenuPopupHelper {
|
|
||||||
public OverflowPopup(Context context, MenuBuilder menu, View anchorView,
|
|
||||||
boolean overflowOnly) {
|
|
||||||
super(context, menu, anchorView, overflowOnly);
|
|
||||||
setCallback(mPopupPresenterCallback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDismiss() {
|
|
||||||
super.onDismiss();
|
|
||||||
mMenu.close();
|
|
||||||
mOverflowPopup = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ActionButtonSubmenu extends MenuPopupHelper {
|
|
||||||
//UNUSED private SubMenuBuilder mSubMenu;
|
|
||||||
|
|
||||||
public ActionButtonSubmenu(Context context, SubMenuBuilder subMenu) {
|
|
||||||
super(context, subMenu);
|
|
||||||
//UNUSED mSubMenu = subMenu;
|
|
||||||
|
|
||||||
MenuItemImpl item = (MenuItemImpl) subMenu.getItem();
|
|
||||||
if (!item.isActionButton()) {
|
|
||||||
// Give a reasonable anchor to nested submenus.
|
|
||||||
setAnchorView(mOverflowButton == null ? (View) mMenuView : mOverflowButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
setCallback(mPopupPresenterCallback);
|
|
||||||
|
|
||||||
boolean preserveIconSpacing = false;
|
|
||||||
final int count = subMenu.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
MenuItem childItem = subMenu.getItem(i);
|
|
||||||
if (childItem.isVisible() && childItem.getIcon() != null) {
|
|
||||||
preserveIconSpacing = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setForceShowIcon(preserveIconSpacing);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDismiss() {
|
|
||||||
super.onDismiss();
|
|
||||||
mActionButtonPopup = null;
|
|
||||||
mOpenSubMenuId = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class PopupPresenterCallback implements MenuPresenter.Callback {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onOpenSubMenu(MenuBuilder subMenu) {
|
|
||||||
if (subMenu == null) return false;
|
|
||||||
|
|
||||||
mOpenSubMenuId = ((SubMenuBuilder) subMenu).getItem().getItemId();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
|
||||||
if (menu instanceof SubMenuBuilder) {
|
|
||||||
((SubMenuBuilder) menu).getRootMenu().close(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OpenOverflowRunnable implements Runnable {
|
|
||||||
private OverflowPopup mPopup;
|
|
||||||
|
|
||||||
public OpenOverflowRunnable(OverflowPopup popup) {
|
|
||||||
mPopup = popup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
mMenu.changeMenuMode();
|
|
||||||
final View menuView = (View) mMenuView;
|
|
||||||
if (menuView != null && menuView.getWindowToken() != null && mPopup.tryShow()) {
|
|
||||||
mOverflowPopup = mPopup;
|
|
||||||
}
|
|
||||||
mPostedOpenRunnable = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,575 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Configuration;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.accessibility.AccessibilityEvent;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import com.actionbarsherlock.internal.widget.IcsLinearLayout;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public class ActionMenuView extends IcsLinearLayout implements MenuBuilder.ItemInvoker, MenuView {
|
|
||||||
//UNUSED private static final String TAG = "ActionMenuView";
|
|
||||||
private static final boolean IS_FROYO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO;
|
|
||||||
|
|
||||||
static final int MIN_CELL_SIZE = 56; // dips
|
|
||||||
static final int GENERATED_ITEM_PADDING = 4; // dips
|
|
||||||
|
|
||||||
private MenuBuilder mMenu;
|
|
||||||
|
|
||||||
private boolean mReserveOverflow;
|
|
||||||
private ActionMenuPresenter mPresenter;
|
|
||||||
private boolean mFormatItems;
|
|
||||||
private int mFormatItemsWidth;
|
|
||||||
private int mMinCellSize;
|
|
||||||
private int mGeneratedItemPadding;
|
|
||||||
//UNUSED private int mMeasuredExtraWidth;
|
|
||||||
|
|
||||||
private boolean mFirst = true;
|
|
||||||
|
|
||||||
public ActionMenuView(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionMenuView(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
setBaselineAligned(false);
|
|
||||||
final float density = context.getResources().getDisplayMetrics().density;
|
|
||||||
mMinCellSize = (int) (MIN_CELL_SIZE * density);
|
|
||||||
mGeneratedItemPadding = (int) (GENERATED_ITEM_PADDING * density);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPresenter(ActionMenuPresenter presenter) {
|
|
||||||
mPresenter = presenter;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExpandedFormat() {
|
|
||||||
return mFormatItems;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onConfigurationChanged(Configuration newConfig) {
|
|
||||||
if (IS_FROYO) {
|
|
||||||
super.onConfigurationChanged(newConfig);
|
|
||||||
}
|
|
||||||
mPresenter.updateMenuView(false);
|
|
||||||
|
|
||||||
if (mPresenter != null && mPresenter.isOverflowMenuShowing()) {
|
|
||||||
mPresenter.hideOverflowMenu();
|
|
||||||
mPresenter.showOverflowMenu();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
//Need to trigger a relayout since we may have been added extremely
|
|
||||||
//late in the initial rendering (e.g., when contained in a ViewPager).
|
|
||||||
//See: https://github.com/JakeWharton/ActionBarSherlock/issues/272
|
|
||||||
if (!IS_FROYO && mFirst) {
|
|
||||||
mFirst = false;
|
|
||||||
requestLayout();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
super.onDraw(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
// If we've been given an exact size to match, apply special formatting during layout.
|
|
||||||
final boolean wasFormatted = mFormatItems;
|
|
||||||
mFormatItems = MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY;
|
|
||||||
|
|
||||||
if (wasFormatted != mFormatItems) {
|
|
||||||
mFormatItemsWidth = 0; // Reset this when switching modes
|
|
||||||
}
|
|
||||||
|
|
||||||
// Special formatting can change whether items can fit as action buttons.
|
|
||||||
// Kick the menu and update presenters when this changes.
|
|
||||||
final int widthSize = MeasureSpec.getMode(widthMeasureSpec);
|
|
||||||
if (mFormatItems && mMenu != null && widthSize != mFormatItemsWidth) {
|
|
||||||
mFormatItemsWidth = widthSize;
|
|
||||||
mMenu.onItemsChanged(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mFormatItems) {
|
|
||||||
onMeasureExactFormat(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
} else {
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void onMeasureExactFormat(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
// We already know the width mode is EXACTLY if we're here.
|
|
||||||
final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
|
|
||||||
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
|
|
||||||
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
|
|
||||||
|
|
||||||
final int widthPadding = getPaddingLeft() + getPaddingRight();
|
|
||||||
final int heightPadding = getPaddingTop() + getPaddingBottom();
|
|
||||||
|
|
||||||
widthSize -= widthPadding;
|
|
||||||
|
|
||||||
// Divide the view into cells.
|
|
||||||
final int cellCount = widthSize / mMinCellSize;
|
|
||||||
final int cellSizeRemaining = widthSize % mMinCellSize;
|
|
||||||
|
|
||||||
if (cellCount == 0) {
|
|
||||||
// Give up, nothing fits.
|
|
||||||
setMeasuredDimension(widthSize, 0);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int cellSize = mMinCellSize + cellSizeRemaining / cellCount;
|
|
||||||
|
|
||||||
int cellsRemaining = cellCount;
|
|
||||||
int maxChildHeight = 0;
|
|
||||||
int maxCellsUsed = 0;
|
|
||||||
int expandableItemCount = 0;
|
|
||||||
int visibleItemCount = 0;
|
|
||||||
boolean hasOverflow = false;
|
|
||||||
|
|
||||||
// This is used as a bitfield to locate the smallest items present. Assumes childCount < 64.
|
|
||||||
long smallestItemsAt = 0;
|
|
||||||
|
|
||||||
final int childCount = getChildCount();
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
final View child = getChildAt(i);
|
|
||||||
if (child.getVisibility() == GONE) continue;
|
|
||||||
|
|
||||||
final boolean isGeneratedItem = child instanceof ActionMenuItemView;
|
|
||||||
visibleItemCount++;
|
|
||||||
|
|
||||||
if (isGeneratedItem) {
|
|
||||||
// Reset padding for generated menu item views; it may change below
|
|
||||||
// and views are recycled.
|
|
||||||
child.setPadding(mGeneratedItemPadding, 0, mGeneratedItemPadding, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
||||||
lp.expanded = false;
|
|
||||||
lp.extraPixels = 0;
|
|
||||||
lp.cellsUsed = 0;
|
|
||||||
lp.expandable = false;
|
|
||||||
lp.leftMargin = 0;
|
|
||||||
lp.rightMargin = 0;
|
|
||||||
lp.preventEdgeOffset = isGeneratedItem && ((ActionMenuItemView) child).hasText();
|
|
||||||
|
|
||||||
// Overflow always gets 1 cell. No more, no less.
|
|
||||||
final int cellsAvailable = lp.isOverflowButton ? 1 : cellsRemaining;
|
|
||||||
|
|
||||||
final int cellsUsed = measureChildForCells(child, cellSize, cellsAvailable,
|
|
||||||
heightMeasureSpec, heightPadding);
|
|
||||||
|
|
||||||
maxCellsUsed = Math.max(maxCellsUsed, cellsUsed);
|
|
||||||
if (lp.expandable) expandableItemCount++;
|
|
||||||
if (lp.isOverflowButton) hasOverflow = true;
|
|
||||||
|
|
||||||
cellsRemaining -= cellsUsed;
|
|
||||||
maxChildHeight = Math.max(maxChildHeight, child.getMeasuredHeight());
|
|
||||||
if (cellsUsed == 1) smallestItemsAt |= (1 << i);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When we have overflow and a single expanded (text) item, we want to try centering it
|
|
||||||
// visually in the available space even though overflow consumes some of it.
|
|
||||||
final boolean centerSingleExpandedItem = hasOverflow && visibleItemCount == 2;
|
|
||||||
|
|
||||||
// Divide space for remaining cells if we have items that can expand.
|
|
||||||
// Try distributing whole leftover cells to smaller items first.
|
|
||||||
|
|
||||||
boolean needsExpansion = false;
|
|
||||||
while (expandableItemCount > 0 && cellsRemaining > 0) {
|
|
||||||
int minCells = Integer.MAX_VALUE;
|
|
||||||
long minCellsAt = 0; // Bit locations are indices of relevant child views
|
|
||||||
int minCellsItemCount = 0;
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
final View child = getChildAt(i);
|
|
||||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
||||||
|
|
||||||
// Don't try to expand items that shouldn't.
|
|
||||||
if (!lp.expandable) continue;
|
|
||||||
|
|
||||||
// Mark indices of children that can receive an extra cell.
|
|
||||||
if (lp.cellsUsed < minCells) {
|
|
||||||
minCells = lp.cellsUsed;
|
|
||||||
minCellsAt = 1 << i;
|
|
||||||
minCellsItemCount = 1;
|
|
||||||
} else if (lp.cellsUsed == minCells) {
|
|
||||||
minCellsAt |= 1 << i;
|
|
||||||
minCellsItemCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Items that get expanded will always be in the set of smallest items when we're done.
|
|
||||||
smallestItemsAt |= minCellsAt;
|
|
||||||
|
|
||||||
if (minCellsItemCount > cellsRemaining) break; // Couldn't expand anything evenly. Stop.
|
|
||||||
|
|
||||||
// We have enough cells, all minimum size items will be incremented.
|
|
||||||
minCells++;
|
|
||||||
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
final View child = getChildAt(i);
|
|
||||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
||||||
if ((minCellsAt & (1 << i)) == 0) {
|
|
||||||
// If this item is already at our small item count, mark it for later.
|
|
||||||
if (lp.cellsUsed == minCells) smallestItemsAt |= 1 << i;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (centerSingleExpandedItem && lp.preventEdgeOffset && cellsRemaining == 1) {
|
|
||||||
// Add padding to this item such that it centers.
|
|
||||||
child.setPadding(mGeneratedItemPadding + cellSize, 0, mGeneratedItemPadding, 0);
|
|
||||||
}
|
|
||||||
lp.cellsUsed++;
|
|
||||||
lp.expanded = true;
|
|
||||||
cellsRemaining--;
|
|
||||||
}
|
|
||||||
|
|
||||||
needsExpansion = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Divide any space left that wouldn't divide along cell boundaries
|
|
||||||
// evenly among the smallest items
|
|
||||||
|
|
||||||
final boolean singleItem = !hasOverflow && visibleItemCount == 1;
|
|
||||||
if (cellsRemaining > 0 && smallestItemsAt != 0 &&
|
|
||||||
(cellsRemaining < visibleItemCount - 1 || singleItem || maxCellsUsed > 1)) {
|
|
||||||
float expandCount = Long.bitCount(smallestItemsAt);
|
|
||||||
|
|
||||||
if (!singleItem) {
|
|
||||||
// The items at the far edges may only expand by half in order to pin to either side.
|
|
||||||
if ((smallestItemsAt & 1) != 0) {
|
|
||||||
LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();
|
|
||||||
if (!lp.preventEdgeOffset) expandCount -= 0.5f;
|
|
||||||
}
|
|
||||||
if ((smallestItemsAt & (1 << (childCount - 1))) != 0) {
|
|
||||||
LayoutParams lp = ((LayoutParams) getChildAt(childCount - 1).getLayoutParams());
|
|
||||||
if (!lp.preventEdgeOffset) expandCount -= 0.5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final int extraPixels = expandCount > 0 ?
|
|
||||||
(int) (cellsRemaining * cellSize / expandCount) : 0;
|
|
||||||
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
if ((smallestItemsAt & (1 << i)) == 0) continue;
|
|
||||||
|
|
||||||
final View child = getChildAt(i);
|
|
||||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
||||||
if (child instanceof ActionMenuItemView) {
|
|
||||||
// If this is one of our views, expand and measure at the larger size.
|
|
||||||
lp.extraPixels = extraPixels;
|
|
||||||
lp.expanded = true;
|
|
||||||
if (i == 0 && !lp.preventEdgeOffset) {
|
|
||||||
// First item gets part of its new padding pushed out of sight.
|
|
||||||
// The last item will get this implicitly from layout.
|
|
||||||
lp.leftMargin = -extraPixels / 2;
|
|
||||||
}
|
|
||||||
needsExpansion = true;
|
|
||||||
} else if (lp.isOverflowButton) {
|
|
||||||
lp.extraPixels = extraPixels;
|
|
||||||
lp.expanded = true;
|
|
||||||
lp.rightMargin = -extraPixels / 2;
|
|
||||||
needsExpansion = true;
|
|
||||||
} else {
|
|
||||||
// If we don't know what it is, give it some margins instead
|
|
||||||
// and let it center within its space. We still want to pin
|
|
||||||
// against the edges.
|
|
||||||
if (i != 0) {
|
|
||||||
lp.leftMargin = extraPixels / 2;
|
|
||||||
}
|
|
||||||
if (i != childCount - 1) {
|
|
||||||
lp.rightMargin = extraPixels / 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
cellsRemaining = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remeasure any items that have had extra space allocated to them.
|
|
||||||
if (needsExpansion) {
|
|
||||||
int heightSpec = MeasureSpec.makeMeasureSpec(heightSize - heightPadding, heightMode);
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
final View child = getChildAt(i);
|
|
||||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
||||||
|
|
||||||
if (!lp.expanded) continue;
|
|
||||||
|
|
||||||
final int width = lp.cellsUsed * cellSize + lp.extraPixels;
|
|
||||||
child.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), heightSpec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (heightMode != MeasureSpec.EXACTLY) {
|
|
||||||
heightSize = maxChildHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
setMeasuredDimension(widthSize, heightSize);
|
|
||||||
//UNUSED mMeasuredExtraWidth = cellsRemaining * cellSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Measure a child view to fit within cell-based formatting. The child's width
|
|
||||||
* will be measured to a whole multiple of cellSize.
|
|
||||||
*
|
|
||||||
* <p>Sets the expandable and cellsUsed fields of LayoutParams.
|
|
||||||
*
|
|
||||||
* @param child Child to measure
|
|
||||||
* @param cellSize Size of one cell
|
|
||||||
* @param cellsRemaining Number of cells remaining that this view can expand to fill
|
|
||||||
* @param parentHeightMeasureSpec MeasureSpec used by the parent view
|
|
||||||
* @param parentHeightPadding Padding present in the parent view
|
|
||||||
* @return Number of cells this child was measured to occupy
|
|
||||||
*/
|
|
||||||
static int measureChildForCells(View child, int cellSize, int cellsRemaining,
|
|
||||||
int parentHeightMeasureSpec, int parentHeightPadding) {
|
|
||||||
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
|
|
||||||
|
|
||||||
final int childHeightSize = MeasureSpec.getSize(parentHeightMeasureSpec) -
|
|
||||||
parentHeightPadding;
|
|
||||||
final int childHeightMode = MeasureSpec.getMode(parentHeightMeasureSpec);
|
|
||||||
final int childHeightSpec = MeasureSpec.makeMeasureSpec(childHeightSize, childHeightMode);
|
|
||||||
|
|
||||||
int cellsUsed = 0;
|
|
||||||
if (cellsRemaining > 0) {
|
|
||||||
final int childWidthSpec = MeasureSpec.makeMeasureSpec(
|
|
||||||
cellSize * cellsRemaining, MeasureSpec.AT_MOST);
|
|
||||||
child.measure(childWidthSpec, childHeightSpec);
|
|
||||||
|
|
||||||
final int measuredWidth = child.getMeasuredWidth();
|
|
||||||
cellsUsed = measuredWidth / cellSize;
|
|
||||||
if (measuredWidth % cellSize != 0) cellsUsed++;
|
|
||||||
}
|
|
||||||
|
|
||||||
final ActionMenuItemView itemView = child instanceof ActionMenuItemView ?
|
|
||||||
(ActionMenuItemView) child : null;
|
|
||||||
final boolean expandable = !lp.isOverflowButton && itemView != null && itemView.hasText();
|
|
||||||
lp.expandable = expandable;
|
|
||||||
|
|
||||||
lp.cellsUsed = cellsUsed;
|
|
||||||
final int targetWidth = cellsUsed * cellSize;
|
|
||||||
child.measure(MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY),
|
|
||||||
childHeightSpec);
|
|
||||||
return cellsUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
||||||
if (!mFormatItems) {
|
|
||||||
super.onLayout(changed, left, top, right, bottom);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int childCount = getChildCount();
|
|
||||||
final int midVertical = (top + bottom) / 2;
|
|
||||||
final int dividerWidth = 0;//getDividerWidth();
|
|
||||||
int overflowWidth = 0;
|
|
||||||
//UNUSED int nonOverflowWidth = 0;
|
|
||||||
int nonOverflowCount = 0;
|
|
||||||
int widthRemaining = right - left - getPaddingRight() - getPaddingLeft();
|
|
||||||
boolean hasOverflow = false;
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
final View v = getChildAt(i);
|
|
||||||
if (v.getVisibility() == GONE) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
LayoutParams p = (LayoutParams) v.getLayoutParams();
|
|
||||||
if (p.isOverflowButton) {
|
|
||||||
overflowWidth = v.getMeasuredWidth();
|
|
||||||
if (hasDividerBeforeChildAt(i)) {
|
|
||||||
overflowWidth += dividerWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
int height = v.getMeasuredHeight();
|
|
||||||
int r = getWidth() - getPaddingRight() - p.rightMargin;
|
|
||||||
int l = r - overflowWidth;
|
|
||||||
int t = midVertical - (height / 2);
|
|
||||||
int b = t + height;
|
|
||||||
v.layout(l, t, r, b);
|
|
||||||
|
|
||||||
widthRemaining -= overflowWidth;
|
|
||||||
hasOverflow = true;
|
|
||||||
} else {
|
|
||||||
final int size = v.getMeasuredWidth() + p.leftMargin + p.rightMargin;
|
|
||||||
//UNUSED nonOverflowWidth += size;
|
|
||||||
widthRemaining -= size;
|
|
||||||
//if (hasDividerBeforeChildAt(i)) {
|
|
||||||
//UNUSED nonOverflowWidth += dividerWidth;
|
|
||||||
//}
|
|
||||||
nonOverflowCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (childCount == 1 && !hasOverflow) {
|
|
||||||
// Center a single child
|
|
||||||
final View v = getChildAt(0);
|
|
||||||
final int width = v.getMeasuredWidth();
|
|
||||||
final int height = v.getMeasuredHeight();
|
|
||||||
final int midHorizontal = (right - left) / 2;
|
|
||||||
final int l = midHorizontal - width / 2;
|
|
||||||
final int t = midVertical - height / 2;
|
|
||||||
v.layout(l, t, l + width, t + height);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int spacerCount = nonOverflowCount - (hasOverflow ? 0 : 1);
|
|
||||||
final int spacerSize = Math.max(0, spacerCount > 0 ? widthRemaining / spacerCount : 0);
|
|
||||||
|
|
||||||
int startLeft = getPaddingLeft();
|
|
||||||
for (int i = 0; i < childCount; i++) {
|
|
||||||
final View v = getChildAt(i);
|
|
||||||
final LayoutParams lp = (LayoutParams) v.getLayoutParams();
|
|
||||||
if (v.getVisibility() == GONE || lp.isOverflowButton) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
startLeft += lp.leftMargin;
|
|
||||||
int width = v.getMeasuredWidth();
|
|
||||||
int height = v.getMeasuredHeight();
|
|
||||||
int t = midVertical - height / 2;
|
|
||||||
v.layout(startLeft, t, startLeft + width, t + height);
|
|
||||||
startLeft += width + lp.rightMargin + spacerSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDetachedFromWindow() {
|
|
||||||
super.onDetachedFromWindow();
|
|
||||||
mPresenter.dismissPopupMenus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOverflowReserved() {
|
|
||||||
return mReserveOverflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOverflowReserved(boolean reserveOverflow) {
|
|
||||||
mReserveOverflow = reserveOverflow;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LayoutParams generateDefaultLayoutParams() {
|
|
||||||
LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT,
|
|
||||||
LayoutParams.WRAP_CONTENT);
|
|
||||||
params.gravity = Gravity.CENTER_VERTICAL;
|
|
||||||
return params;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public LayoutParams generateLayoutParams(AttributeSet attrs) {
|
|
||||||
return new LayoutParams(getContext(), attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
|
|
||||||
if (p instanceof LayoutParams) {
|
|
||||||
LayoutParams result = new LayoutParams((LayoutParams) p);
|
|
||||||
if (result.gravity <= Gravity.NO_GRAVITY) {
|
|
||||||
result.gravity = Gravity.CENTER_VERTICAL;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
return generateDefaultLayoutParams();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
|
||||||
return p != null && p instanceof LayoutParams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LayoutParams generateOverflowButtonLayoutParams() {
|
|
||||||
LayoutParams result = generateDefaultLayoutParams();
|
|
||||||
result.isOverflowButton = true;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean invokeItem(MenuItemImpl item) {
|
|
||||||
return mMenu.performItemAction(item, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getWindowAnimations() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize(MenuBuilder menu) {
|
|
||||||
mMenu = menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
//@Override
|
|
||||||
protected boolean hasDividerBeforeChildAt(int childIndex) {
|
|
||||||
if (childIndex == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
final View childBefore = getChildAt(childIndex - 1);
|
|
||||||
final View child = getChildAt(childIndex);
|
|
||||||
boolean result = false;
|
|
||||||
if (childIndex < getChildCount() && childBefore instanceof ActionMenuChildView) {
|
|
||||||
result |= ((ActionMenuChildView) childBefore).needsDividerAfter();
|
|
||||||
}
|
|
||||||
if (childIndex > 0 && child instanceof ActionMenuChildView) {
|
|
||||||
result |= ((ActionMenuChildView) child).needsDividerBefore();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface ActionMenuChildView {
|
|
||||||
public boolean needsDividerBefore();
|
|
||||||
public boolean needsDividerAfter();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class LayoutParams extends LinearLayout.LayoutParams {
|
|
||||||
public boolean isOverflowButton;
|
|
||||||
public int cellsUsed;
|
|
||||||
public int extraPixels;
|
|
||||||
public boolean expandable;
|
|
||||||
public boolean preventEdgeOffset;
|
|
||||||
|
|
||||||
public boolean expanded;
|
|
||||||
|
|
||||||
public LayoutParams(Context c, AttributeSet attrs) {
|
|
||||||
super(c, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public LayoutParams(LayoutParams other) {
|
|
||||||
super((LinearLayout.LayoutParams) other);
|
|
||||||
isOverflowButton = other.isOverflowButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LayoutParams(int width, int height) {
|
|
||||||
super(width, height);
|
|
||||||
isOverflowButton = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public LayoutParams(int width, int height, boolean isOverflowButton) {
|
|
||||||
super(width, height);
|
|
||||||
this.isOverflowButton = isOverflowButton;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,231 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Build;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class for MenuPresenters that have a consistent container view and item
|
|
||||||
* views. Behaves similarly to an AdapterView in that existing item views will
|
|
||||||
* be reused if possible when items change.
|
|
||||||
*/
|
|
||||||
public abstract class BaseMenuPresenter implements MenuPresenter {
|
|
||||||
private static final boolean IS_HONEYCOMB = Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
|
|
||||||
|
|
||||||
protected Context mSystemContext;
|
|
||||||
protected Context mContext;
|
|
||||||
protected MenuBuilder mMenu;
|
|
||||||
protected LayoutInflater mSystemInflater;
|
|
||||||
protected LayoutInflater mInflater;
|
|
||||||
private Callback mCallback;
|
|
||||||
|
|
||||||
private int mMenuLayoutRes;
|
|
||||||
private int mItemLayoutRes;
|
|
||||||
|
|
||||||
protected MenuView mMenuView;
|
|
||||||
|
|
||||||
private int mId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a new BaseMenuPresenter.
|
|
||||||
*
|
|
||||||
* @param context Context for generating system-supplied views
|
|
||||||
* @param menuLayoutRes Layout resource ID for the menu container view
|
|
||||||
* @param itemLayoutRes Layout resource ID for a single item view
|
|
||||||
*/
|
|
||||||
public BaseMenuPresenter(Context context, int menuLayoutRes, int itemLayoutRes) {
|
|
||||||
mSystemContext = context;
|
|
||||||
mSystemInflater = LayoutInflater.from(context);
|
|
||||||
mMenuLayoutRes = menuLayoutRes;
|
|
||||||
mItemLayoutRes = itemLayoutRes;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initForMenu(Context context, MenuBuilder menu) {
|
|
||||||
mContext = context;
|
|
||||||
mInflater = LayoutInflater.from(mContext);
|
|
||||||
mMenu = menu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuView getMenuView(ViewGroup root) {
|
|
||||||
if (mMenuView == null) {
|
|
||||||
mMenuView = (MenuView) mSystemInflater.inflate(mMenuLayoutRes, root, false);
|
|
||||||
mMenuView.initialize(mMenu);
|
|
||||||
updateMenuView(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
return mMenuView;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reuses item views when it can
|
|
||||||
*/
|
|
||||||
public void updateMenuView(boolean cleared) {
|
|
||||||
final ViewGroup parent = (ViewGroup) mMenuView;
|
|
||||||
if (parent == null) return;
|
|
||||||
|
|
||||||
int childIndex = 0;
|
|
||||||
if (mMenu != null) {
|
|
||||||
mMenu.flagActionItems();
|
|
||||||
ArrayList<MenuItemImpl> visibleItems = mMenu.getVisibleItems();
|
|
||||||
final int itemCount = visibleItems.size();
|
|
||||||
for (int i = 0; i < itemCount; i++) {
|
|
||||||
MenuItemImpl item = visibleItems.get(i);
|
|
||||||
if (shouldIncludeItem(childIndex, item)) {
|
|
||||||
final View convertView = parent.getChildAt(childIndex);
|
|
||||||
final MenuItemImpl oldItem = convertView instanceof MenuView.ItemView ?
|
|
||||||
((MenuView.ItemView) convertView).getItemData() : null;
|
|
||||||
final View itemView = getItemView(item, convertView, parent);
|
|
||||||
if (item != oldItem) {
|
|
||||||
// Don't let old states linger with new data.
|
|
||||||
itemView.setPressed(false);
|
|
||||||
if (IS_HONEYCOMB) itemView.jumpDrawablesToCurrentState();
|
|
||||||
}
|
|
||||||
if (itemView != convertView) {
|
|
||||||
addItemView(itemView, childIndex);
|
|
||||||
}
|
|
||||||
childIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove leftover views.
|
|
||||||
while (childIndex < parent.getChildCount()) {
|
|
||||||
if (!filterLeftoverView(parent, childIndex)) {
|
|
||||||
childIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add an item view at the given index.
|
|
||||||
*
|
|
||||||
* @param itemView View to add
|
|
||||||
* @param childIndex Index within the parent to insert at
|
|
||||||
*/
|
|
||||||
protected void addItemView(View itemView, int childIndex) {
|
|
||||||
final ViewGroup currentParent = (ViewGroup) itemView.getParent();
|
|
||||||
if (currentParent != null) {
|
|
||||||
currentParent.removeView(itemView);
|
|
||||||
}
|
|
||||||
((ViewGroup) mMenuView).addView(itemView, childIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter the child view at index and remove it if appropriate.
|
|
||||||
* @param parent Parent to filter from
|
|
||||||
* @param childIndex Index to filter
|
|
||||||
* @return true if the child view at index was removed
|
|
||||||
*/
|
|
||||||
protected boolean filterLeftoverView(ViewGroup parent, int childIndex) {
|
|
||||||
parent.removeViewAt(childIndex);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCallback(Callback cb) {
|
|
||||||
mCallback = cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new item view that can be re-bound to other item data later.
|
|
||||||
*
|
|
||||||
* @return The new item view
|
|
||||||
*/
|
|
||||||
public MenuView.ItemView createItemView(ViewGroup parent) {
|
|
||||||
return (MenuView.ItemView) mSystemInflater.inflate(mItemLayoutRes, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepare an item view for use. See AdapterView for the basic idea at work here.
|
|
||||||
* This may require creating a new item view, but well-behaved implementations will
|
|
||||||
* re-use the view passed as convertView if present. The returned view will be populated
|
|
||||||
* with data from the item parameter.
|
|
||||||
*
|
|
||||||
* @param item Item to present
|
|
||||||
* @param convertView Existing view to reuse
|
|
||||||
* @param parent Intended parent view - use for inflation.
|
|
||||||
* @return View that presents the requested menu item
|
|
||||||
*/
|
|
||||||
public View getItemView(MenuItemImpl item, View convertView, ViewGroup parent) {
|
|
||||||
MenuView.ItemView itemView;
|
|
||||||
if (convertView instanceof MenuView.ItemView) {
|
|
||||||
itemView = (MenuView.ItemView) convertView;
|
|
||||||
} else {
|
|
||||||
itemView = createItemView(parent);
|
|
||||||
}
|
|
||||||
bindItemView(item, itemView);
|
|
||||||
return (View) itemView;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind item data to an existing item view.
|
|
||||||
*
|
|
||||||
* @param item Item to bind
|
|
||||||
* @param itemView View to populate with item data
|
|
||||||
*/
|
|
||||||
public abstract void bindItemView(MenuItemImpl item, MenuView.ItemView itemView);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter item by child index and item data.
|
|
||||||
*
|
|
||||||
* @param childIndex Indended presentation index of this item
|
|
||||||
* @param item Item to present
|
|
||||||
* @return true if this item should be included in this menu presentation; false otherwise
|
|
||||||
*/
|
|
||||||
public boolean shouldIncludeItem(int childIndex, MenuItemImpl item) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
|
||||||
if (mCallback != null) {
|
|
||||||
mCallback.onCloseMenu(menu, allMenusAreClosing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onSubMenuSelected(SubMenuBuilder menu) {
|
|
||||||
if (mCallback != null) {
|
|
||||||
return mCallback.onOpenSubMenu(menu);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean flagActionItems() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getId() {
|
|
||||||
return mId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(int id) {
|
|
||||||
mId = id;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,278 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.R;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.CompoundButton;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.RadioButton;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The item view for each item in the ListView-based MenuViews.
|
|
||||||
*/
|
|
||||||
public class ListMenuItemView extends LinearLayout implements MenuView.ItemView {
|
|
||||||
private MenuItemImpl mItemData;
|
|
||||||
|
|
||||||
private ImageView mIconView;
|
|
||||||
private RadioButton mRadioButton;
|
|
||||||
private TextView mTitleView;
|
|
||||||
private CheckBox mCheckBox;
|
|
||||||
private TextView mShortcutView;
|
|
||||||
|
|
||||||
private Drawable mBackground;
|
|
||||||
private int mTextAppearance;
|
|
||||||
private Context mTextAppearanceContext;
|
|
||||||
private boolean mPreserveIconSpacing;
|
|
||||||
|
|
||||||
//UNUSED private int mMenuType;
|
|
||||||
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
|
|
||||||
private boolean mForceShowIcon;
|
|
||||||
|
|
||||||
final Context mContext;
|
|
||||||
|
|
||||||
public ListMenuItemView(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs);
|
|
||||||
mContext = context;
|
|
||||||
|
|
||||||
TypedArray a =
|
|
||||||
context.obtainStyledAttributes(
|
|
||||||
attrs, R.styleable.SherlockMenuView, defStyle, 0);
|
|
||||||
|
|
||||||
mBackground = a.getDrawable(R.styleable.SherlockMenuView_itemBackground);
|
|
||||||
mTextAppearance = a.getResourceId(R.styleable.
|
|
||||||
SherlockMenuView_itemTextAppearance, -1);
|
|
||||||
mPreserveIconSpacing = a.getBoolean(
|
|
||||||
R.styleable.SherlockMenuView_preserveIconSpacing, false);
|
|
||||||
mTextAppearanceContext = context;
|
|
||||||
|
|
||||||
a.recycle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ListMenuItemView(Context context, AttributeSet attrs) {
|
|
||||||
this(context, attrs, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onFinishInflate() {
|
|
||||||
super.onFinishInflate();
|
|
||||||
|
|
||||||
setBackgroundDrawable(mBackground);
|
|
||||||
|
|
||||||
mTitleView = (TextView) findViewById(R.id.abs__title);
|
|
||||||
if (mTextAppearance != -1) {
|
|
||||||
mTitleView.setTextAppearance(mTextAppearanceContext,
|
|
||||||
mTextAppearance);
|
|
||||||
}
|
|
||||||
|
|
||||||
mShortcutView = (TextView) findViewById(R.id.abs__shortcut);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initialize(MenuItemImpl itemData, int menuType) {
|
|
||||||
mItemData = itemData;
|
|
||||||
//UNUSED mMenuType = menuType;
|
|
||||||
|
|
||||||
setVisibility(itemData.isVisible() ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
setTitle(itemData.getTitleForItemView(this));
|
|
||||||
setCheckable(itemData.isCheckable());
|
|
||||||
setShortcut(itemData.shouldShowShortcut(), itemData.getShortcut());
|
|
||||||
setIcon(itemData.getIcon());
|
|
||||||
setEnabled(itemData.isEnabled());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setForceShowIcon(boolean forceShow) {
|
|
||||||
mPreserveIconSpacing = mForceShowIcon = forceShow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTitle(CharSequence title) {
|
|
||||||
if (title != null) {
|
|
||||||
mTitleView.setText(title);
|
|
||||||
|
|
||||||
if (mTitleView.getVisibility() != VISIBLE) mTitleView.setVisibility(VISIBLE);
|
|
||||||
} else {
|
|
||||||
if (mTitleView.getVisibility() != GONE) mTitleView.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItemImpl getItemData() {
|
|
||||||
return mItemData;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCheckable(boolean checkable) {
|
|
||||||
|
|
||||||
if (!checkable && mRadioButton == null && mCheckBox == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mRadioButton == null) {
|
|
||||||
insertRadioButton();
|
|
||||||
}
|
|
||||||
if (mCheckBox == null) {
|
|
||||||
insertCheckBox();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Depending on whether its exclusive check or not, the checkbox or
|
|
||||||
// radio button will be the one in use (and the other will be otherCompoundButton)
|
|
||||||
final CompoundButton compoundButton;
|
|
||||||
final CompoundButton otherCompoundButton;
|
|
||||||
|
|
||||||
if (mItemData.isExclusiveCheckable()) {
|
|
||||||
compoundButton = mRadioButton;
|
|
||||||
otherCompoundButton = mCheckBox;
|
|
||||||
} else {
|
|
||||||
compoundButton = mCheckBox;
|
|
||||||
otherCompoundButton = mRadioButton;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkable) {
|
|
||||||
compoundButton.setChecked(mItemData.isChecked());
|
|
||||||
|
|
||||||
final int newVisibility = checkable ? VISIBLE : GONE;
|
|
||||||
if (compoundButton.getVisibility() != newVisibility) {
|
|
||||||
compoundButton.setVisibility(newVisibility);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure the other compound button isn't visible
|
|
||||||
if (otherCompoundButton.getVisibility() != GONE) {
|
|
||||||
otherCompoundButton.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mCheckBox.setVisibility(GONE);
|
|
||||||
mRadioButton.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setChecked(boolean checked) {
|
|
||||||
CompoundButton compoundButton;
|
|
||||||
|
|
||||||
if (mItemData.isExclusiveCheckable()) {
|
|
||||||
if (mRadioButton == null) {
|
|
||||||
insertRadioButton();
|
|
||||||
}
|
|
||||||
compoundButton = mRadioButton;
|
|
||||||
} else {
|
|
||||||
if (mCheckBox == null) {
|
|
||||||
insertCheckBox();
|
|
||||||
}
|
|
||||||
compoundButton = mCheckBox;
|
|
||||||
}
|
|
||||||
|
|
||||||
compoundButton.setChecked(checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShortcut(boolean showShortcut, char shortcutKey) {
|
|
||||||
final int newVisibility = (showShortcut && mItemData.shouldShowShortcut())
|
|
||||||
? VISIBLE : GONE;
|
|
||||||
|
|
||||||
if (newVisibility == VISIBLE) {
|
|
||||||
mShortcutView.setText(mItemData.getShortcutLabel());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mShortcutView.getVisibility() != newVisibility) {
|
|
||||||
mShortcutView.setVisibility(newVisibility);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIcon(Drawable icon) {
|
|
||||||
final boolean showIcon = mItemData.shouldShowIcon() || mForceShowIcon;
|
|
||||||
if (!showIcon && !mPreserveIconSpacing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIconView == null && icon == null && !mPreserveIconSpacing) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIconView == null) {
|
|
||||||
insertIconView();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (icon != null || mPreserveIconSpacing) {
|
|
||||||
mIconView.setImageDrawable(showIcon ? icon : null);
|
|
||||||
|
|
||||||
if (mIconView.getVisibility() != VISIBLE) {
|
|
||||||
mIconView.setVisibility(VISIBLE);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mIconView.setVisibility(GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
if (mIconView != null && mPreserveIconSpacing) {
|
|
||||||
// Enforce minimum icon spacing
|
|
||||||
ViewGroup.LayoutParams lp = getLayoutParams();
|
|
||||||
LayoutParams iconLp = (LayoutParams) mIconView.getLayoutParams();
|
|
||||||
if (lp.height > 0 && iconLp.width <= 0) {
|
|
||||||
iconLp.width = lp.height;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void insertIconView() {
|
|
||||||
LayoutInflater inflater = getInflater();
|
|
||||||
mIconView = (ImageView) inflater.inflate(R.layout.abs__list_menu_item_icon,
|
|
||||||
this, false);
|
|
||||||
addView(mIconView, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void insertRadioButton() {
|
|
||||||
LayoutInflater inflater = getInflater();
|
|
||||||
mRadioButton =
|
|
||||||
(RadioButton) inflater.inflate(R.layout.abs__list_menu_item_radio,
|
|
||||||
this, false);
|
|
||||||
addView(mRadioButton);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void insertCheckBox() {
|
|
||||||
LayoutInflater inflater = getInflater();
|
|
||||||
mCheckBox =
|
|
||||||
(CheckBox) inflater.inflate(R.layout.abs__list_menu_item_checkbox,
|
|
||||||
this, false);
|
|
||||||
addView(mCheckBox);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean prefersCondensedTitle() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean showsIcon() {
|
|
||||||
return mForceShowIcon;
|
|
||||||
}
|
|
||||||
|
|
||||||
private LayoutInflater getInflater() {
|
|
||||||
if (mInflater == null) {
|
|
||||||
mInflater = LayoutInflater.from(mContext);
|
|
||||||
}
|
|
||||||
return mInflater;
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,647 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import android.content.ActivityNotFoundException;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewDebug;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.view.ActionProvider;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
import com.actionbarsherlock.view.SubMenu;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public final class MenuItemImpl implements MenuItem {
|
|
||||||
private static final String TAG = "MenuItemImpl";
|
|
||||||
|
|
||||||
private static final int SHOW_AS_ACTION_MASK = SHOW_AS_ACTION_NEVER |
|
|
||||||
SHOW_AS_ACTION_IF_ROOM |
|
|
||||||
SHOW_AS_ACTION_ALWAYS;
|
|
||||||
|
|
||||||
private final int mId;
|
|
||||||
private final int mGroup;
|
|
||||||
private final int mCategoryOrder;
|
|
||||||
private final int mOrdering;
|
|
||||||
private CharSequence mTitle;
|
|
||||||
private CharSequence mTitleCondensed;
|
|
||||||
private Intent mIntent;
|
|
||||||
private char mShortcutNumericChar;
|
|
||||||
private char mShortcutAlphabeticChar;
|
|
||||||
|
|
||||||
/** The icon's drawable which is only created as needed */
|
|
||||||
private Drawable mIconDrawable;
|
|
||||||
/**
|
|
||||||
* The icon's resource ID which is used to get the Drawable when it is
|
|
||||||
* needed (if the Drawable isn't already obtained--only one of the two is
|
|
||||||
* needed).
|
|
||||||
*/
|
|
||||||
private int mIconResId = NO_ICON;
|
|
||||||
|
|
||||||
/** The menu to which this item belongs */
|
|
||||||
private MenuBuilder mMenu;
|
|
||||||
/** If this item should launch a sub menu, this is the sub menu to launch */
|
|
||||||
private SubMenuBuilder mSubMenu;
|
|
||||||
|
|
||||||
private Runnable mItemCallback;
|
|
||||||
private MenuItem.OnMenuItemClickListener mClickListener;
|
|
||||||
|
|
||||||
private int mFlags = ENABLED;
|
|
||||||
private static final int CHECKABLE = 0x00000001;
|
|
||||||
private static final int CHECKED = 0x00000002;
|
|
||||||
private static final int EXCLUSIVE = 0x00000004;
|
|
||||||
private static final int HIDDEN = 0x00000008;
|
|
||||||
private static final int ENABLED = 0x00000010;
|
|
||||||
private static final int IS_ACTION = 0x00000020;
|
|
||||||
|
|
||||||
private int mShowAsAction = SHOW_AS_ACTION_NEVER;
|
|
||||||
|
|
||||||
private View mActionView;
|
|
||||||
private ActionProvider mActionProvider;
|
|
||||||
private OnActionExpandListener mOnActionExpandListener;
|
|
||||||
private boolean mIsActionViewExpanded = false;
|
|
||||||
|
|
||||||
/** Used for the icon resource ID if this item does not have an icon */
|
|
||||||
static final int NO_ICON = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current use case is for context menu: Extra information linked to the
|
|
||||||
* View that added this item to the context menu.
|
|
||||||
*/
|
|
||||||
private ContextMenuInfo mMenuInfo;
|
|
||||||
|
|
||||||
private static String sPrependShortcutLabel;
|
|
||||||
private static String sEnterShortcutLabel;
|
|
||||||
private static String sDeleteShortcutLabel;
|
|
||||||
private static String sSpaceShortcutLabel;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instantiates this menu item.
|
|
||||||
*
|
|
||||||
* @param menu
|
|
||||||
* @param group Item ordering grouping control. The item will be added after
|
|
||||||
* all other items whose order is <= this number, and before any
|
|
||||||
* that are larger than it. This can also be used to define
|
|
||||||
* groups of items for batch state changes. Normally use 0.
|
|
||||||
* @param id Unique item ID. Use 0 if you do not need a unique ID.
|
|
||||||
* @param categoryOrder The ordering for this item.
|
|
||||||
* @param title The text to display for the item.
|
|
||||||
*/
|
|
||||||
MenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
|
|
||||||
CharSequence title, int showAsAction) {
|
|
||||||
|
|
||||||
/* TODO if (sPrependShortcutLabel == null) {
|
|
||||||
// This is instantiated from the UI thread, so no chance of sync issues
|
|
||||||
sPrependShortcutLabel = menu.getContext().getResources().getString(
|
|
||||||
com.android.internal.R.string.prepend_shortcut_label);
|
|
||||||
sEnterShortcutLabel = menu.getContext().getResources().getString(
|
|
||||||
com.android.internal.R.string.menu_enter_shortcut_label);
|
|
||||||
sDeleteShortcutLabel = menu.getContext().getResources().getString(
|
|
||||||
com.android.internal.R.string.menu_delete_shortcut_label);
|
|
||||||
sSpaceShortcutLabel = menu.getContext().getResources().getString(
|
|
||||||
com.android.internal.R.string.menu_space_shortcut_label);
|
|
||||||
}*/
|
|
||||||
|
|
||||||
mMenu = menu;
|
|
||||||
mId = id;
|
|
||||||
mGroup = group;
|
|
||||||
mCategoryOrder = categoryOrder;
|
|
||||||
mOrdering = ordering;
|
|
||||||
mTitle = title;
|
|
||||||
mShowAsAction = showAsAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Invokes the item by calling various listeners or callbacks.
|
|
||||||
*
|
|
||||||
* @return true if the invocation was handled, false otherwise
|
|
||||||
*/
|
|
||||||
public boolean invoke() {
|
|
||||||
if (mClickListener != null &&
|
|
||||||
mClickListener.onMenuItemClick(this)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mMenu.dispatchMenuItemSelected(mMenu.getRootMenu(), this)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mItemCallback != null) {
|
|
||||||
mItemCallback.run();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIntent != null) {
|
|
||||||
try {
|
|
||||||
mMenu.getContext().startActivity(mIntent);
|
|
||||||
return true;
|
|
||||||
} catch (ActivityNotFoundException e) {
|
|
||||||
Log.e(TAG, "Can't find activity to handle intent; ignoring", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mActionProvider != null && mActionProvider.onPerformDefaultAction()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return (mFlags & ENABLED) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setEnabled(boolean enabled) {
|
|
||||||
if (enabled) {
|
|
||||||
mFlags |= ENABLED;
|
|
||||||
} else {
|
|
||||||
mFlags &= ~ENABLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getGroupId() {
|
|
||||||
return mGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewDebug.CapturedViewProperty
|
|
||||||
public int getItemId() {
|
|
||||||
return mId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOrder() {
|
|
||||||
return mCategoryOrder;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getOrdering() {
|
|
||||||
return mOrdering;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Intent getIntent() {
|
|
||||||
return mIntent;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setIntent(Intent intent) {
|
|
||||||
mIntent = intent;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
Runnable getCallback() {
|
|
||||||
return mItemCallback;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setCallback(Runnable callback) {
|
|
||||||
mItemCallback = callback;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public char getAlphabeticShortcut() {
|
|
||||||
return mShortcutAlphabeticChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setAlphabeticShortcut(char alphaChar) {
|
|
||||||
if (mShortcutAlphabeticChar == alphaChar) return this;
|
|
||||||
|
|
||||||
mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
|
|
||||||
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public char getNumericShortcut() {
|
|
||||||
return mShortcutNumericChar;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setNumericShortcut(char numericChar) {
|
|
||||||
if (mShortcutNumericChar == numericChar) return this;
|
|
||||||
|
|
||||||
mShortcutNumericChar = numericChar;
|
|
||||||
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setShortcut(char numericChar, char alphaChar) {
|
|
||||||
mShortcutNumericChar = numericChar;
|
|
||||||
mShortcutAlphabeticChar = Character.toLowerCase(alphaChar);
|
|
||||||
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The active shortcut (based on QWERTY-mode of the menu).
|
|
||||||
*/
|
|
||||||
char getShortcut() {
|
|
||||||
return (mMenu.isQwertyMode() ? mShortcutAlphabeticChar : mShortcutNumericChar);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return The label to show for the shortcut. This includes the chording
|
|
||||||
* key (for example 'Menu+a'). Also, any non-human readable
|
|
||||||
* characters should be human readable (for example 'Menu+enter').
|
|
||||||
*/
|
|
||||||
String getShortcutLabel() {
|
|
||||||
|
|
||||||
char shortcut = getShortcut();
|
|
||||||
if (shortcut == 0) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder sb = new StringBuilder(sPrependShortcutLabel);
|
|
||||||
switch (shortcut) {
|
|
||||||
|
|
||||||
case '\n':
|
|
||||||
sb.append(sEnterShortcutLabel);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case '\b':
|
|
||||||
sb.append(sDeleteShortcutLabel);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ' ':
|
|
||||||
sb.append(sSpaceShortcutLabel);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
sb.append(shortcut);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Whether this menu item should be showing shortcuts (depends on
|
|
||||||
* whether the menu should show shortcuts and whether this item has
|
|
||||||
* a shortcut defined)
|
|
||||||
*/
|
|
||||||
boolean shouldShowShortcut() {
|
|
||||||
// Show shortcuts if the menu is supposed to show shortcuts AND this item has a shortcut
|
|
||||||
return mMenu.isShortcutsVisible() && (getShortcut() != 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu getSubMenu() {
|
|
||||||
return mSubMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSubMenu() {
|
|
||||||
return mSubMenu != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setSubMenu(SubMenuBuilder subMenu) {
|
|
||||||
mSubMenu = subMenu;
|
|
||||||
|
|
||||||
subMenu.setHeaderTitle(getTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
@ViewDebug.CapturedViewProperty
|
|
||||||
public CharSequence getTitle() {
|
|
||||||
return mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the title for a particular {@link ItemView}
|
|
||||||
*
|
|
||||||
* @param itemView The ItemView that is receiving the title
|
|
||||||
* @return Either the title or condensed title based on what the ItemView
|
|
||||||
* prefers
|
|
||||||
*/
|
|
||||||
CharSequence getTitleForItemView(MenuView.ItemView itemView) {
|
|
||||||
return ((itemView != null) && itemView.prefersCondensedTitle())
|
|
||||||
? getTitleCondensed()
|
|
||||||
: getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setTitle(CharSequence title) {
|
|
||||||
mTitle = title;
|
|
||||||
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
|
|
||||||
if (mSubMenu != null) {
|
|
||||||
mSubMenu.setHeaderTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setTitle(int title) {
|
|
||||||
return setTitle(mMenu.getContext().getString(title));
|
|
||||||
}
|
|
||||||
|
|
||||||
public CharSequence getTitleCondensed() {
|
|
||||||
return mTitleCondensed != null ? mTitleCondensed : mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setTitleCondensed(CharSequence title) {
|
|
||||||
mTitleCondensed = title;
|
|
||||||
|
|
||||||
// Could use getTitle() in the loop below, but just cache what it would do here
|
|
||||||
if (title == null) {
|
|
||||||
title = mTitle;
|
|
||||||
}
|
|
||||||
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Drawable getIcon() {
|
|
||||||
if (mIconDrawable != null) {
|
|
||||||
return mIconDrawable;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIconResId != NO_ICON) {
|
|
||||||
return mMenu.getResources().getDrawable(mIconResId);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setIcon(Drawable icon) {
|
|
||||||
mIconResId = NO_ICON;
|
|
||||||
mIconDrawable = icon;
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setIcon(int iconResId) {
|
|
||||||
mIconDrawable = null;
|
|
||||||
mIconResId = iconResId;
|
|
||||||
|
|
||||||
// If we have a view, we need to push the Drawable to them
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCheckable() {
|
|
||||||
return (mFlags & CHECKABLE) == CHECKABLE;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setCheckable(boolean checkable) {
|
|
||||||
final int oldFlags = mFlags;
|
|
||||||
mFlags = (mFlags & ~CHECKABLE) | (checkable ? CHECKABLE : 0);
|
|
||||||
if (oldFlags != mFlags) {
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setExclusiveCheckable(boolean exclusive) {
|
|
||||||
mFlags = (mFlags & ~EXCLUSIVE) | (exclusive ? EXCLUSIVE : 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isExclusiveCheckable() {
|
|
||||||
return (mFlags & EXCLUSIVE) != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isChecked() {
|
|
||||||
return (mFlags & CHECKED) == CHECKED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setChecked(boolean checked) {
|
|
||||||
if ((mFlags & EXCLUSIVE) != 0) {
|
|
||||||
// Call the method on the Menu since it knows about the others in this
|
|
||||||
// exclusive checkable group
|
|
||||||
mMenu.setExclusiveItemChecked(this);
|
|
||||||
} else {
|
|
||||||
setCheckedInt(checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setCheckedInt(boolean checked) {
|
|
||||||
final int oldFlags = mFlags;
|
|
||||||
mFlags = (mFlags & ~CHECKED) | (checked ? CHECKED : 0);
|
|
||||||
if (oldFlags != mFlags) {
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVisible() {
|
|
||||||
return (mFlags & HIDDEN) == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the visibility of the item. This method DOES NOT notify the
|
|
||||||
* parent menu of a change in this item, so this should only be called from
|
|
||||||
* methods that will eventually trigger this change. If unsure, use {@link #setVisible(boolean)}
|
|
||||||
* instead.
|
|
||||||
*
|
|
||||||
* @param shown Whether to show (true) or hide (false).
|
|
||||||
* @return Whether the item's shown state was changed
|
|
||||||
*/
|
|
||||||
boolean setVisibleInt(boolean shown) {
|
|
||||||
final int oldFlags = mFlags;
|
|
||||||
mFlags = (mFlags & ~HIDDEN) | (shown ? 0 : HIDDEN);
|
|
||||||
return oldFlags != mFlags;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setVisible(boolean shown) {
|
|
||||||
// Try to set the shown state to the given state. If the shown state was changed
|
|
||||||
// (i.e. the previous state isn't the same as given state), notify the parent menu that
|
|
||||||
// the shown state has changed for this item
|
|
||||||
if (setVisibleInt(shown)) mMenu.onItemVisibleChanged(this);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener clickListener) {
|
|
||||||
mClickListener = clickListener;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return mTitle.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setMenuInfo(ContextMenuInfo menuInfo) {
|
|
||||||
mMenuInfo = menuInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ContextMenuInfo getMenuInfo() {
|
|
||||||
return mMenuInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void actionFormatChanged() {
|
|
||||||
mMenu.onItemActionRequestChanged(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return Whether the menu should show icons for menu items.
|
|
||||||
*/
|
|
||||||
public boolean shouldShowIcon() {
|
|
||||||
return mMenu.getOptionalIconsVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isActionButton() {
|
|
||||||
return (mFlags & IS_ACTION) == IS_ACTION;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean requestsActionButton() {
|
|
||||||
return (mShowAsAction & SHOW_AS_ACTION_IF_ROOM) == SHOW_AS_ACTION_IF_ROOM;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean requiresActionButton() {
|
|
||||||
return (mShowAsAction & SHOW_AS_ACTION_ALWAYS) == SHOW_AS_ACTION_ALWAYS;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIsActionButton(boolean isActionButton) {
|
|
||||||
if (isActionButton) {
|
|
||||||
mFlags |= IS_ACTION;
|
|
||||||
} else {
|
|
||||||
mFlags &= ~IS_ACTION;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean showsTextAsAction() {
|
|
||||||
return (mShowAsAction & SHOW_AS_ACTION_WITH_TEXT) == SHOW_AS_ACTION_WITH_TEXT;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShowAsAction(int actionEnum) {
|
|
||||||
switch (actionEnum & SHOW_AS_ACTION_MASK) {
|
|
||||||
case SHOW_AS_ACTION_ALWAYS:
|
|
||||||
case SHOW_AS_ACTION_IF_ROOM:
|
|
||||||
case SHOW_AS_ACTION_NEVER:
|
|
||||||
// Looks good!
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
// Mutually exclusive options selected!
|
|
||||||
throw new IllegalArgumentException("SHOW_AS_ACTION_ALWAYS, SHOW_AS_ACTION_IF_ROOM,"
|
|
||||||
+ " and SHOW_AS_ACTION_NEVER are mutually exclusive.");
|
|
||||||
}
|
|
||||||
mShowAsAction = actionEnum;
|
|
||||||
mMenu.onItemActionRequestChanged(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setActionView(View view) {
|
|
||||||
mActionView = view;
|
|
||||||
mActionProvider = null;
|
|
||||||
if (view != null && view.getId() == View.NO_ID && mId > 0) {
|
|
||||||
view.setId(mId);
|
|
||||||
}
|
|
||||||
mMenu.onItemActionRequestChanged(this);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setActionView(int resId) {
|
|
||||||
final Context context = mMenu.getContext();
|
|
||||||
final LayoutInflater inflater = LayoutInflater.from(context);
|
|
||||||
setActionView(inflater.inflate(resId, new LinearLayout(context), false));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public View getActionView() {
|
|
||||||
if (mActionView != null) {
|
|
||||||
return mActionView;
|
|
||||||
} else if (mActionProvider != null) {
|
|
||||||
mActionView = mActionProvider.onCreateActionView();
|
|
||||||
return mActionView;
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ActionProvider getActionProvider() {
|
|
||||||
return mActionProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem setActionProvider(ActionProvider actionProvider) {
|
|
||||||
mActionView = null;
|
|
||||||
mActionProvider = actionProvider;
|
|
||||||
mMenu.onItemsChanged(true); // Measurement can be changed
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setShowAsActionFlags(int actionEnum) {
|
|
||||||
setShowAsAction(actionEnum);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean expandActionView() {
|
|
||||||
if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0 || mActionView == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mOnActionExpandListener == null ||
|
|
||||||
mOnActionExpandListener.onMenuItemActionExpand(this)) {
|
|
||||||
return mMenu.expandItemActionView(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean collapseActionView() {
|
|
||||||
if ((mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (mActionView == null) {
|
|
||||||
// We're already collapsed if we have no action view.
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mOnActionExpandListener == null ||
|
|
||||||
mOnActionExpandListener.onMenuItemActionCollapse(this)) {
|
|
||||||
return mMenu.collapseItemActionView(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
|
|
||||||
mOnActionExpandListener = listener;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasCollapsibleActionView() {
|
|
||||||
return (mShowAsAction & SHOW_AS_ACTION_COLLAPSE_ACTION_VIEW) != 0 && mActionView != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setActionViewExpanded(boolean isExpanded) {
|
|
||||||
mIsActionViewExpanded = isExpanded;
|
|
||||||
mMenu.onItemsChanged(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isActionViewExpanded() {
|
|
||||||
return mIsActionViewExpanded;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,310 +0,0 @@
|
|||||||
package com.actionbarsherlock.internal.view.menu;
|
|
||||||
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.view.ContextMenu.ContextMenuInfo;
|
|
||||||
import android.view.View;
|
|
||||||
import com.actionbarsherlock.internal.view.ActionProviderWrapper;
|
|
||||||
import com.actionbarsherlock.internal.widget.CollapsibleActionViewWrapper;
|
|
||||||
import com.actionbarsherlock.view.ActionProvider;
|
|
||||||
import com.actionbarsherlock.view.CollapsibleActionView;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
import com.actionbarsherlock.view.SubMenu;
|
|
||||||
|
|
||||||
public class MenuItemWrapper implements MenuItem, android.view.MenuItem.OnMenuItemClickListener {
|
|
||||||
private final android.view.MenuItem mNativeItem;
|
|
||||||
private SubMenu mSubMenu = null;
|
|
||||||
private OnMenuItemClickListener mMenuItemClickListener = null;
|
|
||||||
private OnActionExpandListener mActionExpandListener = null;
|
|
||||||
private android.view.MenuItem.OnActionExpandListener mNativeActionExpandListener = null;
|
|
||||||
|
|
||||||
|
|
||||||
public MenuItemWrapper(android.view.MenuItem nativeItem) {
|
|
||||||
if (nativeItem == null) {
|
|
||||||
throw new IllegalStateException("Wrapped menu item cannot be null.");
|
|
||||||
}
|
|
||||||
mNativeItem = nativeItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getItemId() {
|
|
||||||
return mNativeItem.getItemId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getGroupId() {
|
|
||||||
return mNativeItem.getGroupId();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getOrder() {
|
|
||||||
return mNativeItem.getOrder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setTitle(CharSequence title) {
|
|
||||||
mNativeItem.setTitle(title);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setTitle(int title) {
|
|
||||||
mNativeItem.setTitle(title);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence getTitle() {
|
|
||||||
return mNativeItem.getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setTitleCondensed(CharSequence title) {
|
|
||||||
mNativeItem.setTitleCondensed(title);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CharSequence getTitleCondensed() {
|
|
||||||
return mNativeItem.getTitleCondensed();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setIcon(Drawable icon) {
|
|
||||||
mNativeItem.setIcon(icon);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setIcon(int iconRes) {
|
|
||||||
mNativeItem.setIcon(iconRes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Drawable getIcon() {
|
|
||||||
return mNativeItem.getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setIntent(Intent intent) {
|
|
||||||
mNativeItem.setIntent(intent);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Intent getIntent() {
|
|
||||||
return mNativeItem.getIntent();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setShortcut(char numericChar, char alphaChar) {
|
|
||||||
mNativeItem.setShortcut(numericChar, alphaChar);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setNumericShortcut(char numericChar) {
|
|
||||||
mNativeItem.setNumericShortcut(numericChar);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char getNumericShortcut() {
|
|
||||||
return mNativeItem.getNumericShortcut();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setAlphabeticShortcut(char alphaChar) {
|
|
||||||
mNativeItem.setAlphabeticShortcut(alphaChar);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public char getAlphabeticShortcut() {
|
|
||||||
return mNativeItem.getAlphabeticShortcut();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setCheckable(boolean checkable) {
|
|
||||||
mNativeItem.setCheckable(checkable);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isCheckable() {
|
|
||||||
return mNativeItem.isCheckable();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setChecked(boolean checked) {
|
|
||||||
mNativeItem.setChecked(checked);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isChecked() {
|
|
||||||
return mNativeItem.isChecked();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setVisible(boolean visible) {
|
|
||||||
mNativeItem.setVisible(visible);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVisible() {
|
|
||||||
return mNativeItem.isVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setEnabled(boolean enabled) {
|
|
||||||
mNativeItem.setEnabled(enabled);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isEnabled() {
|
|
||||||
return mNativeItem.isEnabled();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasSubMenu() {
|
|
||||||
return mNativeItem.hasSubMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu getSubMenu() {
|
|
||||||
if (hasSubMenu() && (mSubMenu == null)) {
|
|
||||||
mSubMenu = new SubMenuWrapper(mNativeItem.getSubMenu());
|
|
||||||
}
|
|
||||||
return mSubMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setOnMenuItemClickListener(OnMenuItemClickListener menuItemClickListener) {
|
|
||||||
mMenuItemClickListener = menuItemClickListener;
|
|
||||||
//Register ourselves as the listener to proxy
|
|
||||||
mNativeItem.setOnMenuItemClickListener(this);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMenuItemClick(android.view.MenuItem item) {
|
|
||||||
if (mMenuItemClickListener != null) {
|
|
||||||
return mMenuItemClickListener.onMenuItemClick(this);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ContextMenuInfo getMenuInfo() {
|
|
||||||
return mNativeItem.getMenuInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setShowAsAction(int actionEnum) {
|
|
||||||
mNativeItem.setShowAsAction(actionEnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setShowAsActionFlags(int actionEnum) {
|
|
||||||
mNativeItem.setShowAsActionFlags(actionEnum);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setActionView(View view) {
|
|
||||||
if (view != null && view instanceof CollapsibleActionView) {
|
|
||||||
view = new CollapsibleActionViewWrapper(view);
|
|
||||||
}
|
|
||||||
mNativeItem.setActionView(view);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setActionView(int resId) {
|
|
||||||
//Allow the native menu to inflate the resource
|
|
||||||
mNativeItem.setActionView(resId);
|
|
||||||
if (resId != 0) {
|
|
||||||
//Get newly created view
|
|
||||||
View view = mNativeItem.getActionView();
|
|
||||||
if (view instanceof CollapsibleActionView) {
|
|
||||||
//Wrap it and re-set it
|
|
||||||
mNativeItem.setActionView(new CollapsibleActionViewWrapper(view));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View getActionView() {
|
|
||||||
View actionView = mNativeItem.getActionView();
|
|
||||||
if (actionView instanceof CollapsibleActionViewWrapper) {
|
|
||||||
return ((CollapsibleActionViewWrapper)actionView).unwrap();
|
|
||||||
}
|
|
||||||
return actionView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setActionProvider(ActionProvider actionProvider) {
|
|
||||||
mNativeItem.setActionProvider(new ActionProviderWrapper(actionProvider));
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ActionProvider getActionProvider() {
|
|
||||||
android.view.ActionProvider nativeProvider = mNativeItem.getActionProvider();
|
|
||||||
if (nativeProvider != null && nativeProvider instanceof ActionProviderWrapper) {
|
|
||||||
return ((ActionProviderWrapper)nativeProvider).unwrap();
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean expandActionView() {
|
|
||||||
return mNativeItem.expandActionView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean collapseActionView() {
|
|
||||||
return mNativeItem.collapseActionView();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isActionViewExpanded() {
|
|
||||||
return mNativeItem.isActionViewExpanded();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem setOnActionExpandListener(OnActionExpandListener listener) {
|
|
||||||
mActionExpandListener = listener;
|
|
||||||
|
|
||||||
if (mNativeActionExpandListener == null) {
|
|
||||||
mNativeActionExpandListener = new android.view.MenuItem.OnActionExpandListener() {
|
|
||||||
@Override
|
|
||||||
public boolean onMenuItemActionExpand(android.view.MenuItem menuItem) {
|
|
||||||
if (mActionExpandListener != null) {
|
|
||||||
return mActionExpandListener.onMenuItemActionExpand(MenuItemWrapper.this);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMenuItemActionCollapse(android.view.MenuItem menuItem) {
|
|
||||||
if (mActionExpandListener != null) {
|
|
||||||
return mActionExpandListener.onMenuItemActionCollapse(MenuItemWrapper.this);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
//Register our inner-class as the listener to proxy method calls
|
|
||||||
mNativeItem.setOnActionExpandListener(mNativeActionExpandListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,376 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.database.DataSetObserver;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.View.MeasureSpec;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.view.ViewTreeObserver;
|
|
||||||
import android.widget.AdapterView;
|
|
||||||
import android.widget.BaseAdapter;
|
|
||||||
import android.widget.FrameLayout;
|
|
||||||
import android.widget.ListAdapter;
|
|
||||||
import android.widget.PopupWindow;
|
|
||||||
import com.actionbarsherlock.R;
|
|
||||||
import com.actionbarsherlock.internal.view.View_HasStateListenerSupport;
|
|
||||||
import com.actionbarsherlock.internal.view.View_OnAttachStateChangeListener;
|
|
||||||
import com.actionbarsherlock.internal.widget.IcsListPopupWindow;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Presents a menu as a small, simple popup anchored to another view.
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public class MenuPopupHelper implements AdapterView.OnItemClickListener, View.OnKeyListener,
|
|
||||||
ViewTreeObserver.OnGlobalLayoutListener, PopupWindow.OnDismissListener,
|
|
||||||
View_OnAttachStateChangeListener, MenuPresenter {
|
|
||||||
//UNUSED private static final String TAG = "MenuPopupHelper";
|
|
||||||
|
|
||||||
static final int ITEM_LAYOUT = R.layout.abs__popup_menu_item_layout;
|
|
||||||
|
|
||||||
private Context mContext;
|
|
||||||
private LayoutInflater mInflater;
|
|
||||||
private IcsListPopupWindow mPopup;
|
|
||||||
private MenuBuilder mMenu;
|
|
||||||
private int mPopupMaxWidth;
|
|
||||||
private View mAnchorView;
|
|
||||||
private boolean mOverflowOnly;
|
|
||||||
private ViewTreeObserver mTreeObserver;
|
|
||||||
|
|
||||||
private MenuAdapter mAdapter;
|
|
||||||
|
|
||||||
private Callback mPresenterCallback;
|
|
||||||
|
|
||||||
boolean mForceShowIcon;
|
|
||||||
|
|
||||||
private ViewGroup mMeasureParent;
|
|
||||||
|
|
||||||
public MenuPopupHelper(Context context, MenuBuilder menu) {
|
|
||||||
this(context, menu, null, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuPopupHelper(Context context, MenuBuilder menu, View anchorView) {
|
|
||||||
this(context, menu, anchorView, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuPopupHelper(Context context, MenuBuilder menu,
|
|
||||||
View anchorView, boolean overflowOnly) {
|
|
||||||
mContext = context;
|
|
||||||
mInflater = LayoutInflater.from(context);
|
|
||||||
mMenu = menu;
|
|
||||||
mOverflowOnly = overflowOnly;
|
|
||||||
|
|
||||||
final Resources res = context.getResources();
|
|
||||||
mPopupMaxWidth = Math.max(res.getDisplayMetrics().widthPixels / 2,
|
|
||||||
res.getDimensionPixelSize(R.dimen.abs__config_prefDialogWidth));
|
|
||||||
|
|
||||||
mAnchorView = anchorView;
|
|
||||||
|
|
||||||
menu.addMenuPresenter(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setAnchorView(View anchor) {
|
|
||||||
mAnchorView = anchor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setForceShowIcon(boolean forceShow) {
|
|
||||||
mForceShowIcon = forceShow;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void show() {
|
|
||||||
if (!tryShow()) {
|
|
||||||
throw new IllegalStateException("MenuPopupHelper cannot be used without an anchor");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean tryShow() {
|
|
||||||
mPopup = new IcsListPopupWindow(mContext, null, R.attr.popupMenuStyle);
|
|
||||||
mPopup.setOnDismissListener(this);
|
|
||||||
mPopup.setOnItemClickListener(this);
|
|
||||||
|
|
||||||
mAdapter = new MenuAdapter(mMenu);
|
|
||||||
mPopup.setAdapter(mAdapter);
|
|
||||||
mPopup.setModal(true);
|
|
||||||
|
|
||||||
View anchor = mAnchorView;
|
|
||||||
if (anchor != null) {
|
|
||||||
final boolean addGlobalListener = mTreeObserver == null;
|
|
||||||
mTreeObserver = anchor.getViewTreeObserver(); // Refresh to latest
|
|
||||||
if (addGlobalListener) mTreeObserver.addOnGlobalLayoutListener(this);
|
|
||||||
((View_HasStateListenerSupport)anchor).addOnAttachStateChangeListener(this);
|
|
||||||
mPopup.setAnchorView(anchor);
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
mPopup.setContentWidth(Math.min(measureContentWidth(mAdapter), mPopupMaxWidth));
|
|
||||||
mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED);
|
|
||||||
mPopup.show();
|
|
||||||
mPopup.getListView().setOnKeyListener(this);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void dismiss() {
|
|
||||||
if (isShowing()) {
|
|
||||||
mPopup.dismiss();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onDismiss() {
|
|
||||||
mPopup = null;
|
|
||||||
mMenu.close();
|
|
||||||
if (mTreeObserver != null) {
|
|
||||||
if (!mTreeObserver.isAlive()) mTreeObserver = mAnchorView.getViewTreeObserver();
|
|
||||||
mTreeObserver.removeGlobalOnLayoutListener(this);
|
|
||||||
mTreeObserver = null;
|
|
||||||
}
|
|
||||||
((View_HasStateListenerSupport)mAnchorView).removeOnAttachStateChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShowing() {
|
|
||||||
return mPopup != null && mPopup.isShowing();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
|
||||||
MenuAdapter adapter = mAdapter;
|
|
||||||
adapter.mAdapterMenu.performItemAction(adapter.getItem(position), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean onKey(View v, int keyCode, KeyEvent event) {
|
|
||||||
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_MENU) {
|
|
||||||
dismiss();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int measureContentWidth(ListAdapter adapter) {
|
|
||||||
// Menus don't tend to be long, so this is more sane than it looks.
|
|
||||||
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);
|
|
||||||
final int count = adapter.getCount();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
final int positionType = adapter.getItemViewType(i);
|
|
||||||
if (positionType != itemType) {
|
|
||||||
itemType = positionType;
|
|
||||||
itemView = null;
|
|
||||||
}
|
|
||||||
if (mMeasureParent == null) {
|
|
||||||
mMeasureParent = new FrameLayout(mContext);
|
|
||||||
}
|
|
||||||
itemView = adapter.getView(i, itemView, mMeasureParent);
|
|
||||||
itemView.measure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
width = Math.max(width, itemView.getMeasuredWidth());
|
|
||||||
}
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGlobalLayout() {
|
|
||||||
if (isShowing()) {
|
|
||||||
final View anchor = mAnchorView;
|
|
||||||
if (anchor == null || !anchor.isShown()) {
|
|
||||||
dismiss();
|
|
||||||
} else if (isShowing()) {
|
|
||||||
// Recompute window size and position
|
|
||||||
mPopup.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewAttachedToWindow(View v) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onViewDetachedFromWindow(View v) {
|
|
||||||
if (mTreeObserver != null) {
|
|
||||||
if (!mTreeObserver.isAlive()) mTreeObserver = v.getViewTreeObserver();
|
|
||||||
mTreeObserver.removeGlobalOnLayoutListener(this);
|
|
||||||
}
|
|
||||||
((View_HasStateListenerSupport)v).removeOnAttachStateChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void initForMenu(Context context, MenuBuilder menu) {
|
|
||||||
// Don't need to do anything; we added as a presenter in the constructor.
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuView getMenuView(ViewGroup root) {
|
|
||||||
throw new UnsupportedOperationException("MenuPopupHelpers manage their own views");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void updateMenuView(boolean cleared) {
|
|
||||||
if (mAdapter != null) mAdapter.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCallback(Callback cb) {
|
|
||||||
mPresenterCallback = cb;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
|
|
||||||
if (subMenu.hasVisibleItems()) {
|
|
||||||
MenuPopupHelper subPopup = new MenuPopupHelper(mContext, subMenu, mAnchorView, false);
|
|
||||||
subPopup.setCallback(mPresenterCallback);
|
|
||||||
|
|
||||||
boolean preserveIconSpacing = false;
|
|
||||||
final int count = subMenu.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
MenuItem childItem = subMenu.getItem(i);
|
|
||||||
if (childItem.isVisible() && childItem.getIcon() != null) {
|
|
||||||
preserveIconSpacing = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
subPopup.setForceShowIcon(preserveIconSpacing);
|
|
||||||
|
|
||||||
if (subPopup.tryShow()) {
|
|
||||||
if (mPresenterCallback != null) {
|
|
||||||
mPresenterCallback.onOpenSubMenu(subMenu);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
|
|
||||||
// Only care about the (sub)menu we're presenting.
|
|
||||||
if (menu != mMenu) return;
|
|
||||||
|
|
||||||
dismiss();
|
|
||||||
if (mPresenterCallback != null) {
|
|
||||||
mPresenterCallback.onCloseMenu(menu, allMenusAreClosing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean flagActionItems() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getId() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Parcelable onSaveInstanceState() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRestoreInstanceState(Parcelable state) {
|
|
||||||
}
|
|
||||||
|
|
||||||
private class MenuAdapter extends BaseAdapter {
|
|
||||||
private MenuBuilder mAdapterMenu;
|
|
||||||
private int mExpandedIndex = -1;
|
|
||||||
|
|
||||||
public MenuAdapter(MenuBuilder menu) {
|
|
||||||
mAdapterMenu = menu;
|
|
||||||
registerDataSetObserver(new ExpandedIndexObserver());
|
|
||||||
findExpandedIndex();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getCount() {
|
|
||||||
ArrayList<MenuItemImpl> items = mOverflowOnly ?
|
|
||||||
mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
|
|
||||||
if (mExpandedIndex < 0) {
|
|
||||||
return items.size();
|
|
||||||
}
|
|
||||||
return items.size() - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItemImpl getItem(int position) {
|
|
||||||
ArrayList<MenuItemImpl> items = mOverflowOnly ?
|
|
||||||
mAdapterMenu.getNonActionItems() : mAdapterMenu.getVisibleItems();
|
|
||||||
if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
|
|
||||||
position++;
|
|
||||||
}
|
|
||||||
return items.get(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getItemId(int position) {
|
|
||||||
// Since a menu item's ID is optional, we'll use the position as an
|
|
||||||
// ID for the item in the AdapterView
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public View getView(int position, View convertView, ViewGroup parent) {
|
|
||||||
if (convertView == null) {
|
|
||||||
convertView = mInflater.inflate(ITEM_LAYOUT, parent, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuView.ItemView itemView = (MenuView.ItemView) convertView;
|
|
||||||
if (mForceShowIcon) {
|
|
||||||
((ListMenuItemView) convertView).setForceShowIcon(true);
|
|
||||||
}
|
|
||||||
itemView.initialize(getItem(position), 0);
|
|
||||||
return convertView;
|
|
||||||
}
|
|
||||||
|
|
||||||
void findExpandedIndex() {
|
|
||||||
final MenuItemImpl expandedItem = mMenu.getExpandedItem();
|
|
||||||
if (expandedItem != null) {
|
|
||||||
final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
|
|
||||||
final int count = items.size();
|
|
||||||
for (int i = 0; i < count; i++) {
|
|
||||||
final MenuItemImpl item = items.get(i);
|
|
||||||
if (item == expandedItem) {
|
|
||||||
mExpandedIndex = i;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mExpandedIndex = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ExpandedIndexObserver extends DataSetObserver {
|
|
||||||
@Override
|
|
||||||
public void onChanged() {
|
|
||||||
mAdapter.findExpandedIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,148 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.os.Parcelable;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A MenuPresenter is responsible for building views for a Menu object.
|
|
||||||
* It takes over some responsibility from the old style monolithic MenuBuilder class.
|
|
||||||
*/
|
|
||||||
public interface MenuPresenter {
|
|
||||||
/**
|
|
||||||
* Called by menu implementation to notify another component of open/close events.
|
|
||||||
*/
|
|
||||||
public interface Callback {
|
|
||||||
/**
|
|
||||||
* Called when a menu is closing.
|
|
||||||
* @param menu
|
|
||||||
* @param allMenusAreClosing
|
|
||||||
*/
|
|
||||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a submenu opens. Useful for notifying the application
|
|
||||||
* of menu state so that it does not attempt to hide the action bar
|
|
||||||
* while a submenu is open or similar.
|
|
||||||
*
|
|
||||||
* @param subMenu Submenu currently being opened
|
|
||||||
* @return true if the Callback will handle presenting the submenu, false if
|
|
||||||
* the presenter should attempt to do so.
|
|
||||||
*/
|
|
||||||
public boolean onOpenSubMenu(MenuBuilder subMenu);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize this presenter for the given context and menu.
|
|
||||||
* This method is called by MenuBuilder when a presenter is
|
|
||||||
* added. See {@link MenuBuilder#addMenuPresenter(MenuPresenter)}
|
|
||||||
*
|
|
||||||
* @param context Context for this presenter; used for view creation and resource management
|
|
||||||
* @param menu Menu to host
|
|
||||||
*/
|
|
||||||
public void initForMenu(Context context, MenuBuilder menu);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve a MenuView to display the menu specified in
|
|
||||||
* {@link #initForMenu(Context, Menu)}.
|
|
||||||
*
|
|
||||||
* @param root Intended parent of the MenuView.
|
|
||||||
* @return A freshly created MenuView.
|
|
||||||
*/
|
|
||||||
public MenuView getMenuView(ViewGroup root);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the menu UI in response to a change. Called by
|
|
||||||
* MenuBuilder during the normal course of operation.
|
|
||||||
*
|
|
||||||
* @param cleared true if the menu was entirely cleared
|
|
||||||
*/
|
|
||||||
public void updateMenuView(boolean cleared);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set a callback object that will be notified of menu events
|
|
||||||
* related to this specific presentation.
|
|
||||||
* @param cb Callback that will be notified of future events
|
|
||||||
*/
|
|
||||||
public void setCallback(Callback cb);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Menu implementations to indicate that a submenu item
|
|
||||||
* has been selected. An active Callback should be notified, and
|
|
||||||
* if applicable the presenter should present the submenu.
|
|
||||||
*
|
|
||||||
* @param subMenu SubMenu being opened
|
|
||||||
* @return true if the the event was handled, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean onSubMenuSelected(SubMenuBuilder subMenu);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Menu implementations to indicate that a menu or submenu is
|
|
||||||
* closing. Presenter implementations should close the representation
|
|
||||||
* of the menu indicated as necessary and notify a registered callback.
|
|
||||||
*
|
|
||||||
* @param menu Menu or submenu that is closing.
|
|
||||||
* @param allMenusAreClosing True if all associated menus are closing.
|
|
||||||
*/
|
|
||||||
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by Menu implementations to flag items that will be shown as actions.
|
|
||||||
* @return true if this presenter changed the action status of any items.
|
|
||||||
*/
|
|
||||||
public boolean flagActionItems();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a menu item with a collapsable action view should expand its action view.
|
|
||||||
*
|
|
||||||
* @param menu Menu containing the item to be expanded
|
|
||||||
* @param item Item to be expanded
|
|
||||||
* @return true if this presenter expanded the action view, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a menu item with a collapsable action view should collapse its action view.
|
|
||||||
*
|
|
||||||
* @param menu Menu containing the item to be collapsed
|
|
||||||
* @param item Item to be collapsed
|
|
||||||
* @return true if this presenter collapsed the action view, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns an ID for determining how to save/restore instance state.
|
|
||||||
* @return a valid ID value.
|
|
||||||
*/
|
|
||||||
public int getId();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a Parcelable describing the current state of the presenter.
|
|
||||||
* It will be passed to the {@link #onRestoreInstanceState(Parcelable)}
|
|
||||||
* method of the presenter sharing the same ID later.
|
|
||||||
* @return The saved instance state
|
|
||||||
*/
|
|
||||||
public Parcelable onSaveInstanceState();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Supplies the previously saved instance state to be restored.
|
|
||||||
* @param state The previously saved instance state
|
|
||||||
*/
|
|
||||||
public void onRestoreInstanceState(Parcelable state);
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimal interface for a menu view. {@link #initialize(MenuBuilder)} must be called for the
|
|
||||||
* menu to be functional.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public interface MenuView {
|
|
||||||
/**
|
|
||||||
* Initializes the menu to the given menu. This should be called after the
|
|
||||||
* view is inflated.
|
|
||||||
*
|
|
||||||
* @param menu The menu that this MenuView should display.
|
|
||||||
*/
|
|
||||||
public void initialize(MenuBuilder menu);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the default animations to be used for this menu when entering/exiting.
|
|
||||||
* @return A resource ID for the default animations to be used for this menu.
|
|
||||||
*/
|
|
||||||
public int getWindowAnimations();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Minimal interface for a menu item view. {@link #initialize(MenuItemImpl, int)} must be called
|
|
||||||
* for the item to be functional.
|
|
||||||
*/
|
|
||||||
public interface ItemView {
|
|
||||||
/**
|
|
||||||
* Initializes with the provided MenuItemData. This should be called after the view is
|
|
||||||
* inflated.
|
|
||||||
* @param itemData The item that this ItemView should display.
|
|
||||||
* @param menuType The type of this menu, one of
|
|
||||||
* {@link MenuBuilder#TYPE_ICON}, {@link MenuBuilder#TYPE_EXPANDED},
|
|
||||||
* {@link MenuBuilder#TYPE_DIALOG}).
|
|
||||||
*/
|
|
||||||
public void initialize(MenuItemImpl itemData, int menuType);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the item data that this view is displaying.
|
|
||||||
* @return the item data, or null if there is not one
|
|
||||||
*/
|
|
||||||
public MenuItemImpl getItemData();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the title of the item view.
|
|
||||||
* @param title The title to set.
|
|
||||||
*/
|
|
||||||
public void setTitle(CharSequence title);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the enabled state of the item view.
|
|
||||||
* @param enabled Whether the item view should be enabled.
|
|
||||||
*/
|
|
||||||
public void setEnabled(boolean enabled);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Displays the checkbox for the item view. This does not ensure the item view will be
|
|
||||||
* checked, for that use {@link #setChecked}.
|
|
||||||
* @param checkable Whether to display the checkbox or to hide it
|
|
||||||
*/
|
|
||||||
public void setCheckable(boolean checkable);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the checkbox for the item view. If the checkbox is hidden, it will NOT be
|
|
||||||
* made visible, call {@link #setCheckable(boolean)} for that.
|
|
||||||
* @param checked Whether the checkbox should be checked
|
|
||||||
*/
|
|
||||||
public void setChecked(boolean checked);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the shortcut for the item.
|
|
||||||
* @param showShortcut Whether a shortcut should be shown(if false, the value of
|
|
||||||
* shortcutKey should be ignored).
|
|
||||||
* @param shortcutKey The shortcut key that should be shown on the ItemView.
|
|
||||||
*/
|
|
||||||
public void setShortcut(boolean showShortcut, char shortcutKey);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the icon of this item view.
|
|
||||||
* @param icon The icon of this item. null to hide the icon.
|
|
||||||
*/
|
|
||||||
public void setIcon(Drawable icon);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether this item view prefers displaying the condensed title rather
|
|
||||||
* than the normal title. If a condensed title is not available, the
|
|
||||||
* normal title will be used.
|
|
||||||
*
|
|
||||||
* @return Whether this item view prefers displaying the condensed
|
|
||||||
* title.
|
|
||||||
*/
|
|
||||||
public boolean prefersCondensedTitle();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether this item view shows an icon.
|
|
||||||
*
|
|
||||||
* @return Whether this item view shows an icon.
|
|
||||||
*/
|
|
||||||
public boolean showsIcon();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,185 +0,0 @@
|
|||||||
package com.actionbarsherlock.internal.view.menu;
|
|
||||||
|
|
||||||
import java.util.WeakHashMap;
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import com.actionbarsherlock.view.Menu;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
import com.actionbarsherlock.view.SubMenu;
|
|
||||||
|
|
||||||
public class MenuWrapper implements Menu {
|
|
||||||
private final android.view.Menu mNativeMenu;
|
|
||||||
|
|
||||||
private final WeakHashMap<android.view.MenuItem, MenuItem> mNativeMap =
|
|
||||||
new WeakHashMap<android.view.MenuItem, MenuItem>();
|
|
||||||
|
|
||||||
|
|
||||||
public MenuWrapper(android.view.Menu nativeMenu) {
|
|
||||||
mNativeMenu = nativeMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
public android.view.Menu unwrap() {
|
|
||||||
return mNativeMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
private MenuItem addInternal(android.view.MenuItem nativeItem) {
|
|
||||||
MenuItem item = new MenuItemWrapper(nativeItem);
|
|
||||||
mNativeMap.put(nativeItem, item);
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem add(CharSequence title) {
|
|
||||||
return addInternal(mNativeMenu.add(title));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem add(int titleRes) {
|
|
||||||
return addInternal(mNativeMenu.add(titleRes));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
|
|
||||||
return addInternal(mNativeMenu.add(groupId, itemId, order, title));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem add(int groupId, int itemId, int order, int titleRes) {
|
|
||||||
return addInternal(mNativeMenu.add(groupId, itemId, order, titleRes));
|
|
||||||
}
|
|
||||||
|
|
||||||
private SubMenu addInternal(android.view.SubMenu nativeSubMenu) {
|
|
||||||
SubMenu subMenu = new SubMenuWrapper(nativeSubMenu);
|
|
||||||
android.view.MenuItem nativeItem = nativeSubMenu.getItem();
|
|
||||||
MenuItem item = subMenu.getItem();
|
|
||||||
mNativeMap.put(nativeItem, item);
|
|
||||||
return subMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu addSubMenu(CharSequence title) {
|
|
||||||
return addInternal(mNativeMenu.addSubMenu(title));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu addSubMenu(int titleRes) {
|
|
||||||
return addInternal(mNativeMenu.addSubMenu(titleRes));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu addSubMenu(int groupId, int itemId, int order, CharSequence title) {
|
|
||||||
return addInternal(mNativeMenu.addSubMenu(groupId, itemId, order, title));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
|
|
||||||
return addInternal(mNativeMenu.addSubMenu(groupId, itemId, order, titleRes));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller, Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
|
|
||||||
int result;
|
|
||||||
if (outSpecificItems != null) {
|
|
||||||
android.view.MenuItem[] nativeOutItems = new android.view.MenuItem[outSpecificItems.length];
|
|
||||||
result = mNativeMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, nativeOutItems);
|
|
||||||
for (int i = 0, length = outSpecificItems.length; i < length; i++) {
|
|
||||||
outSpecificItems[i] = new MenuItemWrapper(nativeOutItems[i]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result = mNativeMenu.addIntentOptions(groupId, itemId, order, caller, specifics, intent, flags, null);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeItem(int id) {
|
|
||||||
mNativeMenu.removeItem(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeGroup(int groupId) {
|
|
||||||
mNativeMenu.removeGroup(groupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clear() {
|
|
||||||
mNativeMap.clear();
|
|
||||||
mNativeMenu.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
|
|
||||||
mNativeMenu.setGroupCheckable(group, checkable, exclusive);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGroupVisible(int group, boolean visible) {
|
|
||||||
mNativeMenu.setGroupVisible(group, visible);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setGroupEnabled(int group, boolean enabled) {
|
|
||||||
mNativeMenu.setGroupEnabled(group, enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean hasVisibleItems() {
|
|
||||||
return mNativeMenu.hasVisibleItems();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem findItem(int id) {
|
|
||||||
android.view.MenuItem nativeItem = mNativeMenu.findItem(id);
|
|
||||||
return findItem(nativeItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem findItem(android.view.MenuItem nativeItem) {
|
|
||||||
if (nativeItem == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
MenuItem wrapped = mNativeMap.get(nativeItem);
|
|
||||||
if (wrapped != null) {
|
|
||||||
return wrapped;
|
|
||||||
}
|
|
||||||
|
|
||||||
return addInternal(nativeItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int size() {
|
|
||||||
return mNativeMenu.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem getItem(int index) {
|
|
||||||
android.view.MenuItem nativeItem = mNativeMenu.getItem(index);
|
|
||||||
return findItem(nativeItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
mNativeMenu.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
|
|
||||||
return mNativeMenu.performShortcut(keyCode, event, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isShortcutKey(int keyCode, KeyEvent event) {
|
|
||||||
return mNativeMenu.isShortcutKey(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean performIdentifierAction(int id, int flags) {
|
|
||||||
return mNativeMenu.performIdentifierAction(id, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setQwertyMode(boolean isQwerty) {
|
|
||||||
mNativeMenu.setQwertyMode(isQwerty);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,134 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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.view.menu;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
import com.actionbarsherlock.view.Menu;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
import com.actionbarsherlock.view.SubMenu;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The model for a sub menu, which is an extension of the menu. Most methods are proxied to
|
|
||||||
* the parent menu.
|
|
||||||
*/
|
|
||||||
public class SubMenuBuilder extends MenuBuilder implements SubMenu {
|
|
||||||
private MenuBuilder mParentMenu;
|
|
||||||
private MenuItemImpl mItem;
|
|
||||||
|
|
||||||
public SubMenuBuilder(Context context, MenuBuilder parentMenu, MenuItemImpl item) {
|
|
||||||
super(context);
|
|
||||||
|
|
||||||
mParentMenu = parentMenu;
|
|
||||||
mItem = item;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setQwertyMode(boolean isQwerty) {
|
|
||||||
mParentMenu.setQwertyMode(isQwerty);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isQwertyMode() {
|
|
||||||
return mParentMenu.isQwertyMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setShortcutsVisible(boolean shortcutsVisible) {
|
|
||||||
mParentMenu.setShortcutsVisible(shortcutsVisible);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isShortcutsVisible() {
|
|
||||||
return mParentMenu.isShortcutsVisible();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Menu getParentMenu() {
|
|
||||||
return mParentMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MenuItem getItem() {
|
|
||||||
return mItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setCallback(Callback callback) {
|
|
||||||
mParentMenu.setCallback(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuBuilder getRootMenu() {
|
|
||||||
return mParentMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean dispatchMenuItemSelected(MenuBuilder menu, MenuItem item) {
|
|
||||||
return super.dispatchMenuItemSelected(menu, item) ||
|
|
||||||
mParentMenu.dispatchMenuItemSelected(menu, item);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu setIcon(Drawable icon) {
|
|
||||||
mItem.setIcon(icon);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu setIcon(int iconRes) {
|
|
||||||
mItem.setIcon(iconRes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu setHeaderIcon(Drawable icon) {
|
|
||||||
return (SubMenu) super.setHeaderIconInt(icon);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu setHeaderIcon(int iconRes) {
|
|
||||||
return (SubMenu) super.setHeaderIconInt(iconRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu setHeaderTitle(CharSequence title) {
|
|
||||||
return (SubMenu) super.setHeaderTitleInt(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu setHeaderTitle(int titleRes) {
|
|
||||||
return (SubMenu) super.setHeaderTitleInt(titleRes);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SubMenu setHeaderView(View view) {
|
|
||||||
return (SubMenu) super.setHeaderViewInt(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean expandItemActionView(MenuItemImpl item) {
|
|
||||||
return mParentMenu.expandItemActionView(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean collapseItemActionView(MenuItemImpl item) {
|
|
||||||
return mParentMenu.collapseItemActionView(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getActionViewStatesKey() {
|
|
||||||
final int itemId = mItem != null ? mItem.getItemId() : 0;
|
|
||||||
if (itemId == 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return super.getActionViewStatesKey() + ":" + itemId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,72 +0,0 @@
|
|||||||
package com.actionbarsherlock.internal.view.menu;
|
|
||||||
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.view.View;
|
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
|
||||||
import com.actionbarsherlock.view.SubMenu;
|
|
||||||
|
|
||||||
public class SubMenuWrapper extends MenuWrapper implements SubMenu {
|
|
||||||
private final android.view.SubMenu mNativeSubMenu;
|
|
||||||
private MenuItem mItem = null;
|
|
||||||
|
|
||||||
public SubMenuWrapper(android.view.SubMenu nativeSubMenu) {
|
|
||||||
super(nativeSubMenu);
|
|
||||||
mNativeSubMenu = nativeSubMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu setHeaderTitle(int titleRes) {
|
|
||||||
mNativeSubMenu.setHeaderTitle(titleRes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu setHeaderTitle(CharSequence title) {
|
|
||||||
mNativeSubMenu.setHeaderTitle(title);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu setHeaderIcon(int iconRes) {
|
|
||||||
mNativeSubMenu.setHeaderIcon(iconRes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu setHeaderIcon(Drawable icon) {
|
|
||||||
mNativeSubMenu.setHeaderIcon(icon);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu setHeaderView(View view) {
|
|
||||||
mNativeSubMenu.setHeaderView(view);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearHeader() {
|
|
||||||
mNativeSubMenu.clearHeader();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu setIcon(int iconRes) {
|
|
||||||
mNativeSubMenu.setIcon(iconRes);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public SubMenu setIcon(Drawable icon) {
|
|
||||||
mNativeSubMenu.setIcon(icon);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MenuItem getItem() {
|
|
||||||
if (mItem == null) {
|
|
||||||
mItem = new MenuItemWrapper(mNativeSubMenu.getItem());
|
|
||||||
}
|
|
||||||
return mItem;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.ads;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.ads;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.all;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.all;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.core;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.core;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.keyboard;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.keyboard;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.menu;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.menu;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.other;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.other;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.sherlock;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.sherlock;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the Manifest class actually packed into APK */
|
|
||||||
public final class Manifest {
|
|
||||||
}
|
|
@ -1,5 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
/* This stub is for using by IDE only. It is NOT the R class actually packed into APK */
|
|
||||||
public final class R {
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2012. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android;
|
|
||||||
|
|
||||||
import android.app.TabActivity;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.widget.TabHost;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.solovyev.android.prefs.StringPreference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 1/9/12
|
|
||||||
* Time: 6:17 PM
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class saves the last opened tab in the tabHost to the SharedPreferences in order to reopen it after closing
|
|
||||||
* The tab is defined by it's tab id and tab activity where it is hold => you can use the same tab ids in different tab activities
|
|
||||||
*/
|
|
||||||
public class LastTabSaver implements TabHost.OnTabChangeListener {
|
|
||||||
|
|
||||||
// prefix of preference's key
|
|
||||||
private static final String LAST_OPENED_TAB_P_KEY = "last_opened_tab_";
|
|
||||||
|
|
||||||
// preference object
|
|
||||||
@NotNull
|
|
||||||
private final StringPreference<String> preference;
|
|
||||||
|
|
||||||
// activity that holds tab host
|
|
||||||
@NotNull
|
|
||||||
private final TabActivity tabActivity;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor applies saved preference value on tabHost returned by android.app.TabActivity#getTabHost() method
|
|
||||||
* and registers as onTabChangeListener
|
|
||||||
*
|
|
||||||
* @param tabActivity tab activity
|
|
||||||
* @param defaultTabId default tab (if no preference value is not defined)
|
|
||||||
*/
|
|
||||||
public LastTabSaver(@NotNull TabActivity tabActivity, @NotNull String defaultTabId) {
|
|
||||||
this.tabActivity = tabActivity;
|
|
||||||
this.preference = StringPreference.newInstance(getPreferenceKey(), defaultTabId);
|
|
||||||
|
|
||||||
final TabHost tabHost = tabActivity.getTabHost();
|
|
||||||
tabHost.setCurrentTabByTag(this.getLastOpenedTabId());
|
|
||||||
tabHost.setOnTabChangedListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Method must be invoked on android.app.Activity#onDestroy() method of tab activity
|
|
||||||
*/
|
|
||||||
public void destroy() {
|
|
||||||
final TabHost tabHost = tabActivity.getTabHost();
|
|
||||||
tabHost.setOnTabChangedListener(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTabChanged(String tabId) {
|
|
||||||
preference.putPreference(PreferenceManager.getDefaultSharedPreferences(tabActivity), tabId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getLastOpenedTabId() {
|
|
||||||
return preference.getPreference(PreferenceManager.getDefaultSharedPreferences(tabActivity));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private String getPreferenceKey() {
|
|
||||||
return LAST_OPENED_TAB_P_KEY + tabActivity.getClass().getName();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.history;
|
|
||||||
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.solovyev.android.view.drag.DragButton;
|
|
||||||
import org.solovyev.android.view.drag.DragDirection;
|
|
||||||
import org.solovyev.android.view.drag.SimpleOnDragListener;
|
|
||||||
import org.solovyev.common.history.HistoryAction;
|
|
||||||
import org.solovyev.common.history.HistoryControl;
|
|
||||||
import org.solovyev.common.math.Point2d;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/16/11
|
|
||||||
* Time: 11:36 PM
|
|
||||||
*/
|
|
||||||
public class HistoryDragProcessor<T> implements SimpleOnDragListener.DragProcessor {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final HistoryControl<T> historyControl;
|
|
||||||
|
|
||||||
public HistoryDragProcessor(@NotNull HistoryControl<T> historyControl) {
|
|
||||||
this.historyControl = historyControl;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
|
|
||||||
boolean result = false;
|
|
||||||
|
|
||||||
Log.d(String.valueOf(dragButton.getId()), "History on drag event start: " + dragDirection);
|
|
||||||
|
|
||||||
final HistoryAction historyAction;
|
|
||||||
if ( dragDirection == DragDirection.up ) {
|
|
||||||
historyAction = HistoryAction.undo;
|
|
||||||
} else if ( dragDirection == DragDirection.down ) {
|
|
||||||
historyAction = HistoryAction.redo;
|
|
||||||
} else {
|
|
||||||
historyAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (historyAction != null) {
|
|
||||||
result = true;
|
|
||||||
historyControl.doHistoryAction(historyAction);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.Labeled;
|
|
||||||
import org.solovyev.android.LabeledFormatter;
|
|
||||||
import org.solovyev.android.view.ListRange;
|
|
||||||
import org.solovyev.android.view.Picker;
|
|
||||||
import org.solovyev.common.text.EnumMapper;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/10/12
|
|
||||||
* Time: 2:02 AM
|
|
||||||
*/
|
|
||||||
public abstract class AbstractEnumPickerDialogPreference<T extends Enum & Labeled> extends AbstractPickerDialogPreference<T> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Class<T> enumClass;
|
|
||||||
|
|
||||||
protected AbstractEnumPickerDialogPreference(Context context,
|
|
||||||
AttributeSet attrs,
|
|
||||||
@Nullable String defaultStringValue,
|
|
||||||
boolean needValueText,
|
|
||||||
@NotNull Class<T> enumClass) {
|
|
||||||
super(context, attrs, defaultStringValue, needValueText, new EnumMapper<T>(enumClass));
|
|
||||||
this.enumClass = enumClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Picker.Range<T> createRange(@NotNull T selected) {
|
|
||||||
return new ListRange<T>(Arrays.asList(enumClass.getEnumConstants()), selected, new LabeledFormatter<T>(getContext()));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.Gravity;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.view.Picker;
|
|
||||||
import org.solovyev.common.text.Mapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/10/12
|
|
||||||
* Time: 1:58 AM
|
|
||||||
*/
|
|
||||||
public abstract class AbstractPickerDialogPreference<T> extends AbstractDialogPreference<T> implements Picker.OnChangedListener<T> {
|
|
||||||
|
|
||||||
|
|
||||||
protected AbstractPickerDialogPreference(Context context,
|
|
||||||
AttributeSet attrs,
|
|
||||||
@Nullable String defaultStringValue,
|
|
||||||
boolean needValueText,
|
|
||||||
@NotNull Mapper<T> mapper) {
|
|
||||||
super(context, attrs, defaultStringValue, needValueText, mapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LinearLayout.LayoutParams getParams() {
|
|
||||||
final LinearLayout.LayoutParams result = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
|
|
||||||
|
|
||||||
result.gravity = Gravity.CENTER;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected View createPreferenceView(@NotNull Context context) {
|
|
||||||
final Picker<T> result = new Picker<T>(context);
|
|
||||||
|
|
||||||
result.setOnChangeListener(this);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initPreferenceView(@NotNull View v, @Nullable T value) {
|
|
||||||
if (value != null) {
|
|
||||||
((Picker<T>) v).setRange(createRange(value));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected abstract Picker.Range<T> createRange(@NotNull T selected);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChanged(@NotNull Picker picker, @NotNull T o) {
|
|
||||||
persistValue(o);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,67 +0,0 @@
|
|||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.view.DoubleRange;
|
|
||||||
import org.solovyev.android.view.NumberRange;
|
|
||||||
import org.solovyev.common.interval.Interval;
|
|
||||||
import org.solovyev.common.text.Formatter;
|
|
||||||
import org.solovyev.common.text.NumberIntervalMapper;
|
|
||||||
|
|
||||||
import java.text.DecimalFormat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/10/12
|
|
||||||
* Time: 1:02 AM
|
|
||||||
*/
|
|
||||||
public class DoublePickerDialogPreference extends NumberPickerDialogPreference<Double> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static final Formatter<Double> simpleFormatter = new DoubleFormatter(2);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Double getDefaultStep() {
|
|
||||||
return 1d;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected NumberRange<Double> createRange(@NotNull Interval<Double> boundaries, @NotNull Double step, @NotNull Double selected) {
|
|
||||||
return DoubleRange.newInstance(boundaries.getLeftLimit(), boundaries.getRightLimit(), step, selected, simpleFormatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DoublePickerDialogPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs, new NumberIntervalMapper<Double>(Double.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class DoubleFormatter implements Formatter<Double> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final DecimalFormat df;
|
|
||||||
|
|
||||||
private DoubleFormatter(int numberOfDecimalDigits) {
|
|
||||||
final StringBuilder sb = new StringBuilder("#.#");
|
|
||||||
for (int i = 1; i < numberOfDecimalDigits; i++) {
|
|
||||||
sb.append("#");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.df = new DecimalFormat(sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String formatValue(@Nullable Double value) throws IllegalArgumentException {
|
|
||||||
|
|
||||||
if (value == null) {
|
|
||||||
return "null";
|
|
||||||
} else {
|
|
||||||
synchronized (df) {
|
|
||||||
return df.format(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.solovyev.common.text.NumberIntervalMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/21/11
|
|
||||||
* Time: 11:41 PM
|
|
||||||
*/
|
|
||||||
public class FloatRangeSeekBarPreference extends RangeSeekBarPreference<Float> {
|
|
||||||
|
|
||||||
public FloatRangeSeekBarPreference(@NotNull Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs, new NumberIntervalMapper<Float>(Float.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Float getDefaultStep() {
|
|
||||||
return 1f;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Float add(@NotNull Float l, @NotNull Float r) {
|
|
||||||
return l + r;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.solovyev.android.view.IntegerRange;
|
|
||||||
import org.solovyev.android.view.NumberRange;
|
|
||||||
import org.solovyev.common.interval.Interval;
|
|
||||||
import org.solovyev.common.text.NumberIntervalMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/26/11
|
|
||||||
* Time: 10:31 PM
|
|
||||||
*/
|
|
||||||
public class IntegerPickerDialogPreference extends NumberPickerDialogPreference<Integer>{
|
|
||||||
|
|
||||||
public IntegerPickerDialogPreference(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs, new NumberIntervalMapper<Integer>(Integer.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Integer getDefaultStep() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected NumberRange<Integer> createRange(@NotNull Interval<Integer> boundaries, @NotNull Integer step, @NotNull Integer selected) {
|
|
||||||
return IntegerRange.newInstance(boundaries.getLeftLimit(), boundaries.getRightLimit(), step, selected);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.solovyev.common.text.NumberIntervalMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/19/11
|
|
||||||
* Time: 10:04 PM
|
|
||||||
*/
|
|
||||||
public class IntegerRangeSeekBarPreference extends RangeSeekBarPreference<Integer> {
|
|
||||||
|
|
||||||
public IntegerRangeSeekBarPreference(@NotNull Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs, new NumberIntervalMapper<Integer>(Integer.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Integer getDefaultStep() {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Integer add(@NotNull Integer l, @NotNull Integer r) {
|
|
||||||
return l + r;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,196 +0,0 @@
|
|||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
|
|
||||||
import android.app.AlertDialog.Builder;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.preference.ListPreference;
|
|
||||||
import android.preference.Preference;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.view.R;
|
|
||||||
import org.solovyev.common.text.CollectionTransformations;
|
|
||||||
import org.solovyev.common.text.StringMapper;
|
|
||||||
import org.solovyev.common.text.StringUtils;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link Preference} that displays a list of entries as
|
|
||||||
* a dialog and allows multiple selections
|
|
||||||
* <p>
|
|
||||||
* This preference will store a string into the SharedPreferences. This string will be the values selected
|
|
||||||
* from the {@link #setEntryValues(CharSequence[])} array.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public class MultiSelectListPreference<T> extends ListPreference {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static final String DEFAULT_SEPARATOR = ";";
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final org.solovyev.common.text.Mapper<List<String>> mapper;
|
|
||||||
|
|
||||||
private boolean[] checkedIndices;
|
|
||||||
|
|
||||||
/*
|
|
||||||
**********************************************************************
|
|
||||||
*
|
|
||||||
* CONSTRUCTORS
|
|
||||||
*
|
|
||||||
**********************************************************************
|
|
||||||
*/
|
|
||||||
|
|
||||||
public MultiSelectListPreference(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public MultiSelectListPreference(Context context, @Nullable AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
|
|
||||||
String separator = DEFAULT_SEPARATOR;
|
|
||||||
|
|
||||||
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiSelectListPreference);
|
|
||||||
for (int i = 0; i < a.getIndexCount(); i++) {
|
|
||||||
int attr = a.getIndex(i);
|
|
||||||
|
|
||||||
final String attrValue = a.getString(attr);
|
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(attrValue)) {
|
|
||||||
switch (attr) {
|
|
||||||
case R.styleable.MultiSelectListPreference_separator:
|
|
||||||
separator = attrValue;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.mapper = new Mapper(separator);
|
|
||||||
|
|
||||||
this.checkedIndices = new boolean[getEntries().length];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEntries(@NotNull CharSequence[] entries) {
|
|
||||||
super.setEntries(entries);
|
|
||||||
|
|
||||||
checkedIndices = new boolean[entries.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPrepareDialogBuilder(@NotNull Builder builder) {
|
|
||||||
final CharSequence[] entries = getEntries();
|
|
||||||
final CharSequence[] entryValues = getEntryValues();
|
|
||||||
|
|
||||||
if (entries == null || entryValues == null || entries.length != entryValues.length) {
|
|
||||||
throw new IllegalStateException("ListPreference requires an entries array and an entryValues array which are both the same length");
|
|
||||||
}
|
|
||||||
|
|
||||||
restoreCheckedEntries();
|
|
||||||
|
|
||||||
builder.setMultiChoiceItems(entries, checkedIndices,
|
|
||||||
new DialogInterface.OnMultiChoiceClickListener() {
|
|
||||||
public void onClick(DialogInterface dialog, int which, boolean value) {
|
|
||||||
checkedIndices[which] = value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private void restoreCheckedEntries() {
|
|
||||||
final CharSequence[] entryValues = getEntryValues();
|
|
||||||
|
|
||||||
final List<String> values = mapper.parseValue(getValue());
|
|
||||||
if (values != null) {
|
|
||||||
for (String value : values) {
|
|
||||||
for (int i = 0; i < entryValues.length; i++) {
|
|
||||||
final CharSequence entry = entryValues[i];
|
|
||||||
if (entry.equals(value)) {
|
|
||||||
checkedIndices[i] = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDialogClosed(boolean positiveResult) {
|
|
||||||
final CharSequence[] entryValues = getEntryValues();
|
|
||||||
if (positiveResult && entryValues != null) {
|
|
||||||
|
|
||||||
final List<String> checkedValues = new ArrayList<String>();
|
|
||||||
for (int i = 0; i < entryValues.length; i++) {
|
|
||||||
if (checkedIndices[i]) {
|
|
||||||
checkedValues.add(entryValues[i].toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
final String value = mapper.formatValue(checkedValues);
|
|
||||||
if (callChangeListener(value)) {
|
|
||||||
setValue(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class Mapper implements org.solovyev.common.text.Mapper<List<String>> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final String separator;
|
|
||||||
|
|
||||||
public Mapper(@NotNull String separator) {
|
|
||||||
this.separator = separator;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String formatValue(@Nullable List<String> value) throws IllegalArgumentException {
|
|
||||||
return CollectionTransformations.formatValue(value, separator, StringMapper.getInstance());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<String> parseValue(@Nullable String value) throws IllegalArgumentException {
|
|
||||||
return CollectionTransformations.split(value, separator, StringMapper.getInstance());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static <T> org.solovyev.common.text.Mapper<List<T>> newListMapper(@NotNull org.solovyev.common.text.Mapper<T> nestedMapper) {
|
|
||||||
return new ListMapper<T>(DEFAULT_SEPARATOR, nestedMapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static <T> org.solovyev.common.text.Mapper<List<T>> newListMapper(@NotNull org.solovyev.common.text.Mapper<T> nestedMapper,
|
|
||||||
@NotNull String separator) {
|
|
||||||
return new ListMapper<T>(separator, nestedMapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class ListMapper<T> implements org.solovyev.common.text.Mapper<List<T>> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final String separator;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final org.solovyev.common.text.Mapper<T> nestedMapper;
|
|
||||||
|
|
||||||
public ListMapper(@NotNull String separator, @NotNull org.solovyev.common.text.Mapper<T> nestedMapper) {
|
|
||||||
this.separator = separator;
|
|
||||||
this.nestedMapper = nestedMapper;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String formatValue(@Nullable List<T> value) throws IllegalArgumentException {
|
|
||||||
return CollectionTransformations.formatValue(value, separator, nestedMapper);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public List<T> parseValue(@Nullable String value) throws IllegalArgumentException {
|
|
||||||
return CollectionTransformations.split(value, separator, nestedMapper);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,52 +0,0 @@
|
|||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.solovyev.android.view.NumberRange;
|
|
||||||
import org.solovyev.android.view.Picker;
|
|
||||||
import org.solovyev.common.interval.Interval;
|
|
||||||
import org.solovyev.common.text.NumberIntervalMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/10/12
|
|
||||||
* Time: 12:57 AM
|
|
||||||
*/
|
|
||||||
public abstract class NumberPickerDialogPreference<N extends Number & Comparable<N>> extends AbstractPickerDialogPreference<N> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Interval<N> boundaries;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final N step;
|
|
||||||
|
|
||||||
protected NumberPickerDialogPreference(Context context,
|
|
||||||
AttributeSet attrs,
|
|
||||||
@NotNull NumberIntervalMapper<N> mapper) {
|
|
||||||
super(context, attrs, null, false, mapper.getMapper());
|
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
boundaries = mapper.parseValue(attrs.getAttributeValue(AbstractDialogPreference.localNameSpace, "boundaries"));
|
|
||||||
|
|
||||||
final String stringStep = attrs.getAttributeValue(AbstractDialogPreference.localNameSpace, "step");
|
|
||||||
if (stringStep == null) {
|
|
||||||
step = getDefaultStep();
|
|
||||||
} else {
|
|
||||||
step = mapper.getMapper().parseValue(stringStep);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected abstract N getDefaultStep();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Picker.Range<N> createRange(@NotNull N selected) {
|
|
||||||
return createRange(boundaries, step, selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected abstract NumberRange<N> createRange(@NotNull Interval<N> boundaries, @NotNull N step, @NotNull N selected);
|
|
||||||
|
|
||||||
}
|
|
@ -1,92 +0,0 @@
|
|||||||
package org.solovyev.android.prefs;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.solovyev.android.view.AbstractRangeSeekBar;
|
|
||||||
import org.solovyev.android.view.NumberRangeSeekBar;
|
|
||||||
import org.solovyev.common.interval.Interval;
|
|
||||||
import org.solovyev.common.interval.IntervalImpl;
|
|
||||||
import org.solovyev.common.text.NumberIntervalMapper;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/19/11
|
|
||||||
* Time: 12:27 PM
|
|
||||||
*/
|
|
||||||
public abstract class RangeSeekBarPreference<T extends Number & Comparable<T>> extends AbstractDialogPreference<Interval<T>> implements AbstractRangeSeekBar.OnRangeSeekBarChangeListener<T> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Interval<T> boundaries;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final T step;
|
|
||||||
|
|
||||||
public RangeSeekBarPreference(@NotNull Context context, AttributeSet attrs, @NotNull NumberIntervalMapper<T> mapper) {
|
|
||||||
super(context, attrs, null, true, mapper);
|
|
||||||
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
boundaries = mapper.parseValue(attrs.getAttributeValue(AbstractDialogPreference.localNameSpace, "boundaries"));
|
|
||||||
|
|
||||||
final String stepValue = attrs.getAttributeValue(AbstractDialogPreference.localNameSpace, "step");
|
|
||||||
if ( stepValue == null ) {
|
|
||||||
step = getDefaultStep();
|
|
||||||
} else {
|
|
||||||
step = mapper.getMapper().parseValue(stepValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected abstract T getDefaultStep();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected View createPreferenceView(@NotNull Context context) {
|
|
||||||
int count = 0;
|
|
||||||
for ( T t = boundaries.getLeftLimit(); t.compareTo(boundaries.getRightLimit()) <= 0; t = add(t, step) ) {
|
|
||||||
count += 1;
|
|
||||||
}
|
|
||||||
final NumberRangeSeekBar<T> result = new NumberRangeSeekBar<T>(boundaries, count, context);
|
|
||||||
|
|
||||||
result.setNotifyWhileDragging(true);
|
|
||||||
result.setOnRangeSeekBarChangeListener(this);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected abstract T add(@NotNull T l, @NotNull T r);
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected LinearLayout.LayoutParams getParams() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initPreferenceView(@NotNull View v, Interval<T> value) {
|
|
||||||
if (value != null) {
|
|
||||||
((NumberRangeSeekBar<T>) v).setSelectedMinValue(value.getLeftLimit());
|
|
||||||
((NumberRangeSeekBar<T>) v).setSelectedMaxValue(value.getRightLimit());
|
|
||||||
setValueText(value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void rangeSeekBarValuesChanged(T minValue, T maxValue, boolean changeComplete) {
|
|
||||||
final Interval<T> interval = IntervalImpl.newClosed(minValue, maxValue);
|
|
||||||
|
|
||||||
if (changeComplete) {
|
|
||||||
persistValue(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
setValueText(interval);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setValueText(@NotNull Interval<T> interval) {
|
|
||||||
final String t = String.valueOf(interval);
|
|
||||||
final String valueText = getValueText();
|
|
||||||
updateValueText(valueText == null ? t : t.concat(valueText));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/2/12
|
|
||||||
* Time: 4:25 PM
|
|
||||||
*/
|
|
||||||
public abstract class AbstractAlertDialogBuilder<D extends AlertDialog> implements DialogBuilder<D> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Context context;
|
|
||||||
|
|
||||||
protected AbstractAlertDialogBuilder(@NotNull Context context) {
|
|
||||||
this.context = context;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected Context getContext() {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,421 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/19/11
|
|
||||||
* Time: 3:30 PM
|
|
||||||
*/
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.*;
|
|
||||||
import android.graphics.Paint.Style;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.common.Converter;
|
|
||||||
import org.solovyev.common.math.LinearNormalizer;
|
|
||||||
import org.solovyev.common.math.Normalizer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Widget that lets users select a minimum and maximum value on a given numerical range.
|
|
||||||
* The range value types can be one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
|
|
||||||
*
|
|
||||||
* @param <T> The Number type of the range values. One of Long, Double, Integer, Float, Short, Byte or BigDecimal.
|
|
||||||
* @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
|
|
||||||
*/
|
|
||||||
public abstract class AbstractRangeSeekBar<T> extends ImageView {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Paint paint = new Paint();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final ThumbContainer tc;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Converter<T, Double> toDoubleConverter;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Converter<Double, T> toTConverter;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final T minValue, maxValue;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Normalizer fromValueNormalizer;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Normalizer fromScreenNormalizer;
|
|
||||||
|
|
||||||
private double normalizedMinValue = 0d;
|
|
||||||
|
|
||||||
private double normalizedMaxValue = 1d;
|
|
||||||
|
|
||||||
private Thumb pressedThumb = null;
|
|
||||||
|
|
||||||
private boolean notifyWhileDragging = false;
|
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private OnRangeSeekBarChangeListener<T> listener;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new RangeSeekBar.
|
|
||||||
*
|
|
||||||
* @param minValue The minimum value of the selectable range.
|
|
||||||
* @param maxValue The maximum value of the selectable range.
|
|
||||||
* @param steps number of steps to be used in range seek bar
|
|
||||||
* @param context parent context
|
|
||||||
* @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
|
|
||||||
*/
|
|
||||||
public AbstractRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, @Nullable Integer steps, Context context) throws IllegalArgumentException {
|
|
||||||
super(context);
|
|
||||||
|
|
||||||
this.minValue = minValue;
|
|
||||||
this.maxValue = maxValue;
|
|
||||||
|
|
||||||
this.toDoubleConverter = getToDoubleConverter();
|
|
||||||
this.toTConverter = getToTConverter();
|
|
||||||
|
|
||||||
fromValueNormalizer = new LinearNormalizer(toDoubleConverter.convert(minValue), toDoubleConverter.convert(maxValue));
|
|
||||||
|
|
||||||
tc = new ThumbContainer();
|
|
||||||
|
|
||||||
fromScreenNormalizer = new Normalizer() {
|
|
||||||
@Override
|
|
||||||
public double normalize(double value) {
|
|
||||||
int width = getWidth();
|
|
||||||
if (width <= 2 * tc.padding) {
|
|
||||||
// prevent division by zero, simply return 0.
|
|
||||||
return 0d;
|
|
||||||
} else {
|
|
||||||
double result = (value - tc.padding) / (width - 2 * tc.padding);
|
|
||||||
return Math.min(1d, Math.max(0d, result));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public double denormalize(double value) {
|
|
||||||
return (float) (tc.padding + value * (getWidth() - 2 * tc.padding));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected abstract Converter<Double,T> getToTConverter();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected abstract Converter<T,Double> getToDoubleConverter();
|
|
||||||
|
|
||||||
public boolean isNotifyWhileDragging() {
|
|
||||||
return notifyWhileDragging;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Should the widget notify the listener callback while the user is still dragging a thumb? Default is false.
|
|
||||||
*
|
|
||||||
* @param flag
|
|
||||||
*/
|
|
||||||
public void setNotifyWhileDragging(boolean flag) {
|
|
||||||
this.notifyWhileDragging = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the absolute minimum value of the range that has been set at construction time.
|
|
||||||
*
|
|
||||||
* @return The absolute minimum value of the range.
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public T getMinValue() {
|
|
||||||
return minValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the absolute maximum value of the range that has been set at construction time.
|
|
||||||
*
|
|
||||||
* @return The absolute maximum value of the range.
|
|
||||||
*/
|
|
||||||
@NotNull
|
|
||||||
public T getMaxValue() {
|
|
||||||
return maxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently selected min value.
|
|
||||||
*
|
|
||||||
* @return The currently selected min value.
|
|
||||||
*/
|
|
||||||
public T getSelectedMinValue() {
|
|
||||||
return denormalizeValue(normalizedMinValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the currently selected minimum value. The widget will be invalidated and redrawn.
|
|
||||||
*
|
|
||||||
* @param value The Number value to set the minimum value to. Will be clamped to given absolute minimum/maximum range.
|
|
||||||
*/
|
|
||||||
public void setSelectedMinValue(@NotNull T value) {
|
|
||||||
setNormalizedMinValue(normalizeValue(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently selected max value.
|
|
||||||
*
|
|
||||||
* @return The currently selected max value.
|
|
||||||
*/
|
|
||||||
public T getSelectedMaxValue() {
|
|
||||||
return denormalizeValue(normalizedMaxValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the currently selected maximum value. The widget will be invalidated and redrawn.
|
|
||||||
*
|
|
||||||
* @param value The Number value to set the maximum value to. Will be clamped to given absolute minimum/maximum range.
|
|
||||||
*/
|
|
||||||
public void setSelectedMaxValue(@NotNull T value) {
|
|
||||||
setNormalizedMaxValue(normalizeValue(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers given listener callback to notify about changed selected values.
|
|
||||||
*
|
|
||||||
* @param listener The listener to notify about changed selected values.
|
|
||||||
*/
|
|
||||||
public void setOnRangeSeekBarChangeListener(OnRangeSeekBarChangeListener<T> listener) {
|
|
||||||
this.listener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Handles thumb selection and movement. Notifies listener callback on certain events.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
switch (event.getAction()) {
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
pressedThumb = evalPressedThumb(event.getX());
|
|
||||||
invalidate();
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
|
||||||
if (pressedThumb != null) {
|
|
||||||
|
|
||||||
double value = convertToNormalizedValue(event.getX());
|
|
||||||
|
|
||||||
if (Thumb.MIN.equals(pressedThumb)) {
|
|
||||||
setNormalizedMinValue(value);
|
|
||||||
} else if (Thumb.MAX.equals(pressedThumb)) {
|
|
||||||
setNormalizedMaxValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (notifyWhileDragging && listener != null) {
|
|
||||||
listener.rangeSeekBarValuesChanged(getSelectedMinValue(), getSelectedMaxValue(), false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
case MotionEvent.ACTION_CANCEL:
|
|
||||||
pressedThumb = null;
|
|
||||||
invalidate();
|
|
||||||
if (listener != null) {
|
|
||||||
listener.rangeSeekBarValuesChanged(getSelectedMinValue(), getSelectedMaxValue(), true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures correct size of the widget.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
int width = 200;
|
|
||||||
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(widthMeasureSpec)) {
|
|
||||||
width = MeasureSpec.getSize(widthMeasureSpec);
|
|
||||||
}
|
|
||||||
|
|
||||||
int height = tc.thumbImage.getHeight();
|
|
||||||
if (MeasureSpec.UNSPECIFIED != MeasureSpec.getMode(heightMeasureSpec)) {
|
|
||||||
height = Math.min(height, MeasureSpec.getSize(heightMeasureSpec));
|
|
||||||
}
|
|
||||||
setMeasuredDimension(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the widget on the given canvas.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
super.onDraw(canvas);
|
|
||||||
// draw seek bar background line
|
|
||||||
final RectF rect = tc.getRect();
|
|
||||||
paint.setStyle(Style.FILL);
|
|
||||||
paint.setColor(Color.GRAY);
|
|
||||||
canvas.drawRect(rect, paint);
|
|
||||||
// draw seek bar active range line
|
|
||||||
rect.left = convertToScreenValue(normalizedMinValue);
|
|
||||||
rect.right = convertToScreenValue(normalizedMaxValue);
|
|
||||||
// orange color
|
|
||||||
paint.setColor(Color.rgb(255, 165, 0));
|
|
||||||
canvas.drawRect(rect, paint);
|
|
||||||
|
|
||||||
// draw minimum thumb
|
|
||||||
drawThumb(convertToScreenValue(normalizedMinValue), Thumb.MIN == pressedThumb, canvas);
|
|
||||||
|
|
||||||
// draw maximum thumb
|
|
||||||
drawThumb(convertToScreenValue(normalizedMaxValue), Thumb.MAX == pressedThumb, canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Draws the "normal" resp. "pressed" thumb image on specified x-coordinate.
|
|
||||||
*
|
|
||||||
* @param normalizedToScreenValue The x-coordinate in screen space where to draw the image.
|
|
||||||
* @param pressed Is the thumb currently in "pressed" state?
|
|
||||||
* @param canvas The canvas to draw upon.
|
|
||||||
*/
|
|
||||||
private void drawThumb(float normalizedToScreenValue, boolean pressed, Canvas canvas) {
|
|
||||||
canvas.drawBitmap(tc.getImage(pressed), normalizedToScreenValue - tc.thumbHalfWidth, (float) ((0.5f * getHeight()) - tc.thumbHalfHeight), paint);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decides which (if any) thumb is touched by the given x-coordinate.
|
|
||||||
*
|
|
||||||
* @param touchX The x-coordinate of a touch event in screen space.
|
|
||||||
* @return The pressed thumb or null if none has been touched.
|
|
||||||
*/
|
|
||||||
private Thumb evalPressedThumb(float touchX) {
|
|
||||||
Thumb result = null;
|
|
||||||
boolean minThumbPressed = isInThumbRange(touchX, normalizedMinValue);
|
|
||||||
boolean maxThumbPressed = isInThumbRange(touchX, normalizedMaxValue);
|
|
||||||
if (minThumbPressed && maxThumbPressed) {
|
|
||||||
// if both thumbs are pressed (they lie on top of each other), choose the one with more room to drag. this avoids "stalling" the thumbs in a corner, not being able to drag them apart anymore.
|
|
||||||
result = (touchX / getWidth() > 0.5f) ? Thumb.MIN : Thumb.MAX;
|
|
||||||
} else if (minThumbPressed) {
|
|
||||||
result = Thumb.MIN;
|
|
||||||
} else if (maxThumbPressed) {
|
|
||||||
result = Thumb.MAX;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decides if given x-coordinate in screen space needs to be interpreted as "within" the normalized thumb x-coordinate.
|
|
||||||
*
|
|
||||||
* @param touchX The x-coordinate in screen space to check.
|
|
||||||
* @param normalizedThumbValue The normalized x-coordinate of the thumb to check.
|
|
||||||
* @return true if x-coordinate is in thumb range, false otherwise.
|
|
||||||
*/
|
|
||||||
private boolean isInThumbRange(float touchX, double normalizedThumbValue) {
|
|
||||||
return Math.abs(touchX - convertToScreenValue(normalizedThumbValue)) <= tc.thumbHalfWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets normalized min value to value so that 0 <= value <= normalized max value <= 1.
|
|
||||||
* The View will get invalidated when calling this method.
|
|
||||||
*
|
|
||||||
* @param value The new normalized min value to set.
|
|
||||||
*/
|
|
||||||
private void setNormalizedMinValue(double value) {
|
|
||||||
normalizedMinValue = Math.max(0d, Math.min(1d, Math.min(value, normalizedMaxValue)));
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets normalized max value to value so that 0 <= normalized min value <= value <= 1.
|
|
||||||
* The View will get invalidated when calling this method.
|
|
||||||
*
|
|
||||||
* @param value The new normalized max value to set.
|
|
||||||
*/
|
|
||||||
private void setNormalizedMaxValue(double value) {
|
|
||||||
normalizedMaxValue = Math.max(0d, Math.min(1d, Math.max(value, normalizedMinValue)));
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a normalized value to a Number object in the value space between absolute minimum and maximum.
|
|
||||||
*
|
|
||||||
* @param normalized
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private T denormalizeValue(double normalized) {
|
|
||||||
return toTConverter.convert(fromValueNormalizer.denormalize(normalized));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the given Number value to a normalized double.
|
|
||||||
*
|
|
||||||
* @param value The Number value to normalize.
|
|
||||||
* @return The normalized double.
|
|
||||||
*/
|
|
||||||
private double normalizeValue(T value) {
|
|
||||||
return fromValueNormalizer.normalize(toDoubleConverter.convert(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts a normalized value into screen space.
|
|
||||||
*
|
|
||||||
* @param normalizedValue The normalized value to convert.
|
|
||||||
* @return The converted value in screen space.
|
|
||||||
*/
|
|
||||||
private float convertToScreenValue(double normalizedValue) {
|
|
||||||
return (float)this.fromScreenNormalizer.denormalize(normalizedValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts screen space x-coordinates into normalized values.
|
|
||||||
*
|
|
||||||
* @param screenValue The x-coordinate in screen space to convert.
|
|
||||||
* @return The normalized value.
|
|
||||||
*/
|
|
||||||
private double convertToNormalizedValue(float screenValue) {
|
|
||||||
return this.fromScreenNormalizer.normalize(screenValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback listener interface to notify about changed range values.
|
|
||||||
*
|
|
||||||
* @param <T> The Number type the RangeSeekBar has been declared with.
|
|
||||||
* @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
|
|
||||||
*/
|
|
||||||
public interface OnRangeSeekBarChangeListener<T> {
|
|
||||||
|
|
||||||
void rangeSeekBarValuesChanged(T minValue, T maxValue, boolean changeComplete);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thumb constants (min and max).
|
|
||||||
*
|
|
||||||
* @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
|
|
||||||
*/
|
|
||||||
private static enum Thumb {
|
|
||||||
MIN, MAX
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ThumbContainer {
|
|
||||||
@NotNull
|
|
||||||
private final Bitmap thumbImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_normal);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Bitmap thumbPressedImage = BitmapFactory.decodeResource(getResources(), R.drawable.seek_thumb_pressed);
|
|
||||||
|
|
||||||
private final float thumbWidth = thumbImage.getWidth();
|
|
||||||
|
|
||||||
private final float thumbHalfWidth = 0.5f * thumbWidth;
|
|
||||||
|
|
||||||
private final float thumbHalfHeight = 0.5f * thumbImage.getHeight();
|
|
||||||
|
|
||||||
private final float lineHeight = 0.3f * thumbHalfHeight;
|
|
||||||
|
|
||||||
private final float padding = thumbHalfWidth;
|
|
||||||
|
|
||||||
public RectF getRect() {
|
|
||||||
return new RectF(padding, 0.5f * (getHeight() - lineHeight), getWidth() - padding, 0.5f * (getHeight() + lineHeight));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Bitmap getImage(boolean pressed) {
|
|
||||||
return pressed ? thumbPressedImage : thumbImage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.app.Activity;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/19/12
|
|
||||||
* Time: 4:23 PM
|
|
||||||
*/
|
|
||||||
public interface ActivityViewBuilder {
|
|
||||||
|
|
||||||
void buildView(@NotNull Activity activity);
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.drawable.Drawable;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 11/3/12
|
|
||||||
* Time: 10:49 PM
|
|
||||||
*/
|
|
||||||
public final class AndroidViewUtils {
|
|
||||||
|
|
||||||
private AndroidViewUtils() {
|
|
||||||
throw new AssertionError();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean drawDrawables(Canvas canvas, @NotNull TextView textView) {
|
|
||||||
final int compoundPaddingLeft = textView.getCompoundPaddingLeft();
|
|
||||||
final int compoundPaddingTop = textView.getCompoundPaddingTop();
|
|
||||||
final int compoundPaddingRight = textView.getCompoundPaddingRight();
|
|
||||||
final int compoundPaddingBottom = textView.getCompoundPaddingBottom();
|
|
||||||
|
|
||||||
final int scrollX = textView.getScrollX();
|
|
||||||
final int scrollY = textView.getScrollY();
|
|
||||||
|
|
||||||
final int right = textView.getRight();
|
|
||||||
final int left = textView.getLeft();
|
|
||||||
final int bottom = textView.getBottom();
|
|
||||||
final int top = textView.getTop();
|
|
||||||
|
|
||||||
final Drawable[] drawables = textView.getCompoundDrawables();
|
|
||||||
if (drawables != null) {
|
|
||||||
|
|
||||||
int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop;
|
|
||||||
int hspace = right - left - compoundPaddingRight - compoundPaddingLeft;
|
|
||||||
|
|
||||||
Drawable topDr = drawables[1];
|
|
||||||
// IMPORTANT: The coordinates computed are also used in invalidateDrawable()
|
|
||||||
// Make sure to update invalidateDrawable() when changing this code.
|
|
||||||
if (topDr != null) {
|
|
||||||
canvas.save();
|
|
||||||
canvas.translate(scrollX + compoundPaddingLeft + (hspace - topDr.getBounds().width()) / 2,
|
|
||||||
scrollY + textView.getPaddingTop() + vspace / 2);
|
|
||||||
topDr.draw(canvas);
|
|
||||||
canvas.restore();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void applyButtonDef(@NotNull Button button, @NotNull ButtonDef buttonDef) {
|
|
||||||
button.setText(buttonDef.getText());
|
|
||||||
|
|
||||||
final Integer drawableResId = buttonDef.getDrawableResId();
|
|
||||||
if ( drawableResId != null ) {
|
|
||||||
button.setPadding(0, 0, 0, 0);
|
|
||||||
|
|
||||||
final Drawable drawable = button.getContext().getResources().getDrawable(drawableResId);
|
|
||||||
button.setCompoundDrawablesWithIntrinsicBounds(null, drawable, null, null);
|
|
||||||
button.setCompoundDrawablePadding(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
applyViewDef(button, buttonDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void applyButtonDef(@NotNull ImageButton imageButton, @NotNull ButtonDef buttonDef) {
|
|
||||||
final Integer drawableResId = buttonDef.getDrawableResId();
|
|
||||||
if ( drawableResId != null ) {
|
|
||||||
imageButton.setImageDrawable(imageButton.getContext().getResources().getDrawable(drawableResId));
|
|
||||||
}
|
|
||||||
|
|
||||||
applyViewDef(imageButton, buttonDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void applyViewDef(@NotNull View view, @NotNull ViewDef viewDef) {
|
|
||||||
final Integer backgroundResId = viewDef.getBackgroundResId();
|
|
||||||
if (backgroundResId != null) {
|
|
||||||
view.setBackgroundResource(backgroundResId);
|
|
||||||
}
|
|
||||||
|
|
||||||
final String tag = viewDef.getTag();
|
|
||||||
if ( tag != null ) {
|
|
||||||
view.setTag(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,301 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.Layout.Alignment;
|
|
||||||
import android.text.StaticLayout;
|
|
||||||
import android.text.TextPaint;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Text view that auto adjusts text size to fit within the view.
|
|
||||||
* If the text size equals the minimum text size and still does not
|
|
||||||
* fit, append with an ellipsis.
|
|
||||||
*
|
|
||||||
* @author Chase Colburn
|
|
||||||
* @since Apr 4, 2011
|
|
||||||
*/
|
|
||||||
public class AutoResizeTextView extends TextView {
|
|
||||||
|
|
||||||
// Minimum text size for this text view
|
|
||||||
public static final float MIN_TEXT_SIZE = 20;
|
|
||||||
|
|
||||||
private float initialTextSize = 100;
|
|
||||||
|
|
||||||
// Interface for resize notifications
|
|
||||||
public interface OnTextResizeListener {
|
|
||||||
public void onTextResize(TextView textView, float oldSize, float newSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Off screen canvas for text size rendering
|
|
||||||
private static final Canvas textResizeCanvas = new Canvas();
|
|
||||||
|
|
||||||
// Our ellipse string
|
|
||||||
private static final String ellipsis = "...";
|
|
||||||
|
|
||||||
// Registered resize listener
|
|
||||||
private OnTextResizeListener textResizeListener;
|
|
||||||
|
|
||||||
// Flag for text and/or size changes to force a resize
|
|
||||||
private boolean needsResize = false;
|
|
||||||
|
|
||||||
// Lower bounds for text size
|
|
||||||
private float minTextSize = MIN_TEXT_SIZE;
|
|
||||||
|
|
||||||
// Text view line spacing multiplier
|
|
||||||
private float spacingMult = 1.0f;
|
|
||||||
|
|
||||||
// Text view additional line spacing
|
|
||||||
private float spacingAdd = 0.0f;
|
|
||||||
|
|
||||||
// Add ellipsis to text that overflows at the smallest text size
|
|
||||||
private boolean addEllipsis = true;
|
|
||||||
|
|
||||||
// Default constructor override
|
|
||||||
public AutoResizeTextView(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default constructor when inflating from XML file
|
|
||||||
public AutoResizeTextView(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Default constructor override
|
|
||||||
public AutoResizeTextView(Context context, AttributeSet attrs, int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When text changes, set the force resize flag to true and resetInterpreter the text size.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onTextChanged(final CharSequence text, final int start, final int before, final int after) {
|
|
||||||
needsResize = true;
|
|
||||||
// Since this view may be reused, it is good to resetInterpreter the text size
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If the text view size changed, set the force resize flag to true
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
|
||||||
if (w != oldw || h != oldh) {
|
|
||||||
needsResize = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Register listener to receive resize notifications
|
|
||||||
*
|
|
||||||
* @param listener
|
|
||||||
*/
|
|
||||||
public void setOnResizeListener(OnTextResizeListener listener) {
|
|
||||||
textResizeListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override the set text size to update our internal reference values
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setTextSize(int unit, float size) {
|
|
||||||
super.setTextSize(unit, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Override the set line spacing to update our internal reference values
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setLineSpacing(float add, float mult) {
|
|
||||||
super.setLineSpacing(add, mult);
|
|
||||||
spacingMult = mult;
|
|
||||||
spacingAdd = add;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the lower text size limit and invalidate the view
|
|
||||||
*
|
|
||||||
* @param minTextSize
|
|
||||||
*/
|
|
||||||
public void setMinTextSize(float minTextSize) {
|
|
||||||
this.minTextSize = minTextSize;
|
|
||||||
requestLayout();
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return lower text size limit
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public float getMinTextSize() {
|
|
||||||
return minTextSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set flag to add ellipsis to text that overflows at the smallest text size
|
|
||||||
*
|
|
||||||
* @param addEllipsis
|
|
||||||
*/
|
|
||||||
public void setAddEllipsis(boolean addEllipsis) {
|
|
||||||
this.addEllipsis = addEllipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return flag to add ellipsis to text that overflows at the smallest text size
|
|
||||||
*
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public boolean getAddEllipsis() {
|
|
||||||
return addEllipsis;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resize text after measuring
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
|
||||||
if (changed || needsResize) {
|
|
||||||
int widthLimit = (right - left) - getCompoundPaddingLeft() - getCompoundPaddingRight();
|
|
||||||
int heightLimit = (bottom - top) - getCompoundPaddingBottom() - getCompoundPaddingTop();
|
|
||||||
resizeText(widthLimit, heightLimit, getText());
|
|
||||||
}
|
|
||||||
super.onLayout(changed, left, top, right, bottom);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resize the text size with default width and height
|
|
||||||
*/
|
|
||||||
public void resizeText() {
|
|
||||||
resizeText(getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void resizeText(final CharSequence text) {
|
|
||||||
int heightLimit = getHeight() - getPaddingBottom() - getPaddingTop();
|
|
||||||
int widthLimit = getWidth() - getPaddingLeft() - getPaddingRight();
|
|
||||||
resizeText(widthLimit, heightLimit, text);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resize the text size with specified width and height
|
|
||||||
*
|
|
||||||
* @param width
|
|
||||||
* @param height
|
|
||||||
* @param text
|
|
||||||
*/
|
|
||||||
private void resizeText(int width, int height, CharSequence text) {
|
|
||||||
Log.d(this.getClass().getName(), "Resizing: w=" + width + ", h=" + height + ", text='" + text + "'");
|
|
||||||
|
|
||||||
// Do not resize if the view does not have dimensions or there is no text
|
|
||||||
if (text == null || text.length() == 0 || height <= 0 || width <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the text view's paint object
|
|
||||||
TextPaint textPaint = getPaint();
|
|
||||||
|
|
||||||
// Store the current text size
|
|
||||||
float oldTextSize = textPaint.getTextSize();
|
|
||||||
Log.d(this.getClass().getName(), "Old text size: " + oldTextSize);
|
|
||||||
|
|
||||||
// If there is a max text size set, use the lesser of that and the default text size
|
|
||||||
// todo serso: +2 is a workaround => to be checked boundary constraints
|
|
||||||
float newTextSize = initialTextSize + 2;
|
|
||||||
|
|
||||||
int newTextHeight;
|
|
||||||
|
|
||||||
if (text instanceof Editable) {
|
|
||||||
((Editable) text).append("|");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
// Get the required text height
|
|
||||||
newTextHeight = getTextRect(text, textPaint, width, newTextSize);
|
|
||||||
|
|
||||||
logDimensions(newTextSize, newTextHeight);
|
|
||||||
if (newTextHeight > height) {
|
|
||||||
// Until we either fit within our text view or we had reached our min text size, incrementally try smaller sizes
|
|
||||||
while (newTextHeight > height) {
|
|
||||||
if (newTextSize <= minTextSize) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
newTextSize = Math.max(newTextSize - 1, minTextSize);
|
|
||||||
newTextHeight = getTextRect(text, textPaint, width, newTextSize);
|
|
||||||
logDimensions(newTextSize, newTextHeight);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (newTextHeight < height) {
|
|
||||||
if (newTextSize <= minTextSize) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
newTextSize = Math.max(newTextSize + 1, minTextSize);
|
|
||||||
newTextHeight = getTextRect(text, textPaint, width, newTextSize);
|
|
||||||
logDimensions(newTextSize, newTextHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (text instanceof Editable) {
|
|
||||||
((Editable) text).delete(text.length() - 1, text.length());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initialTextSize = newTextSize;
|
|
||||||
|
|
||||||
// If we had reached our minimum text size and still don't fit, append an ellipsis
|
|
||||||
if (addEllipsis && newTextSize == minTextSize && newTextHeight > height) {
|
|
||||||
// Draw using a static layout
|
|
||||||
StaticLayout layout = new StaticLayout(text, textPaint, width, Alignment.ALIGN_NORMAL, spacingMult, spacingAdd, false);
|
|
||||||
layout.draw(textResizeCanvas);
|
|
||||||
int lastLine = layout.getLineForVertical(height) - 1;
|
|
||||||
int start = layout.getLineStart(lastLine);
|
|
||||||
int end = layout.getLineEnd(lastLine);
|
|
||||||
float lineWidth = layout.getLineWidth(lastLine);
|
|
||||||
float ellipseWidth = textPaint.measureText(ellipsis);
|
|
||||||
|
|
||||||
// Trim characters off until we have enough room to draw the ellipsis
|
|
||||||
while (width < lineWidth + ellipseWidth) {
|
|
||||||
lineWidth = textPaint.measureText(text.subSequence(start, --end + 1).toString());
|
|
||||||
}
|
|
||||||
setText(text.subSequence(0, end) + ellipsis);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some devices try to auto adjust line spacing, so force default line spacing
|
|
||||||
// and invalidate the layout as a side effect
|
|
||||||
textPaint.setTextSize(newTextSize);
|
|
||||||
setLineSpacing(spacingAdd, spacingMult);
|
|
||||||
|
|
||||||
// Notify the listener if registered
|
|
||||||
if (textResizeListener != null) {
|
|
||||||
textResizeListener.onTextResize(this, oldTextSize, newTextSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset force resize flag
|
|
||||||
needsResize = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logDimensions(float newTextSize, int newTextHeight) {
|
|
||||||
Log.d(this.getClass().getName(), "Nex text size: " + newTextSize + ", new text height: " + newTextHeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the text size of the text paint object and use a static layout to render text off screen before measuring
|
|
||||||
private int getTextRect(CharSequence source, TextPaint paint, int width, float textSize) {
|
|
||||||
// Update the text paint object
|
|
||||||
paint.setTextSize(textSize);
|
|
||||||
// Draw using a static layout
|
|
||||||
StaticLayout layout = new StaticLayout(source, paint, width, Alignment.ALIGN_NORMAL, spacingMult, spacingAdd, false);
|
|
||||||
layout.draw(textResizeCanvas);
|
|
||||||
return layout.getHeight();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 11/4/12
|
|
||||||
* Time: 11:52 PM
|
|
||||||
*/
|
|
||||||
public interface ButtonDef extends ViewDef {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Integer getDrawableResId();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
CharSequence getText();
|
|
||||||
}
|
|
@ -1,232 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.Paint.Style;
|
|
||||||
import android.os.Vibrator;
|
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.widget.Button;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.solovyev.android.FontSizeAdjuster;
|
|
||||||
import org.solovyev.common.math.Point2d;
|
|
||||||
import org.solovyev.common.text.StringUtils;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* NOTE: copied from com.android.calculator2.ColorButton
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Button with click-animation effect.
|
|
||||||
*/
|
|
||||||
public class ColorButton extends Button {
|
|
||||||
|
|
||||||
private int magicFlameColour;
|
|
||||||
private boolean drawMagicFlame = true;
|
|
||||||
|
|
||||||
private static final int CLICK_FEEDBACK_INTERVAL = 10;
|
|
||||||
private static final int CLICK_FEEDBACK_DURATION = 350;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Point2d textPosition;
|
|
||||||
private long animationStart;
|
|
||||||
private Paint feedbackPaint;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final OnClickListenerVibrator onClickListener;
|
|
||||||
|
|
||||||
private static final float H_TEXT_POSITION_DEFAULT_VALUE = 0.5f;
|
|
||||||
|
|
||||||
private float hTextPosition = H_TEXT_POSITION_DEFAULT_VALUE;
|
|
||||||
|
|
||||||
private boolean showText = true;
|
|
||||||
|
|
||||||
public ColorButton(Context context, AttributeSet attrs) {
|
|
||||||
this(context, attrs, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ColorButton(Context context, AttributeSet attrs, boolean init) {
|
|
||||||
super(context, attrs);
|
|
||||||
|
|
||||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DragButton);
|
|
||||||
final int N = a.getIndexCount();
|
|
||||||
for (int i = 0; i < N; i++) {
|
|
||||||
int attr = a.getIndex(i);
|
|
||||||
|
|
||||||
final String attrValue = a.getString(attr);
|
|
||||||
|
|
||||||
if (!StringUtils.isEmpty(attrValue)) {
|
|
||||||
switch (attr) {
|
|
||||||
case R.styleable.DragButton_hTextPosition:
|
|
||||||
this.hTextPosition = Float.valueOf(attrValue);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (init) {
|
|
||||||
init(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onClickListener = new OnClickListenerVibrator((Vibrator)context.getSystemService(Context.VIBRATOR_SERVICE), PreferenceManager.getDefaultSharedPreferences(context));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void init(Context context) {
|
|
||||||
final Resources resources = getResources();
|
|
||||||
|
|
||||||
magicFlameColour = resources.getColor(R.color.magic_flame);
|
|
||||||
feedbackPaint = new Paint();
|
|
||||||
feedbackPaint.setStyle(Style.STROKE);
|
|
||||||
feedbackPaint.setStrokeWidth(2);
|
|
||||||
|
|
||||||
// applying textColor property on the paint (now it is not possible to set paint color through the xml)
|
|
||||||
getPaint().setColor(getCurrentTextColor());
|
|
||||||
|
|
||||||
animationStart = -1;
|
|
||||||
|
|
||||||
if (context instanceof FontSizeAdjuster) {
|
|
||||||
((FontSizeAdjuster) context).adjustFontSize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSizeChanged(int w, int h, int oldW, int oldH) {
|
|
||||||
measureText();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void measureText() {
|
|
||||||
Paint paint = getPaint();
|
|
||||||
|
|
||||||
if (getText() != null) {
|
|
||||||
textPosition = getTextPosition(paint, getText());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Point2d getTextPosition(@NotNull Paint paint, @NotNull CharSequence text) {
|
|
||||||
final Point2d result = new Point2d();
|
|
||||||
|
|
||||||
result.setX(hTextPosition * getWidth() - 0.5f * paint.measureText(text.toString()));
|
|
||||||
|
|
||||||
float height = getHeight() - paint.ascent() - paint.descent();
|
|
||||||
|
|
||||||
result.setY(height / 2);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onTextChanged(CharSequence text, int start, int before, int after) {
|
|
||||||
measureText();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void drawMagicFlame(int duration, Canvas canvas) {
|
|
||||||
int alpha = 255 - 255 * duration / CLICK_FEEDBACK_DURATION;
|
|
||||||
int color = magicFlameColour | (alpha << 24);
|
|
||||||
|
|
||||||
feedbackPaint.setColor(color);
|
|
||||||
canvas.drawRect(1, 1, getWidth() - 1, getHeight() - 1, feedbackPaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDraw(Canvas canvas) {
|
|
||||||
if (drawMagicFlame) {
|
|
||||||
if (animationStart != -1) {
|
|
||||||
int animDuration = (int) (System.currentTimeMillis() - animationStart);
|
|
||||||
|
|
||||||
if (animDuration >= CLICK_FEEDBACK_DURATION) {
|
|
||||||
animationStart = -1;
|
|
||||||
} else {
|
|
||||||
drawMagicFlame(animDuration, canvas);
|
|
||||||
postInvalidateDelayed(CLICK_FEEDBACK_INTERVAL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CharSequence text = getText();
|
|
||||||
if ( !StringUtils.isEmpty(text) && textPosition != null) {
|
|
||||||
if (showText) {
|
|
||||||
canvas.drawText(text, 0, text.length(), textPosition.getX(), textPosition.getY(), getPaint());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
AndroidViewUtils.drawDrawables(canvas, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void animateClickFeedback() {
|
|
||||||
animationStart = System.currentTimeMillis();
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean performClick() {
|
|
||||||
vibrate();
|
|
||||||
return super.performClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean performLongClick() {
|
|
||||||
vibrate();
|
|
||||||
return super.performLongClick();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void vibrate() {
|
|
||||||
this.onClickListener.onClick(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
boolean result = super.onTouchEvent(event);
|
|
||||||
|
|
||||||
if (this.drawMagicFlame) {
|
|
||||||
switch (event.getAction()) {
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
animateClickFeedback();
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
case MotionEvent.ACTION_CANCEL:
|
|
||||||
invalidate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDrawMagicFlame(boolean drawMagicFlame) {
|
|
||||||
this.drawMagicFlame = drawMagicFlame;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShowText() {
|
|
||||||
return showText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShowText(boolean showText) {
|
|
||||||
this.showText = showText;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,234 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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 org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.*;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
|
|
||||||
public class ColorPickerDialog extends Dialog {
|
|
||||||
|
|
||||||
public interface OnColorChangedListener {
|
|
||||||
void colorChanged(int color);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OnColorChangedListener mListener;
|
|
||||||
private int mInitialColor;
|
|
||||||
|
|
||||||
private static class ColorPickerView extends View {
|
|
||||||
private Paint mPaint;
|
|
||||||
private Paint mCenterPaint;
|
|
||||||
private final int[] mColors;
|
|
||||||
private OnColorChangedListener mListener;
|
|
||||||
|
|
||||||
ColorPickerView(Context c, OnColorChangedListener l, int color) {
|
|
||||||
super(c);
|
|
||||||
mListener = l;
|
|
||||||
mColors = new int[]{
|
|
||||||
0xFFFF0000, 0xFFFF00FF, 0xFF0000FF, 0xFF00FFFF, 0xFF00FF00,
|
|
||||||
0xFFFFFF00, 0xFFFF0000
|
|
||||||
};
|
|
||||||
Shader s = new SweepGradient(0, 0, mColors, null);
|
|
||||||
|
|
||||||
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
mPaint.setShader(s);
|
|
||||||
mPaint.setStyle(Paint.Style.STROKE);
|
|
||||||
mPaint.setStrokeWidth(32);
|
|
||||||
|
|
||||||
mCenterPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
|
||||||
mCenterPaint.setColor(color);
|
|
||||||
mCenterPaint.setStrokeWidth(5);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean mTrackingCenter;
|
|
||||||
private boolean mHighlightCenter;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
float r = CENTER_X - mPaint.getStrokeWidth() * 0.5f;
|
|
||||||
|
|
||||||
canvas.translate(CENTER_X, CENTER_X);
|
|
||||||
|
|
||||||
canvas.drawOval(new RectF(-r, -r, r, r), mPaint);
|
|
||||||
canvas.drawCircle(0, 0, CENTER_RADIUS, mCenterPaint);
|
|
||||||
|
|
||||||
if (mTrackingCenter) {
|
|
||||||
int c = mCenterPaint.getColor();
|
|
||||||
mCenterPaint.setStyle(Paint.Style.STROKE);
|
|
||||||
|
|
||||||
if (mHighlightCenter) {
|
|
||||||
mCenterPaint.setAlpha(0xFF);
|
|
||||||
} else {
|
|
||||||
mCenterPaint.setAlpha(0x80);
|
|
||||||
}
|
|
||||||
canvas.drawCircle(0, 0,
|
|
||||||
CENTER_RADIUS + mCenterPaint.getStrokeWidth(),
|
|
||||||
mCenterPaint);
|
|
||||||
|
|
||||||
mCenterPaint.setStyle(Paint.Style.FILL);
|
|
||||||
mCenterPaint.setColor(c);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
setMeasuredDimension(CENTER_X * 2, CENTER_Y * 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final int CENTER_X = 100;
|
|
||||||
private static final int CENTER_Y = 100;
|
|
||||||
private static final int CENTER_RADIUS = 32;
|
|
||||||
|
|
||||||
private int floatToByte(float x) {
|
|
||||||
int n = java.lang.Math.round(x);
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int pinToByte(int n) {
|
|
||||||
if (n < 0) {
|
|
||||||
n = 0;
|
|
||||||
} else if (n > 255) {
|
|
||||||
n = 255;
|
|
||||||
}
|
|
||||||
return n;
|
|
||||||
}
|
|
||||||
|
|
||||||
private int ave(int s, int d, float p) {
|
|
||||||
return s + java.lang.Math.round(p * (d - s));
|
|
||||||
}
|
|
||||||
|
|
||||||
private int interpColor(int colors[], float unit) {
|
|
||||||
if (unit <= 0) {
|
|
||||||
return colors[0];
|
|
||||||
}
|
|
||||||
if (unit >= 1) {
|
|
||||||
return colors[colors.length - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
float p = unit * (colors.length - 1);
|
|
||||||
int i = (int) p;
|
|
||||||
p -= i;
|
|
||||||
|
|
||||||
// now p is just the fractional part [0...1) and i is the index
|
|
||||||
int c0 = colors[i];
|
|
||||||
int c1 = colors[i + 1];
|
|
||||||
int a = ave(Color.alpha(c0), Color.alpha(c1), p);
|
|
||||||
int r = ave(Color.red(c0), Color.red(c1), p);
|
|
||||||
int g = ave(Color.green(c0), Color.green(c1), p);
|
|
||||||
int b = ave(Color.blue(c0), Color.blue(c1), p);
|
|
||||||
|
|
||||||
return Color.argb(a, r, g, b);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int rotateColor(int color, float rad) {
|
|
||||||
float deg = rad * 180 / 3.1415927f;
|
|
||||||
int r = Color.red(color);
|
|
||||||
int g = Color.green(color);
|
|
||||||
int b = Color.blue(color);
|
|
||||||
|
|
||||||
ColorMatrix cm = new ColorMatrix();
|
|
||||||
ColorMatrix tmp = new ColorMatrix();
|
|
||||||
|
|
||||||
cm.setRGB2YUV();
|
|
||||||
tmp.setRotate(0, deg);
|
|
||||||
cm.postConcat(tmp);
|
|
||||||
tmp.setYUV2RGB();
|
|
||||||
cm.postConcat(tmp);
|
|
||||||
|
|
||||||
final float[] a = cm.getArray();
|
|
||||||
|
|
||||||
int ir = floatToByte(a[0] * r + a[1] * g + a[2] * b);
|
|
||||||
int ig = floatToByte(a[5] * r + a[6] * g + a[7] * b);
|
|
||||||
int ib = floatToByte(a[10] * r + a[11] * g + a[12] * b);
|
|
||||||
|
|
||||||
return Color.argb(Color.alpha(color), pinToByte(ir),
|
|
||||||
pinToByte(ig), pinToByte(ib));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final float PI = 3.1415926f;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
float x = event.getX() - CENTER_X;
|
|
||||||
float y = event.getY() - CENTER_Y;
|
|
||||||
boolean inCenter = java.lang.Math.sqrt(x * x + y * y) <= CENTER_RADIUS;
|
|
||||||
|
|
||||||
switch (event.getAction()) {
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
mTrackingCenter = inCenter;
|
|
||||||
if (inCenter) {
|
|
||||||
mHighlightCenter = true;
|
|
||||||
invalidate();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MotionEvent.ACTION_MOVE:
|
|
||||||
if (mTrackingCenter) {
|
|
||||||
if (mHighlightCenter != inCenter) {
|
|
||||||
mHighlightCenter = inCenter;
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
float angle = (float) java.lang.Math.atan2(y, x);
|
|
||||||
// need to turn angle [-PI ... PI] into unit [0....1]
|
|
||||||
float unit = angle / (2 * PI);
|
|
||||||
if (unit < 0) {
|
|
||||||
unit += 1;
|
|
||||||
}
|
|
||||||
mCenterPaint.setColor(interpColor(mColors, unit));
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
if (mTrackingCenter) {
|
|
||||||
if (inCenter) {
|
|
||||||
mListener.colorChanged(mCenterPaint.getColor());
|
|
||||||
}
|
|
||||||
mTrackingCenter = false; // so we draw w/o halo
|
|
||||||
invalidate();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ColorPickerDialog(Context context,
|
|
||||||
OnColorChangedListener listener,
|
|
||||||
int initialColor) {
|
|
||||||
super(context);
|
|
||||||
|
|
||||||
mListener = listener;
|
|
||||||
mInitialColor = initialColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
|
||||||
super.onCreate(savedInstanceState);
|
|
||||||
OnColorChangedListener l = new OnColorChangedListener() {
|
|
||||||
public void colorChanged(int color) {
|
|
||||||
mListener.colorChanged(color);
|
|
||||||
dismiss();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
setContentView(new ColorPickerView(getContext(), l, mInitialColor));
|
|
||||||
setTitle("Pick a Color");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,104 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.solovyev.android.ActivityDestroyerController;
|
|
||||||
import android.app.Activity;
|
|
||||||
import android.app.AlertDialog;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.DialogOnActivityDestroyedListener;
|
|
||||||
import org.solovyev.android.view.R;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/29/12
|
|
||||||
* Time: 1:25 PM
|
|
||||||
*/
|
|
||||||
public class ConfirmationDialogBuilder implements DialogBuilder<AlertDialog> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Context context;
|
|
||||||
|
|
||||||
private final int messageResId;
|
|
||||||
|
|
||||||
private int titleResId = R.string.c_confirmation;
|
|
||||||
|
|
||||||
private int positiveButtonTextResId = android.R.string.ok;
|
|
||||||
|
|
||||||
private int negativeButtonTextResId = android.R.string.cancel;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private DialogInterface.OnClickListener positiveHandler;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private DialogInterface.OnClickListener negativeHandler;
|
|
||||||
|
|
||||||
public ConfirmationDialogBuilder(@NotNull Context context, int messageResId) {
|
|
||||||
this.context = context;
|
|
||||||
this.messageResId = messageResId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ConfirmationDialogBuilder setTitleResId(int titleResId) {
|
|
||||||
this.titleResId = titleResId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ConfirmationDialogBuilder setPositiveButtonTextResId(int positiveButtonTextResId) {
|
|
||||||
this.positiveButtonTextResId = positiveButtonTextResId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ConfirmationDialogBuilder setNegativeButtonTextResId(int negativeButtonTextResId) {
|
|
||||||
this.negativeButtonTextResId = negativeButtonTextResId;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ConfirmationDialogBuilder setPositiveHandler(@Nullable DialogInterface.OnClickListener positiveHandler) {
|
|
||||||
this.positiveHandler = positiveHandler;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public ConfirmationDialogBuilder setNegativeHandler(@Nullable DialogInterface.OnClickListener negativeHandler) {
|
|
||||||
this.negativeHandler = negativeHandler;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public AlertDialog build() {
|
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
|
||||||
|
|
||||||
builder.setTitle(titleResId);
|
|
||||||
builder.setMessage(messageResId);
|
|
||||||
builder.setPositiveButton(positiveButtonTextResId, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
if ( positiveHandler != null ) {
|
|
||||||
positiveHandler.onClick(dialog, which);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.setNegativeButton(negativeButtonTextResId, new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
|
||||||
if ( negativeHandler != null ) {
|
|
||||||
negativeHandler.onClick(dialog, which);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
final AlertDialog result = builder.create();
|
|
||||||
if ( context instanceof Activity) {
|
|
||||||
ActivityDestroyerController.getInstance().put((Activity)context, new DialogOnActivityDestroyedListener(result));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/20/12
|
|
||||||
* Time: 12:01 AM
|
|
||||||
*/
|
|
||||||
public interface DataActivityViewBuilder<D> extends ActivityViewBuilder {
|
|
||||||
|
|
||||||
void setData(@NotNull D data);
|
|
||||||
}
|
|
@ -1,12 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import org.solovyev.common.Builder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/29/12
|
|
||||||
* Time: 1:24 PM
|
|
||||||
*/
|
|
||||||
public interface DialogBuilder<D extends DialogInterface> extends Builder<D> {
|
|
||||||
}
|
|
@ -1,51 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.common.text.Formatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/10/12
|
|
||||||
* Time: 12:51 AM
|
|
||||||
*/
|
|
||||||
public class DoubleRange extends NumberRange<Double> {
|
|
||||||
|
|
||||||
public DoubleRange(@NotNull Double min,
|
|
||||||
@NotNull Double max,
|
|
||||||
@NotNull Double step,
|
|
||||||
int startPosition,
|
|
||||||
@Nullable Formatter<Double> formatter) {
|
|
||||||
super(min, max, step, startPosition, formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static NumberRange<Double> newInstance(@NotNull Double min,
|
|
||||||
@NotNull Double max,
|
|
||||||
@NotNull Double step,
|
|
||||||
@NotNull Double selected,
|
|
||||||
@Nullable Formatter<Double> formatter) {
|
|
||||||
if (selected < min || selected > max) {
|
|
||||||
throw new IllegalArgumentException("Selected value: " + selected + " should be >= " + min + " and <= " + max + "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
int startPosition = 0;
|
|
||||||
for ( double i = min; i < selected; i += step ) {
|
|
||||||
startPosition += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new DoubleRange(min, max, step, startPosition, formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getCount(@NotNull Double min, @NotNull Double max, @NotNull Double step) {
|
|
||||||
int result = (int) ((max - min) / step);
|
|
||||||
return result + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Double getValueAt(int position, @NotNull Double min, @NotNull Double max, @NotNull Double step) {
|
|
||||||
return min + position * step;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/19/12
|
|
||||||
* Time: 12:50 AM
|
|
||||||
*/
|
|
||||||
public interface Grayable {
|
|
||||||
|
|
||||||
void grayOut();
|
|
||||||
|
|
||||||
void grayIn();
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.graphics.RectF;
|
|
||||||
import android.view.View;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/19/12
|
|
||||||
* Time: 12:51 AM
|
|
||||||
*/
|
|
||||||
public class GrayableImpl implements ViewGrayable {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Paint paint;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void grayOut() {
|
|
||||||
paint = new Paint();
|
|
||||||
paint.setARGB(180, 75, 75, 75);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void grayIn() {
|
|
||||||
paint = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void dispatchDraw(@NotNull View view, @NotNull Canvas canvas) {
|
|
||||||
final Paint localPaint = paint;
|
|
||||||
if (localPaint != null) {
|
|
||||||
final RectF drawRect = new RectF();
|
|
||||||
drawRect.set(0, 0, view.getMeasuredWidth(), view.getMeasuredHeight());
|
|
||||||
|
|
||||||
canvas.drawRoundRect(drawRect, 5, 5, localPaint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.widget.RelativeLayout;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/19/12
|
|
||||||
* Time: 12:34 AM
|
|
||||||
*/
|
|
||||||
public class GrayableRelativeLayout extends RelativeLayout implements Grayable {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private ViewGrayable grayable = new GrayableImpl();
|
|
||||||
|
|
||||||
public GrayableRelativeLayout(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public GrayableRelativeLayout(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void grayOut() {
|
|
||||||
grayable.grayOut();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void grayIn() {
|
|
||||||
grayable.grayIn();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void dispatchDraw(Canvas canvas) {
|
|
||||||
super.dispatchDraw(canvas);
|
|
||||||
|
|
||||||
this.grayable.dispatchDraw(this, canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/19/12
|
|
||||||
* Time: 12:53 AM
|
|
||||||
*/
|
|
||||||
public interface GrayableView extends Grayable {
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.common.text.Formatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/10/12
|
|
||||||
* Time: 12:08 AM
|
|
||||||
*/
|
|
||||||
public class IntegerRange extends NumberRange<Integer> {
|
|
||||||
|
|
||||||
public IntegerRange(@NotNull Integer min,
|
|
||||||
@NotNull Integer max,
|
|
||||||
@NotNull Integer step,
|
|
||||||
int startPosition,
|
|
||||||
@Nullable Formatter<Integer> formatter) {
|
|
||||||
super(min, max, step, startPosition, formatter);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static NumberRange<Integer> newInstance(@NotNull Integer min, @NotNull Integer max, @NotNull Integer step, @NotNull Integer selected) {
|
|
||||||
if (selected < min || selected > max) {
|
|
||||||
throw new IllegalArgumentException("Selected value: " + selected + " should be >= " + min + " and <= " + max + "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
int startPosition = 0;
|
|
||||||
for ( int i = min; i < selected; i += step ) {
|
|
||||||
startPosition += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new IntegerRange(min, max, step, startPosition, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected int getCount(@NotNull Integer min, @NotNull Integer max, @NotNull Integer step) {
|
|
||||||
// (4 - 0)/1 + 1= 5
|
|
||||||
// (4 - 0)/2 + 1 = 3
|
|
||||||
// (4 - 1)/2 + 1 = 2
|
|
||||||
return (max - min) / step + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Integer getValueAt(int position, @NotNull Integer min, @NotNull Integer max, @NotNull Integer step) {
|
|
||||||
return min + position * step;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.common.text.Formatter;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/10/12
|
|
||||||
* Time: 1:55 AM
|
|
||||||
*/
|
|
||||||
public class ListRange<T> implements Picker.Range<T> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private List<T> elements;
|
|
||||||
|
|
||||||
private int startPosition;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Formatter<T> formatter;
|
|
||||||
|
|
||||||
public ListRange(@NotNull List<T> elements, @Nullable T selected) {
|
|
||||||
this.elements = elements;
|
|
||||||
this.startPosition = elements.indexOf(selected);
|
|
||||||
if ( this.startPosition < 0 ) {
|
|
||||||
this.startPosition = 0;
|
|
||||||
}
|
|
||||||
this.formatter = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ListRange(@NotNull List<T> elements, @Nullable T selected, @Nullable Formatter<T> formatter) {
|
|
||||||
this(elements, selected);
|
|
||||||
this.formatter = formatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getStartPosition() {
|
|
||||||
return this.startPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
return this.elements.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getStringValueAt(int position) {
|
|
||||||
final T value = getValueAt(position);
|
|
||||||
return formatter == null ? value.toString() : formatter.formatValue(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public T getValueAt(int position) {
|
|
||||||
return this.elements.get(position);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.widget.ListAdapter;
|
|
||||||
import android.widget.ListView;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/26/12
|
|
||||||
* Time: 1:05 PM
|
|
||||||
*/
|
|
||||||
public class ListViewBuilder implements ViewBuilder<ListView> {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Integer layoutId;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private ListAdapter listAdapter;
|
|
||||||
|
|
||||||
private ListViewBuilder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static ViewBuilder<ListView> newInstance(@NotNull ListAdapter listAdapter) {
|
|
||||||
final ListViewBuilder result = new ListViewBuilder();
|
|
||||||
|
|
||||||
result.layoutId = null;
|
|
||||||
result.listAdapter = listAdapter;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static ViewBuilder<ListView> newInstance(int layoutId, @NotNull ListAdapter listAdapter) {
|
|
||||||
final ListViewBuilder result = new ListViewBuilder();
|
|
||||||
|
|
||||||
result.layoutId = layoutId;
|
|
||||||
result.listAdapter = listAdapter;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public ListView build(@NotNull Context context) {
|
|
||||||
final ListView result;
|
|
||||||
if (layoutId != null) {
|
|
||||||
result = ViewFromLayoutBuilder.<ListView>newInstance(layoutId).build(context);
|
|
||||||
} else {
|
|
||||||
result = new ListView(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
result.setAdapter(listAdapter);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,83 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.common.text.Formatter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 8/9/12
|
|
||||||
* Time: 11:44 PM
|
|
||||||
*/
|
|
||||||
public abstract class NumberRange<N extends Number & Comparable<N>> implements Picker.Range<N> {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Formatter<N> formatter;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final N min;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final N max;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final N step;
|
|
||||||
|
|
||||||
private final int startPosition;
|
|
||||||
|
|
||||||
private int count = -1;
|
|
||||||
|
|
||||||
public NumberRange(@NotNull N min,
|
|
||||||
@NotNull N max,
|
|
||||||
@NotNull N step,
|
|
||||||
int startPosition,
|
|
||||||
@Nullable Formatter<N> formatter) {
|
|
||||||
assert min.compareTo(max) <= 0;
|
|
||||||
|
|
||||||
this.min = min;
|
|
||||||
this.max = max;
|
|
||||||
this.step = step;
|
|
||||||
this.startPosition = startPosition;
|
|
||||||
this.formatter = formatter;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getStartPosition() {
|
|
||||||
if ( this.startPosition < getCount() ) {
|
|
||||||
return this.startPosition;
|
|
||||||
} else {
|
|
||||||
return getCount() - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
if (count == -1) {
|
|
||||||
count = getCount(min, max, step);
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract int getCount(@NotNull N min, @NotNull N max, @NotNull N step);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public String getStringValueAt(int position) {
|
|
||||||
int count = getCount();
|
|
||||||
if (position < 0 || position >= count) {
|
|
||||||
throw new IllegalArgumentException("Position " + position + " must be >= 0 and < " + count + "!");
|
|
||||||
}
|
|
||||||
|
|
||||||
final N number = getValueAt(position, min, max, step);
|
|
||||||
return formatter == null ? number.toString() : formatter.formatValue(number);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public N getValueAt(int position) {
|
|
||||||
return getValueAt(position, min, max, step);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
protected abstract N getValueAt(int position, @NotNull N min, @NotNull N max, @NotNull N step);
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.common.Converter;
|
|
||||||
import org.solovyev.common.interval.Interval;
|
|
||||||
import org.solovyev.common.math.NumberValuer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/19/11
|
|
||||||
* Time: 4:26 PM
|
|
||||||
*/
|
|
||||||
public class NumberRangeSeekBar<T extends Number & Comparable<T>> extends AbstractRangeSeekBar<T> {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final NumberType numberType;
|
|
||||||
|
|
||||||
|
|
||||||
public NumberRangeSeekBar(@NotNull Interval<T> boundaries, @Nullable Integer steps, Context context) throws IllegalArgumentException {
|
|
||||||
this(boundaries.getLeftLimit(), boundaries.getRightLimit(), steps, context);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new RangeSeekBar.
|
|
||||||
*
|
|
||||||
* @param minValue The minimum value of the selectable range.
|
|
||||||
* @param maxValue The maximum value of the selectable range.
|
|
||||||
* @param steps number of steps of range
|
|
||||||
* @param context parent context
|
|
||||||
* @throws IllegalArgumentException Will be thrown if min/max value types are not one of Long, Double, Integer, Float, Short, Byte or BigDecimal.
|
|
||||||
*/
|
|
||||||
public NumberRangeSeekBar(@NotNull T minValue, @NotNull T maxValue, @Nullable Integer steps, Context context) throws IllegalArgumentException {
|
|
||||||
super(minValue, maxValue, steps, context);
|
|
||||||
|
|
||||||
numberType = NumberType.fromNumber(minValue);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Converter<Double, T> getToTConverter() {
|
|
||||||
return new Converter<Double, T>() {
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public T convert(@NotNull Double value) {
|
|
||||||
return (T) numberType.toNumber(value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
protected Converter<T, Double> getToDoubleConverter() {
|
|
||||||
return new NumberValuer<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,68 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility enumeration used to convert between Numbers and doubles.
|
|
||||||
*
|
|
||||||
* @author Stephan Tittel (stephan.tittel@kom.tu-darmstadt.de)
|
|
||||||
*/
|
|
||||||
enum NumberType {
|
|
||||||
|
|
||||||
LONG(Long.class),
|
|
||||||
DOUBLE(Double.class),
|
|
||||||
INTEGER(Integer.class),
|
|
||||||
FLOAT(Float.class),
|
|
||||||
SHORT(Short.class),
|
|
||||||
BYTE(Byte.class),
|
|
||||||
BIG_DECIMAL(BigDecimal.class);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Class underlyingClass;
|
|
||||||
|
|
||||||
NumberType(@NotNull Class underlyingClass) {
|
|
||||||
this.underlyingClass = underlyingClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static <E extends Number> NumberType fromNumber(E value) throws IllegalArgumentException {
|
|
||||||
|
|
||||||
for (NumberType numberType : NumberType.values()) {
|
|
||||||
if (numberType.underlyingClass.isInstance(value)) {
|
|
||||||
return numberType;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException("Number class '" + value.getClass().getName() + "' is not supported");
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Number> T toNumber(double value) {
|
|
||||||
|
|
||||||
switch (this) {
|
|
||||||
case LONG:
|
|
||||||
return (T)new Long((long) value);
|
|
||||||
case DOUBLE:
|
|
||||||
return (T)new Double(value);
|
|
||||||
case INTEGER:
|
|
||||||
return (T)new Integer((int) value);
|
|
||||||
case FLOAT:
|
|
||||||
return (T)new Float((float) value);
|
|
||||||
case SHORT:
|
|
||||||
return (T)new Short((short) value);
|
|
||||||
case BYTE:
|
|
||||||
return (T)new Byte((byte) value);
|
|
||||||
case BIG_DECIMAL:
|
|
||||||
return (T)new BigDecimal(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new InstantiationError("can't convert " + this + " to a Number object");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Vibrator;
|
|
||||||
import android.view.View;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 10/26/11
|
|
||||||
* Time: 11:25 PM
|
|
||||||
*/
|
|
||||||
public class OnClickListenerVibrator implements View.OnClickListener {
|
|
||||||
|
|
||||||
private static final float VIBRATION_TIME_SCALE = 1.0f;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private VibratorContainer vibrator;
|
|
||||||
|
|
||||||
public OnClickListenerVibrator(@Nullable Vibrator vibrator,
|
|
||||||
@NotNull SharedPreferences preferences) {
|
|
||||||
this.vibrator = new VibratorContainer(vibrator, preferences, VIBRATION_TIME_SCALE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
vibrator.vibrate();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,283 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/18/11
|
|
||||||
* Time: 10:03 PM
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2008 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.LinearLayout;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A view for selecting a number
|
|
||||||
* <p/>
|
|
||||||
* For a dialog using this view, see {@link android.app.TimePickerDialog}.
|
|
||||||
*/
|
|
||||||
public class Picker<T> extends LinearLayout {
|
|
||||||
|
|
||||||
public static interface OnChangedListener<T> {
|
|
||||||
void onChanged(@NotNull Picker picker, @NotNull T value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static interface Range<T> {
|
|
||||||
|
|
||||||
int getStartPosition();
|
|
||||||
|
|
||||||
int getCount();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
String getStringValueAt(int position);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
T getValueAt(int position);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Handler uiHandler = new Handler();
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Runnable runnable = new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
if (increment) {
|
|
||||||
changeCurrent(current + 1);
|
|
||||||
uiHandler.postDelayed(this, speed);
|
|
||||||
} else if (decrement) {
|
|
||||||
changeCurrent(current - 1);
|
|
||||||
uiHandler.postDelayed(this, speed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final TextView text;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Range<T> range;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Current value of this NumberPicker
|
|
||||||
*/
|
|
||||||
private int current;
|
|
||||||
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private OnChangedListener<T> onChangedListener;
|
|
||||||
|
|
||||||
private long speed = 300;
|
|
||||||
|
|
||||||
private boolean increment;
|
|
||||||
|
|
||||||
private boolean decrement;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final PickerButton incrementButton;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final PickerButton decrementButton;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new number picker
|
|
||||||
*
|
|
||||||
* @param context the application environment
|
|
||||||
*/
|
|
||||||
public Picker(Context context) {
|
|
||||||
this(context, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new number picker
|
|
||||||
*
|
|
||||||
* @param context the application environment
|
|
||||||
* @param attrs a collection of attributes
|
|
||||||
*/
|
|
||||||
public Picker(Context context, @Nullable AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
|
|
||||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Picker);
|
|
||||||
|
|
||||||
final int orientation = a.getInt(R.styleable.Picker_orientation, VERTICAL);
|
|
||||||
|
|
||||||
setOrientation(orientation);
|
|
||||||
|
|
||||||
// INFLATING LAYOUT
|
|
||||||
final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
if (orientation == HORIZONTAL) {
|
|
||||||
inflater.inflate(R.layout.number_picker_horizontal, this, true);
|
|
||||||
} else {
|
|
||||||
inflater.inflate(R.layout.number_picker, this, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
final OnClickListener clickListener = new OnClickListener() {
|
|
||||||
public void onClick(View v) {
|
|
||||||
// now perform the increment/decrement
|
|
||||||
if (R.id.increment == v.getId()) {
|
|
||||||
changeCurrent(current + 1);
|
|
||||||
} else if (R.id.decrement == v.getId()) {
|
|
||||||
changeCurrent(current - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final OnLongClickListener longClickListener = new OnLongClickListener() {
|
|
||||||
/**
|
|
||||||
* We start the long click here but rely on the {@link PickerButton}
|
|
||||||
* to inform us when the long click has ended.
|
|
||||||
*/
|
|
||||||
public boolean onLongClick(View v) {
|
|
||||||
if (R.id.increment == v.getId()) {
|
|
||||||
increment = true;
|
|
||||||
uiHandler.post(runnable);
|
|
||||||
} else if (R.id.decrement == v.getId()) {
|
|
||||||
decrement = true;
|
|
||||||
uiHandler.post(runnable);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
incrementButton = (PickerButton) this.findViewById(R.id.increment);
|
|
||||||
incrementButton.setNumberPicker(this);
|
|
||||||
incrementButton.setOnClickListener(clickListener);
|
|
||||||
incrementButton.setOnLongClickListener(longClickListener);
|
|
||||||
|
|
||||||
decrementButton = (PickerButton) this.findViewById(R.id.decrement);
|
|
||||||
decrementButton.setNumberPicker(this);
|
|
||||||
decrementButton.setOnClickListener(clickListener);
|
|
||||||
decrementButton.setOnLongClickListener(longClickListener);
|
|
||||||
|
|
||||||
text = (TextView) this.findViewById(R.id.timepicker_input);
|
|
||||||
|
|
||||||
if (!isEnabled()) {
|
|
||||||
setEnabled(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the enabled state of this view. The interpretation of the enabled
|
|
||||||
* state varies by subclass.
|
|
||||||
*
|
|
||||||
* @param enabled True if this view is enabled, false otherwise.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setEnabled(boolean enabled) {
|
|
||||||
super.setEnabled(enabled);
|
|
||||||
incrementButton.setEnabled(enabled);
|
|
||||||
decrementButton.setEnabled(enabled);
|
|
||||||
text.setEnabled(enabled);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the callback that indicates the number has been adjusted by the user.
|
|
||||||
*
|
|
||||||
* @param listener the callback, should not be null.
|
|
||||||
*/
|
|
||||||
public void setOnChangeListener(OnChangedListener<T> listener) {
|
|
||||||
this.onChangedListener = listener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setRange(@NotNull Range<T> range) {
|
|
||||||
this.range = range;
|
|
||||||
this.current = range.getStartPosition();
|
|
||||||
|
|
||||||
updateView();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setCurrent(int current) {
|
|
||||||
if (current < 0 || current >= range.getCount()) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"Current: " + current + " should be >= 0 and < " + range.getCount());
|
|
||||||
}
|
|
||||||
this.current = current;
|
|
||||||
updateView();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the speed at which the numbers will scroll when the +/-
|
|
||||||
* buttons are longpressed
|
|
||||||
*
|
|
||||||
* @param speed The speed (in milliseconds) at which the numbers will scroll
|
|
||||||
* default 300ms
|
|
||||||
*/
|
|
||||||
public void setSpeed(long speed) {
|
|
||||||
this.speed = speed;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void changeCurrent(int current) {
|
|
||||||
|
|
||||||
// Wrap around the values if we go past the start or end
|
|
||||||
if (current < 0) {
|
|
||||||
current = this.range.getCount() - 1;
|
|
||||||
} else if (current >= this.range.getCount()) {
|
|
||||||
current = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.current = current;
|
|
||||||
|
|
||||||
notifyChange();
|
|
||||||
updateView();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void notifyChange() {
|
|
||||||
if (onChangedListener != null) {
|
|
||||||
onChangedListener.onChanged(this, range.getValueAt(current));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateView() {
|
|
||||||
text.setText(range.getStringValueAt(current));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public void cancelIncrement() {
|
|
||||||
increment = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public void cancelDecrement() {
|
|
||||||
decrement = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return current position in Picker
|
|
||||||
*/
|
|
||||||
public int getCurrent() {
|
|
||||||
return current;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,105 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 9/18/11
|
|
||||||
* Time: 10:04 PM
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2008 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.view.KeyEvent;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.widget.ImageButton;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class exists purely to cancel long click events, that got
|
|
||||||
* started in NumberPicker
|
|
||||||
*/
|
|
||||||
public class PickerButton extends ImageButton {
|
|
||||||
|
|
||||||
private Picker numberPicker;
|
|
||||||
|
|
||||||
public PickerButton(Context context, AttributeSet attrs,
|
|
||||||
int defStyle) {
|
|
||||||
super(context, attrs, defStyle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PickerButton(Context context, AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public PickerButton(Context context) {
|
|
||||||
super(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setNumberPicker(Picker picker) {
|
|
||||||
numberPicker = picker;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouchEvent(MotionEvent event) {
|
|
||||||
cancelLongPressIfRequired(event);
|
|
||||||
return super.onTouchEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTrackballEvent(MotionEvent event) {
|
|
||||||
cancelLongPressIfRequired(event);
|
|
||||||
return super.onTrackballEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
|
||||||
if ((keyCode == KeyEvent.KEYCODE_DPAD_CENTER)
|
|
||||||
|| (keyCode == KeyEvent.KEYCODE_ENTER)) {
|
|
||||||
cancelLongpress();
|
|
||||||
}
|
|
||||||
return super.onKeyUp(keyCode, event);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cancelLongPressIfRequired(MotionEvent event) {
|
|
||||||
if ((event.getAction() == MotionEvent.ACTION_CANCEL)
|
|
||||||
|| (event.getAction() == MotionEvent.ACTION_UP)) {
|
|
||||||
cancelLongpress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void cancelLongpress() {
|
|
||||||
if (R.id.increment == getId()) {
|
|
||||||
numberPicker.cancelIncrement();
|
|
||||||
} else if (R.id.decrement == getId()) {
|
|
||||||
numberPicker.cancelDecrement();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onWindowFocusChanged(boolean hasWindowFocus) {
|
|
||||||
super.onWindowFocusChanged(hasWindowFocus);
|
|
||||||
if (!hasWindowFocus) {
|
|
||||||
cancelLongpress();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.TextView;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/19/12
|
|
||||||
* Time: 11:50 PM
|
|
||||||
*/
|
|
||||||
public class TextViewBuilder implements UpdatableViewBuilder<TextView> {
|
|
||||||
|
|
||||||
private int textViewLayoutId;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String tag;
|
|
||||||
|
|
||||||
private TextViewBuilder() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static UpdatableViewBuilder<TextView> newInstance(int textViewLayoutId, @Nullable String tag) {
|
|
||||||
final TextViewBuilder result = new TextViewBuilder();
|
|
||||||
|
|
||||||
result.textViewLayoutId = textViewLayoutId;
|
|
||||||
result.tag = tag;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public TextView build(@NotNull Context context) {
|
|
||||||
final TextView result = ViewFromLayoutBuilder.<TextView>newInstance(textViewLayoutId).build(context);
|
|
||||||
|
|
||||||
result.setTag(createViewTag());
|
|
||||||
|
|
||||||
return updateView(context, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private String createViewTag() {
|
|
||||||
return tag == null ? this.getClass().getName() : tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public TextView updateView(@NotNull Context context, @NotNull View view) {
|
|
||||||
if (createViewTag().equals(view.getTag())) {
|
|
||||||
return (TextView) view;
|
|
||||||
} else {
|
|
||||||
return build(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
* or visit http://se.solovyev.org
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.os.Vibrator;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.prefs.BooleanPreference;
|
|
||||||
import org.solovyev.android.prefs.NumberToStringPreference;
|
|
||||||
import org.solovyev.android.prefs.Preference;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 10/26/11
|
|
||||||
* Time: 11:40 PM
|
|
||||||
*/
|
|
||||||
public class VibratorContainer implements SharedPreferences.OnSharedPreferenceChangeListener {
|
|
||||||
|
|
||||||
public static class Preferences {
|
|
||||||
public static final Preference<Boolean> hapticFeedbackEnabled = new BooleanPreference("org.solovyev.android.calculator.CalculatorModel_haptic_feedback", false);
|
|
||||||
public static final Preference<Long> hapticFeedbackDuration = new NumberToStringPreference<Long>("org.solovyev.android.calculator.CalculatorActivity_calc_haptic_feedback_duration_key", 60L, Long.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
private final float vibrationTimeScale;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private final Vibrator vibrator;
|
|
||||||
|
|
||||||
private long time = 0;
|
|
||||||
|
|
||||||
public VibratorContainer(@Nullable Vibrator vibrator, @NotNull SharedPreferences preferences, float vibrationTimeScale) {
|
|
||||||
this.vibrator = vibrator;
|
|
||||||
this.vibrationTimeScale = vibrationTimeScale;
|
|
||||||
|
|
||||||
preferences.registerOnSharedPreferenceChangeListener(this);
|
|
||||||
onSharedPreferenceChanged(preferences, null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void vibrate() {
|
|
||||||
if (time > 0 && vibrator != null) {
|
|
||||||
vibrator.vibrate(time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences preferences, @Nullable String key) {
|
|
||||||
if ( Preferences.hapticFeedbackEnabled.getPreference(preferences)) {
|
|
||||||
//noinspection ConstantConditions
|
|
||||||
this.time = getScaledValue(Preferences.hapticFeedbackDuration.getPreference(preferences));
|
|
||||||
} else {
|
|
||||||
this.time = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private long getScaledValue(long vibrationTime) {
|
|
||||||
return (long) (vibrationTime * vibrationTimeScale);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 11/4/12
|
|
||||||
* Time: 11:59 PM
|
|
||||||
*/
|
|
||||||
public interface ViewDef {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Float getLayoutWeight();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Integer getLayoutMarginLeft();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Integer getLayoutMarginRight();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
String getTag();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
Integer getBackgroundResId();
|
|
||||||
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.view.ViewGroup;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/18/12
|
|
||||||
* Time: 12:57 AM
|
|
||||||
*/
|
|
||||||
public class ViewFromLayoutBuilder<V extends View> implements ViewBuilder<V> {
|
|
||||||
|
|
||||||
private final int layoutId;
|
|
||||||
|
|
||||||
private final int viewId;
|
|
||||||
|
|
||||||
private final boolean wholeLayout;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private LayoutInflater layoutInflater;
|
|
||||||
|
|
||||||
private ViewFromLayoutBuilder(int layoutId, int viewId, boolean wholeLayout) {
|
|
||||||
this.layoutId = layoutId;
|
|
||||||
this.viewId = viewId;
|
|
||||||
this.wholeLayout = wholeLayout;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static <V extends View> ViewFromLayoutBuilder<V> newInstance(int layoutId, int viewId) {
|
|
||||||
return new ViewFromLayoutBuilder<V>(layoutId, viewId, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static <V extends View> ViewFromLayoutBuilder<V> newInstance(int layoutId) {
|
|
||||||
return new ViewFromLayoutBuilder<V>(layoutId, 0, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLayoutInflater(@Nullable LayoutInflater layoutInflater) {
|
|
||||||
this.layoutInflater = layoutInflater;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public V build(@NotNull Context context) {
|
|
||||||
|
|
||||||
LayoutInflater li = layoutInflater;
|
|
||||||
if (li == null) {
|
|
||||||
li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wholeLayout) {
|
|
||||||
// if whole layout - just return view
|
|
||||||
return (V)li.inflate(layoutId, null);
|
|
||||||
} else {
|
|
||||||
// else try to find view by id
|
|
||||||
final ViewGroup itemView = (ViewGroup) li.inflate(layoutId, null);
|
|
||||||
return (V)itemView.findViewById(viewId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package org.solovyev.android.view;
|
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.view.View;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 4/19/12
|
|
||||||
* Time: 12:55 AM
|
|
||||||
*/
|
|
||||||
public interface ViewGrayable extends Grayable {
|
|
||||||
|
|
||||||
void dispatchDraw(@NotNull View view, @NotNull Canvas canvas);
|
|
||||||
}
|
|
@ -1,462 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view.drag;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.content.res.TypedArray;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.Paint;
|
|
||||||
import android.text.TextPaint;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.view.R;
|
|
||||||
import org.solovyev.common.math.Point2d;
|
|
||||||
import org.solovyev.common.text.CollectionTransformations;
|
|
||||||
import org.solovyev.common.text.NumberParser;
|
|
||||||
import org.solovyev.common.text.StringUtils;
|
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 7/17/11
|
|
||||||
* Time: 10:25 PM
|
|
||||||
*/
|
|
||||||
public class DirectionDragButton extends DragButton {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final static Float DEFAULT_DIRECTION_TEXT_SCALE_FLOAT = 0.33f;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final static Integer DEFAULT_DIRECTION_TEXT_ALPHA = 140;
|
|
||||||
|
|
||||||
private final static int DEFAULT_DIRECTION_TEXT_COLOR = Color.WHITE;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final static String DEFAULT_DIRECTION_TEXT_SCALE = "0.33;0.33;0.33;0.33";
|
|
||||||
|
|
||||||
protected static class DirectionTextData {
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final GuiDragDirection guiDragDirection;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private String text;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Point2d position;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private TextPaint paint;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Float textScale = 0.5f;
|
|
||||||
|
|
||||||
private boolean showText = true;
|
|
||||||
|
|
||||||
private DirectionTextData(@NotNull GuiDragDirection guiDragDirection, @NotNull String text) {
|
|
||||||
this.guiDragDirection = guiDragDirection;
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public GuiDragDirection getGuiDragDirection() {
|
|
||||||
return guiDragDirection;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getText() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setText(@NotNull String text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Point2d getPosition() {
|
|
||||||
return position;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPosition(@NotNull Point2d position) {
|
|
||||||
this.position = position;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public TextPaint getPaint() {
|
|
||||||
return paint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPaint(@NotNull TextPaint paint) {
|
|
||||||
this.paint = paint;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Float getTextScale() {
|
|
||||||
return textScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTextScale(@NotNull Float textScale) {
|
|
||||||
this.textScale = textScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isShowText() {
|
|
||||||
return showText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShowText(boolean showText) {
|
|
||||||
this.showText = showText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected static enum GuiDragDirection {
|
|
||||||
up(DragDirection.up, 0) {
|
|
||||||
@Override
|
|
||||||
public int getAttributeId() {
|
|
||||||
return R.styleable.DirectionDragButton_textUp;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
|
|
||||||
return getUpDownTextPosition(paint, basePaint, text, baseText, 1, w, h);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
down(DragDirection.down, 2) {
|
|
||||||
@Override
|
|
||||||
public int getAttributeId() {
|
|
||||||
return R.styleable.DirectionDragButton_textDown;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
|
|
||||||
return getUpDownTextPosition(paint, basePaint, text, baseText, -1, w, h);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
left(DragDirection.left, 3) {
|
|
||||||
@Override
|
|
||||||
public int getAttributeId() {
|
|
||||||
return R.styleable.DirectionDragButton_textLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
|
|
||||||
return getLeftRightTextPosition(paint, basePaint, text, baseText, w, h, true);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
right(DragDirection.right, 1) {
|
|
||||||
@Override
|
|
||||||
public int getAttributeId() {
|
|
||||||
return R.styleable.DirectionDragButton_textRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
@Override
|
|
||||||
public Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h) {
|
|
||||||
return getLeftRightTextPosition(paint, basePaint, text, baseText, w, h, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final DragDirection dragDirection;
|
|
||||||
|
|
||||||
private final int attributePosition;
|
|
||||||
|
|
||||||
GuiDragDirection(@NotNull DragDirection dragDirection, int attributePosition) {
|
|
||||||
this.dragDirection = dragDirection;
|
|
||||||
this.attributePosition = attributePosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract int getAttributeId();
|
|
||||||
|
|
||||||
public int getAttributePosition() {
|
|
||||||
return attributePosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public abstract Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, int w, int h);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static Point2d getLeftRightTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, CharSequence text, @NotNull CharSequence baseText, int w, int h, boolean left) {
|
|
||||||
final Point2d result = new Point2d();
|
|
||||||
|
|
||||||
if (left) {
|
|
||||||
float width = paint.measureText(" ");
|
|
||||||
result.setX(width);
|
|
||||||
} else {
|
|
||||||
float width = paint.measureText(text.toString() + " ");
|
|
||||||
result.setX(w - width);
|
|
||||||
}
|
|
||||||
|
|
||||||
float selfHeight = paint.ascent() + paint.descent();
|
|
||||||
|
|
||||||
basePaint.measureText(StringUtils.getNotEmpty(baseText, "|"));
|
|
||||||
|
|
||||||
result.setY(h / 2 - selfHeight / 2);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static Point2d getUpDownTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, CharSequence baseText, float direction, int w, int h) {
|
|
||||||
final Point2d result = new Point2d();
|
|
||||||
|
|
||||||
float width = paint.measureText(text.toString() + " ");
|
|
||||||
result.setX(w - width);
|
|
||||||
|
|
||||||
float selfHeight = paint.ascent() + paint.descent();
|
|
||||||
|
|
||||||
basePaint.measureText(StringUtils.getNotEmpty(baseText, "|"));
|
|
||||||
|
|
||||||
if (direction < 0) {
|
|
||||||
result.setY(h / 2 + h / 3 - selfHeight / 2);
|
|
||||||
} else {
|
|
||||||
result.setY(h / 2 - h / 3 - selfHeight / 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public static GuiDragDirection valueOf(@NotNull DragDirection dragDirection) {
|
|
||||||
for (GuiDragDirection guiDragDirection : values()) {
|
|
||||||
if (guiDragDirection.dragDirection == dragDirection) {
|
|
||||||
return guiDragDirection;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Map<GuiDragDirection, DirectionTextData> directionTextDataMap = new EnumMap<GuiDragDirection, DirectionTextData>(GuiDragDirection.class);
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private String directionTextScale = DEFAULT_DIRECTION_TEXT_SCALE;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Integer directionTextAlpha = DEFAULT_DIRECTION_TEXT_ALPHA;
|
|
||||||
|
|
||||||
private int directionTextColor = DEFAULT_DIRECTION_TEXT_COLOR;
|
|
||||||
|
|
||||||
private boolean initialized = false;
|
|
||||||
|
|
||||||
public DirectionDragButton(Context context, @NotNull AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
init(context, attrs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public DirectionDragButton(Context context, @NotNull DirectionDragButtonDef directionDragButtonDef) {
|
|
||||||
super(context, directionDragButtonDef);
|
|
||||||
init(context, directionDragButtonDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(@NotNull Context context,
|
|
||||||
@NotNull DirectionDragButtonDef directionDragButtonDef) {
|
|
||||||
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
|
|
||||||
final CharSequence directionText = directionDragButtonDef.getText(guiDragDirection.dragDirection);
|
|
||||||
this.directionTextDataMap.put(guiDragDirection, new DirectionTextData(guiDragDirection, StringUtils.getNotEmpty(directionText, "")));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void applyDef(@NotNull DirectionDragButtonDef directionDragButtonDef) {
|
|
||||||
super.applyDef(directionDragButtonDef);
|
|
||||||
|
|
||||||
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
|
|
||||||
final CharSequence directionText = directionDragButtonDef.getText(guiDragDirection.dragDirection);
|
|
||||||
this.directionTextDataMap.put(guiDragDirection, new DirectionTextData(guiDragDirection, StringUtils.getNotEmpty(directionText, "")));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void init(@NotNull Context context, @NotNull AttributeSet attrs) {
|
|
||||||
|
|
||||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DirectionDragButton);
|
|
||||||
|
|
||||||
for (int i = 0; i < a.getIndexCount(); i++) {
|
|
||||||
int attr = a.getIndex(i);
|
|
||||||
|
|
||||||
if (a.hasValue(attr)) {
|
|
||||||
switch (attr) {
|
|
||||||
case R.styleable.DirectionDragButton_directionTextColor:
|
|
||||||
this.directionTextColor = a.getColor(attr, DEFAULT_DIRECTION_TEXT_COLOR);
|
|
||||||
break;
|
|
||||||
case R.styleable.DirectionDragButton_directionTextScale:
|
|
||||||
this.directionTextScale = a.getString(attr);
|
|
||||||
break;
|
|
||||||
case R.styleable.DirectionDragButton_directionTextAlpha:
|
|
||||||
this.directionTextAlpha = a.getInt(attr, DEFAULT_DIRECTION_TEXT_ALPHA);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
// try drag direction text
|
|
||||||
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
|
|
||||||
if (guiDragDirection.getAttributeId() == attr) {
|
|
||||||
this.directionTextDataMap.put(guiDragDirection, new DirectionTextData(guiDragDirection, a.getString(attr)));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Map.Entry<GuiDragDirection, Float> entry : getDirectionTextScales().entrySet()) {
|
|
||||||
final DirectionTextData dtd = directionTextDataMap.get(entry.getKey());
|
|
||||||
if (dtd != null) {
|
|
||||||
dtd.setTextScale(entry.getValue());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSizeChanged(int w, int h, int oldW, int oldH) {
|
|
||||||
measureText();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onTextChanged(CharSequence text, int start, int before, int after) {
|
|
||||||
measureText();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void measureText() {
|
|
||||||
|
|
||||||
if (initialized) {
|
|
||||||
final Paint basePaint = getPaint();
|
|
||||||
final Resources resources = getResources();
|
|
||||||
|
|
||||||
for (DirectionTextData directionTextData : directionTextDataMap.values()) {
|
|
||||||
initDirectionTextPaint(basePaint, directionTextData, resources);
|
|
||||||
|
|
||||||
final GuiDragDirection guiDragDirection = directionTextData.getGuiDragDirection();
|
|
||||||
final String directionText = directionTextData.getText();
|
|
||||||
final Paint directionPaint = directionTextData.getPaint();
|
|
||||||
|
|
||||||
directionTextData.setPosition(guiDragDirection.getTextPosition(directionPaint, basePaint, directionText, getText(), getWidth(), getHeight()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDraw(Canvas canvas) {
|
|
||||||
super.onDraw(canvas);
|
|
||||||
|
|
||||||
final TextPaint paint = getPaint();
|
|
||||||
final Resources resources = getResources();
|
|
||||||
|
|
||||||
for (DirectionTextData directionTextData : directionTextDataMap.values()) {
|
|
||||||
if (directionTextData.isShowText()) {
|
|
||||||
initDirectionTextPaint(paint, directionTextData, resources);
|
|
||||||
final String text = directionTextData.getText();
|
|
||||||
final Point2d position = directionTextData.getPosition();
|
|
||||||
canvas.drawText(text, 0, text.length(), position.getX(), position.getY(), directionTextData.getPaint());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void initDirectionTextPaint(@NotNull Paint basePaint,
|
|
||||||
@NotNull DirectionTextData directionTextData,
|
|
||||||
@NotNull Resources resources) {
|
|
||||||
final TextPaint directionTextPaint = new TextPaint(basePaint);
|
|
||||||
|
|
||||||
directionTextPaint.setColor(directionTextColor);
|
|
||||||
directionTextPaint.setAlpha(getDirectionTextAlpha());
|
|
||||||
directionTextPaint.setTextSize(basePaint.getTextSize() * directionTextData.getTextScale());
|
|
||||||
|
|
||||||
directionTextData.setPaint(directionTextPaint);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int getDirectionTextAlpha() {
|
|
||||||
return directionTextAlpha;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
@Nullable
|
|
||||||
public String getTextUp() {
|
|
||||||
return getText(GuiDragDirection.up);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
@Nullable
|
|
||||||
public String getTextDown() {
|
|
||||||
return getText(GuiDragDirection.down);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public String getText(@NotNull DragDirection direction) {
|
|
||||||
final GuiDragDirection guiDragDirection = GuiDragDirection.valueOf(direction);
|
|
||||||
return guiDragDirection == null ? null : getText(guiDragDirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
public void showDirectionText(boolean showDirectionText, @NotNull DragDirection direction) {
|
|
||||||
final GuiDragDirection guiDragDirection = GuiDragDirection.valueOf(direction);
|
|
||||||
final DirectionTextData directionTextData = this.directionTextDataMap.get(guiDragDirection);
|
|
||||||
if ( directionTextData != null ) {
|
|
||||||
directionTextData.setShowText(showDirectionText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String getText(@NotNull GuiDragDirection direction) {
|
|
||||||
DirectionTextData directionTextData = this.directionTextDataMap.get(direction);
|
|
||||||
if ( directionTextData == null ) {
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
if ( directionTextData.isShowText() ) {
|
|
||||||
return directionTextData.getText();
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public String getDirectionTextScale() {
|
|
||||||
return directionTextScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private Map<GuiDragDirection, Float> getDirectionTextScales() {
|
|
||||||
final List<Float> scales = CollectionTransformations.split(getDirectionTextScale(), ";", NumberParser.getParser(Float.class));
|
|
||||||
|
|
||||||
final Map<GuiDragDirection, Float> result = new HashMap<GuiDragDirection, Float>();
|
|
||||||
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
|
|
||||||
result.put(guiDragDirection, DEFAULT_DIRECTION_TEXT_SCALE_FLOAT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (scales.size() == 1) {
|
|
||||||
final Float scale = scales.get(0);
|
|
||||||
for (Map.Entry<GuiDragDirection, Float> entry : result.entrySet()) {
|
|
||||||
entry.setValue(scale);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (int i = 0; i < scales.size(); i++) {
|
|
||||||
for (GuiDragDirection guiDragDirection : GuiDragDirection.values()) {
|
|
||||||
if (guiDragDirection.getAttributePosition() == i) {
|
|
||||||
result.put(guiDragDirection, scales.get(i));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package org.solovyev.android.view.drag;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 11/3/12
|
|
||||||
* Time: 1:51 PM
|
|
||||||
*/
|
|
||||||
public interface DirectionDragButtonDef extends DragButtonDef {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
CharSequence getText(@NotNull DragDirection dragDirection);
|
|
||||||
}
|
|
@ -1,175 +0,0 @@
|
|||||||
package org.solovyev.android.view.drag;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
|
|
||||||
import java.util.EnumMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 11/3/12
|
|
||||||
* Time: 1:57 PM
|
|
||||||
*/
|
|
||||||
public class DirectionDragButtonDefImpl implements DirectionDragButtonDef {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private CharSequence text;
|
|
||||||
|
|
||||||
private Map<DragDirection, CharSequence> directionsTexts = new EnumMap<DragDirection, CharSequence>(DragDirection.class);
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Integer backgroundResId;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Integer drawableResId;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private String tag;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Float weight;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Integer layoutMarginLeft;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Integer layoutMarginRight;
|
|
||||||
|
|
||||||
private DirectionDragButtonDefImpl() {
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static DirectionDragButtonDefImpl newInstance(@Nullable CharSequence text) {
|
|
||||||
return newInstance(text, null, null, null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static DirectionDragButtonDefImpl newInstance(@Nullable CharSequence text,
|
|
||||||
@Nullable CharSequence up,
|
|
||||||
@Nullable CharSequence right,
|
|
||||||
@Nullable CharSequence down,
|
|
||||||
@Nullable CharSequence left) {
|
|
||||||
return newInstance(text, up, right, down, left, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static DirectionDragButtonDefImpl newInstance(@Nullable CharSequence text,
|
|
||||||
@Nullable CharSequence up,
|
|
||||||
@Nullable CharSequence right,
|
|
||||||
@Nullable CharSequence down,
|
|
||||||
@Nullable CharSequence left,
|
|
||||||
@Nullable Integer backgroundColor) {
|
|
||||||
final DirectionDragButtonDefImpl result = new DirectionDragButtonDefImpl();
|
|
||||||
|
|
||||||
result.text = text;
|
|
||||||
result.directionsTexts.put(DragDirection.up, up);
|
|
||||||
result.directionsTexts.put(DragDirection.right, right);
|
|
||||||
result.directionsTexts.put(DragDirection.down, down);
|
|
||||||
result.directionsTexts.put(DragDirection.left, left);
|
|
||||||
|
|
||||||
result.backgroundResId = backgroundColor;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static DirectionDragButtonDefImpl newDrawableInstance(@NotNull Integer drawableResId) {
|
|
||||||
return newDrawableInstance(drawableResId, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public static DirectionDragButtonDefImpl newDrawableInstance(@NotNull Integer drawableResId, @Nullable Integer backgroundColor) {
|
|
||||||
final DirectionDragButtonDefImpl result = new DirectionDragButtonDefImpl();
|
|
||||||
|
|
||||||
result.drawableResId = drawableResId;
|
|
||||||
result.backgroundResId = backgroundColor;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public CharSequence getText(@NotNull DragDirection dragDirection) {
|
|
||||||
return directionsTexts.get(dragDirection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Float getLayoutWeight() {
|
|
||||||
return this.weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Integer getLayoutMarginLeft() {
|
|
||||||
return this.layoutMarginLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Integer getLayoutMarginRight() {
|
|
||||||
return this.layoutMarginRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Integer getDrawableResId() {
|
|
||||||
return this.drawableResId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public String getTag() {
|
|
||||||
return tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public Integer getBackgroundResId() {
|
|
||||||
return this.backgroundResId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public CharSequence getText() {
|
|
||||||
return text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWeight(@Nullable Float weight) {
|
|
||||||
this.weight = weight;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLayoutMarginRight(@Nullable Integer layoutMarginRight) {
|
|
||||||
this.layoutMarginRight = layoutMarginRight;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLayoutMarginLeft(@Nullable Integer layoutMarginLeft) {
|
|
||||||
this.layoutMarginLeft = layoutMarginLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBackgroundResId(int backgroundResId) {
|
|
||||||
this.backgroundResId = backgroundResId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setTag(@Nullable String tag) {
|
|
||||||
this.tag = tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setText(@Nullable CharSequence text) {
|
|
||||||
this.text = text;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBackgroundResId(@Nullable Integer backgroundResId) {
|
|
||||||
this.backgroundResId = backgroundResId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDrawableResId(@Nullable Integer drawableResId) {
|
|
||||||
this.drawableResId = drawableResId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDirectionText(@NotNull DragDirection key, @Nullable CharSequence text) {
|
|
||||||
directionsTexts.put(key, text);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,164 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
|
|
||||||
* For more information, please, contact se.solovyev@gmail.com
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.solovyev.android.view.drag;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.graphics.Canvas;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.util.AttributeSet;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.view.MotionEvent;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.Button;
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
import org.jetbrains.annotations.Nullable;
|
|
||||||
import org.solovyev.android.view.AndroidViewUtils;
|
|
||||||
import org.solovyev.common.math.Point2d;
|
|
||||||
import org.solovyev.common.text.StringUtils;
|
|
||||||
|
|
||||||
public class DragButton extends Button {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private Point2d startPoint = null;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private org.solovyev.android.view.drag.OnDragListener onDragListener;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private DragButton.OnTouchListenerImpl onTouchListener;
|
|
||||||
|
|
||||||
private boolean showText = true;
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private final Handler uiHandler = new Handler();
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private CharSequence textBackup;
|
|
||||||
|
|
||||||
public DragButton(@NotNull Context context, @NotNull AttributeSet attrs) {
|
|
||||||
super(context, attrs);
|
|
||||||
setOnTouchListener(new OnTouchListenerImpl());
|
|
||||||
}
|
|
||||||
|
|
||||||
public DragButton(@NotNull Context context, @NotNull DragButtonDef dragButtonDef) {
|
|
||||||
super(context);
|
|
||||||
|
|
||||||
setOnTouchListener(new OnTouchListenerImpl());
|
|
||||||
|
|
||||||
setText(dragButtonDef.getText());
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setOnDragListener(@Nullable org.solovyev.android.view.drag.OnDragListener onDragListener) {
|
|
||||||
this.onDragListener = onDragListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public org.solovyev.android.view.drag.OnDragListener getOnDragListener() {
|
|
||||||
return onDragListener;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void applyDef(@NotNull DragButtonDef buttonDef) {
|
|
||||||
AndroidViewUtils.applyButtonDef(this, buttonDef);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setOnTouchListener(OnTouchListener l) {
|
|
||||||
if (l instanceof OnTouchListenerImpl) {
|
|
||||||
this.onTouchListener = (OnTouchListenerImpl) l;
|
|
||||||
super.setOnTouchListener(l);
|
|
||||||
} else {
|
|
||||||
this.onTouchListener.nestedOnTouchListener = l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OnTouchListener implementation that fires onDrag()
|
|
||||||
*
|
|
||||||
* @author serso
|
|
||||||
*/
|
|
||||||
private final class OnTouchListenerImpl implements OnTouchListener {
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
private OnTouchListener nestedOnTouchListener;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onTouch(@NotNull View v, @NotNull MotionEvent event) {
|
|
||||||
// processing on touch event
|
|
||||||
|
|
||||||
boolean consumed = false;
|
|
||||||
|
|
||||||
// in order to avoid possible NPEs
|
|
||||||
final Point2d localStartPoint = startPoint;
|
|
||||||
final org.solovyev.android.view.drag.OnDragListener localOnDragListener = onDragListener;
|
|
||||||
|
|
||||||
if (localOnDragListener != null) {
|
|
||||||
// only if onDrag() listener specified
|
|
||||||
|
|
||||||
Log.d(String.valueOf(getId()), "onTouch() for: " + getId() + " . Motion event: " + event);
|
|
||||||
|
|
||||||
switch (event.getAction()) {
|
|
||||||
case MotionEvent.ACTION_DOWN:
|
|
||||||
// start tracking: set start point
|
|
||||||
startPoint = new Point2d(event.getX(), event.getY());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MotionEvent.ACTION_UP:
|
|
||||||
// stop tracking
|
|
||||||
|
|
||||||
if (localStartPoint != null) {
|
|
||||||
consumed = localOnDragListener.onDrag(DragButton.this, new DragEvent(localStartPoint, event));
|
|
||||||
|
|
||||||
if (consumed) {
|
|
||||||
if (localOnDragListener.isSuppressOnClickEvent()) {
|
|
||||||
// prevent on click action
|
|
||||||
v.setPressed(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
startPoint = null;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nestedOnTouchListener != null && !consumed) {
|
|
||||||
return nestedOnTouchListener.onTouch(v, event);
|
|
||||||
} else {
|
|
||||||
return consumed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onDraw(Canvas canvas) {
|
|
||||||
CharSequence text = getText();
|
|
||||||
if (!StringUtils.isEmpty(text)) {
|
|
||||||
super.onDraw(canvas);
|
|
||||||
} else {
|
|
||||||
if (!AndroidViewUtils.drawDrawables(canvas, this)) {
|
|
||||||
super.onDraw(canvas);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public boolean isShowText() {
|
|
||||||
return showText;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setShowText(boolean showText) {
|
|
||||||
if (this.showText != showText) {
|
|
||||||
if ( showText ) {
|
|
||||||
setText(textBackup);
|
|
||||||
textBackup = null;
|
|
||||||
} else {
|
|
||||||
textBackup = this.getText();
|
|
||||||
setText(null);
|
|
||||||
}
|
|
||||||
this.showText = showText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package org.solovyev.android.view.drag;
|
|
||||||
|
|
||||||
import org.solovyev.android.view.ButtonDef;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* User: serso
|
|
||||||
* Date: 11/3/12
|
|
||||||
* Time: 1:51 PM
|
|
||||||
*/
|
|
||||||
public interface DragButtonDef extends ButtonDef {
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user