diff options
author | 2012-02-14 15:52:17 -0800 | |
---|---|---|
committer | 2012-02-14 15:52:17 -0800 | |
commit | dd29f8c4e3db3338bc055302145c3bc51a27566f (patch) | |
tree | 5ae5f5c6fb6e6aa899bfbf6c63b72f3e19f16d80 | |
parent | 9ae763133238ad5a1963d99a69cf891293345cb3 (diff) | |
parent | 73eb97f628b298c7bd032aa9db11dadf05f5b539 (diff) |
Merge "Revert "Incorrect behavior of View clear focus.""
-rw-r--r-- | core/java/android/view/View.java | 23 | ||||
-rw-r--r-- | core/java/android/view/ViewGroup.java | 19 | ||||
-rw-r--r-- | core/java/android/view/ViewRootImpl.java | 35 | ||||
-rw-r--r-- | core/tests/coretests/src/android/widget/focus/RequestFocus.java | 2 | ||||
-rw-r--r-- | core/tests/coretests/src/android/widget/focus/RequestFocusTest.java | 161 |
5 files changed, 33 insertions, 207 deletions
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index ba1b7af2e4bc..87104f4bc364 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -3766,14 +3766,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } if ((mPrivateFlags & FOCUSED) != 0) { - // If this is the first focusable do not clear focus since the we - // try to give it focus every time a view clears its focus. Hence, - // the view that would gain focus already has it. - View firstFocusable = getFirstFocusable(); - if (firstFocusable == this) { - return; - } - mPrivateFlags &= ~FOCUSED; if (mParent != null) { @@ -3782,24 +3774,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal onFocusChanged(false, 0, null); refreshDrawableState(); - - // The view cleared focus and invoked the callbacks, so now is the - // time to give focus to the the first focusable to ensure that the - // gain focus is announced after clear focus. - if (firstFocusable != null) { - firstFocusable.requestFocus(FOCUS_FORWARD); - } } } - private View getFirstFocusable() { - ViewRootImpl viewRoot = getViewRootImpl(); - if (viewRoot != null) { - return viewRoot.focusSearch(null, FOCUS_FORWARD); - } - return null; - } - /** * Called to clear the focus of a view that is about to be removed. * Doesn't call clearChildFocus, which prevents this view from taking diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index a2e85a334575..bc147ac201f4 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -675,14 +675,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ @Override public void clearFocus() { - if (DBG) { - System.out.println(this + " clearFocus()"); - } - if (mFocused == null) { - super.clearFocus(); - } else { + super.clearFocus(); + + // clear any child focus if it exists + if (mFocused != null) { mFocused.clearFocus(); - mFocused = null; } } @@ -694,12 +691,12 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if (DBG) { System.out.println(this + " unFocus()"); } - if (mFocused == null) { - super.unFocus(); - } else { + + super.unFocus(); + if (mFocused != null) { mFocused.unFocus(); - mFocused = null; } + mFocused = null; } /** diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index 1930a5edd968..1c3bbfa42d26 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -174,7 +174,6 @@ public final class ViewRootImpl extends Handler implements ViewParent, View mView; View mFocusedView; View mRealFocusedView; // this is not set to null in touch mode - View mOldFocusedView; int mViewVisibility; boolean mAppVisible = true; int mOrigWindowType = -1; @@ -2273,34 +2272,33 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void requestChildFocus(View child, View focused) { checkThread(); - - if (DEBUG_INPUT_RESIZE) { - Log.v(TAG, "Request child focus: focus now " + focused); + if (mFocusedView != focused) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused); + scheduleTraversals(); } - - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, focused); - scheduleTraversals(); - mFocusedView = mRealFocusedView = focused; + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now " + + mFocusedView); } public void clearChildFocus(View child) { checkThread(); - if (DEBUG_INPUT_RESIZE) { - Log.v(TAG, "Clearing child focus"); - } - - mOldFocusedView = mFocusedView; - - // Invoke the listener only if there is no view to take focus - if (focusSearch(null, View.FOCUS_FORWARD) == null) { - mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mOldFocusedView, null); - } + View oldFocus = mFocusedView; + if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus"); mFocusedView = mRealFocusedView = null; + if (mView != null && !mView.hasFocus()) { + // If a view gets the focus, the listener will be invoked from requestChildFocus() + if (!mView.requestFocus(View.FOCUS_FORWARD)) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); + } + } else if (oldFocus != null) { + mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null); + } } + public void focusableViewAvailable(View v) { checkThread(); @@ -2772,7 +2770,6 @@ public final class ViewRootImpl extends Handler implements ViewParent, mView.unFocus(); mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null); mFocusedView = null; - mOldFocusedView = null; return true; } } diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocus.java b/core/tests/coretests/src/android/widget/focus/RequestFocus.java index 21d762a55061..af9ee170abe4 100644 --- a/core/tests/coretests/src/android/widget/focus/RequestFocus.java +++ b/core/tests/coretests/src/android/widget/focus/RequestFocus.java @@ -21,7 +21,9 @@ import com.android.frameworks.coretests.R; import android.app.Activity; import android.os.Bundle; import android.os.Handler; +import android.widget.LinearLayout; import android.widget.Button; +import android.view.View; /** * Exercises cases where elements of the UI are requestFocus()ed. diff --git a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java index baf587e39c1b..a78b0c932c4b 100644 --- a/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java +++ b/core/tests/coretests/src/android/widget/focus/RequestFocusTest.java @@ -16,27 +16,21 @@ package android.widget.focus; +import android.widget.focus.RequestFocus; +import com.android.frameworks.coretests.R; + import android.os.Handler; -import android.test.ActivityInstrumentationTestCase2; -import android.test.UiThreadTest; +import android.test.ActivityInstrumentationTestCase; import android.test.suitebuilder.annotation.LargeTest; import android.test.suitebuilder.annotation.MediumTest; -import android.util.AndroidRuntimeException; -import android.view.View; -import android.view.View.OnFocusChangeListener; -import android.view.ViewTreeObserver.OnGlobalFocusChangeListener; import android.widget.Button; - -import com.android.frameworks.coretests.R; - -import java.util.ArrayList; -import java.util.List; +import android.util.AndroidRuntimeException; /** * {@link RequestFocusTest} is set up to exercise cases where the views that * have focus become invisible or GONE. */ -public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFocus> { +public class RequestFocusTest extends ActivityInstrumentationTestCase<RequestFocus> { private Button mTopLeftButton; private Button mBottomLeftButton; @@ -45,7 +39,7 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFo private Handler mHandler; public RequestFocusTest() { - super(RequestFocus.class); + super("com.android.frameworks.coretests", RequestFocus.class); } @Override @@ -100,145 +94,4 @@ public class RequestFocusTest extends ActivityInstrumentationTestCase2<RequestFo e.getClass().getName()); } } - - /** - * This tests checks the case in which the first focusable View clears focus. - * In such a case the framework tries to give the focus to another View starting - * from the top. Hence, the framework will try to give focus to the view that - * wants to clear its focus. From a client perspective, the view does not loose - * focus after the call, therefore no callback for focus change should be invoked. - * - * @throws Exception If an error occurs. - */ - @UiThreadTest - public void testOnFocusChangeNotCalledIfFocusDoesNotMove() throws Exception { - // Get the first focusable. - Button button = mTopLeftButton; - - // Make sure that the button is the first focusable and focus it. - button.getRootView().requestFocus(View.FOCUS_DOWN); - assertTrue(button.hasFocus()); - - // Attach on focus change listener that should not be called. - button.setOnFocusChangeListener(new OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - throw new IllegalStateException("Unexpeced call to" - + "OnFocusChangeListener#onFocusChange"); - } - }); - - // Attach on global focus change listener that should not be called. - button.getViewTreeObserver().addOnGlobalFocusChangeListener( - new OnGlobalFocusChangeListener() { - @Override - public void onGlobalFocusChanged(View oldFocus, View newFocus) { - throw new IllegalStateException("Unexpeced call to" - + "OnFocusChangeListener#onFocusChange"); - } - }); - - // Try to clear focus. - button.clearFocus(); - } - - /** - * This tests check whether the on focus change callbacks are invoked in - * the proper order when a View loses focus and the framework gives it to - * the fist focusable one. - * - * @throws Exception - */ - @UiThreadTest - public void testOnFocusChangeCallbackOrder() throws Exception { - // Get the first focusable. - Button clearingFocusButton = mTopRightButton; - Button gainingFocusButton = mTopLeftButton; - - // Make sure that the clearing focus is not the first focusable. - View focusCandidate = clearingFocusButton.getRootView().getParent().focusSearch(null, - View.FOCUS_FORWARD); - assertNotSame("The clearing focus button is not the first focusable.", - clearingFocusButton, focusCandidate); - assertSame("The gaining focus button is the first focusable.", - gainingFocusButton, focusCandidate); - - // Focus the clearing focus button. - clearingFocusButton.requestFocus(); - assertTrue(clearingFocusButton.hasFocus()); - - // Register the invocation order checker. - CallbackOrderChecker checker = new CallbackOrderChecker(clearingFocusButton, - gainingFocusButton); - clearingFocusButton.setOnFocusChangeListener(checker); - gainingFocusButton.setOnFocusChangeListener(checker); - clearingFocusButton.getViewTreeObserver().addOnGlobalFocusChangeListener(checker); - - // Try to clear focus. - clearingFocusButton.clearFocus(); - - // Check that no callback was invoked since focus did not move. - checker.verify(); - } - - /** - * This class check whether the focus change callback are invoked in order. - */ - private class CallbackOrderChecker implements OnFocusChangeListener, - OnGlobalFocusChangeListener { - - private class CallbackInvocation { - final String mMethodName; - final Object[] mArguments; - - CallbackInvocation(String methodName, Object[] arguments) { - mMethodName = methodName; - mArguments = arguments; - } - } - - private final View mClearingFocusView; - private final View mGainingFocusView; - - private final List<CallbackInvocation> mInvocations = new ArrayList<CallbackInvocation>(); - - public CallbackOrderChecker(View clearingFocusView, View gainingFocusView) { - mClearingFocusView = clearingFocusView; - mGainingFocusView = gainingFocusView; - } - - @Override - public void onFocusChange(View view, boolean hasFocus) { - CallbackInvocation invocation = new CallbackInvocation( - "OnFocusChangeListener#onFocusChange", new Object[] {view, hasFocus}); - mInvocations.add(invocation); - } - - @Override - public void onGlobalFocusChanged(View oldFocus, View newFocus) { - CallbackInvocation invocation = new CallbackInvocation( - "OnFocusChangeListener#onFocusChange", new Object[] {oldFocus, newFocus}); - mInvocations.add(invocation); - } - - public void verify() { - assertSame("All focus change callback should be invoked.", 3, mInvocations.size()); - assertInvioked("Callback for View clearing focus explected.", 0, - "OnFocusChangeListener#onFocusChange", - new Object[] {mClearingFocusView, false}); - assertInvioked("Callback for View global focus change explected.", 1, - "OnFocusChangeListener#onFocusChange", new Object[] {mClearingFocusView, - mGainingFocusView}); - assertInvioked("Callback for View gaining focus explected.", 2, - "OnFocusChangeListener#onFocusChange", new Object[] {mGainingFocusView, true}); - } - - private void assertInvioked(String message, int order, String methodName, - Object[] arguments) { - CallbackInvocation invocation = mInvocations.get(order); - assertEquals(message, methodName, invocation.mMethodName); - assertEquals(message, arguments[0], invocation.mArguments[0]); - assertEquals(message, arguments[1], invocation.mArguments[1]); - } - } } |