Skip to content

Commit bb0cbae

Browse files
author
Fabrice Di Meglio
committed
Fix for bug #7417949 TextView / EditText error Drawable is not put on the left in RTL mode
- keep the Error Drawable infos into the Drawables cache - reset left/right Drawable state before resolving where to put the Error Drawable - get the mirrored Drawable for the Error popup background - set the Error popup position depending on the layout direction (so that the "triangle" of the background is pointing to the middle of the Error icon) One restriction: we load the Error popup background Drawable corresponding to the layout direction of the System Locale. So if you set the Layout direction on a TextView (or an EditText) to RTL and set an error to it when you are in a RTL System Locale, then you see that the background "triangle" is not pointing to the Error icon. This is working as intended as the AssetManager load the Drawable resource depending on the configuration which is in that case the RTL one thus loading the RTL version of the background (and not the LTR one). Thus there can be a discrepancy between the "layout direction" of the TextView and the one from the Error popup background. This would happen only thru using the SDK and not in a normal case when running an App. Change-Id: I91bbfbe46ac20efe0e585c5d4c766db23b5c709d
1 parent 5acc379 commit bb0cbae

22 files changed

+178
-73
lines changed

core/java/android/widget/Editor.java

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -309,13 +309,15 @@ public void setError(CharSequence error, Drawable icon) {
309309
}
310310

311311
private void setErrorIcon(Drawable icon) {
312-
final Drawables dr = mTextView.mDrawables;
313-
if (dr != null) {
314-
mTextView.setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, icon,
315-
dr.mDrawableBottom);
316-
} else {
317-
mTextView.setCompoundDrawables(null, null, icon, null);
312+
Drawables dr = mTextView.mDrawables;
313+
if (dr == null) {
314+
mTextView.mDrawables = dr = new Drawables();
318315
}
316+
dr.setErrorDrawable(icon, mTextView);
317+
318+
mTextView.resetResolvedDrawables();
319+
mTextView.invalidate();
320+
mTextView.requestLayout();
319321
}
320322

321323
private void hideError() {
@@ -329,7 +331,7 @@ private void hideError() {
329331
}
330332

331333
/**
332-
* Returns the Y offset to make the pointy top of the error point
334+
* Returns the X offset to make the pointy top of the error point
333335
* at the middle of the error icon.
334336
*/
335337
private int getErrorX() {
@@ -340,8 +342,23 @@ private int getErrorX() {
340342
final float scale = mTextView.getResources().getDisplayMetrics().density;
341343

342344
final Drawables dr = mTextView.mDrawables;
343-
return mTextView.getWidth() - mErrorPopup.getWidth() - mTextView.getPaddingRight() -
344-
(dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f);
345+
346+
final int layoutDirection = mTextView.getLayoutDirection();
347+
int errorX;
348+
int offset;
349+
switch (layoutDirection) {
350+
default:
351+
case View.LAYOUT_DIRECTION_LTR:
352+
offset = - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f);
353+
errorX = mTextView.getWidth() - mErrorPopup.getWidth() -
354+
mTextView.getPaddingRight() + offset;
355+
break;
356+
case View.LAYOUT_DIRECTION_RTL:
357+
offset = (dr != null ? dr.mDrawableSizeLeft : 0) / 2 - (int) (25 * scale + 0.5f);
358+
errorX = mTextView.getPaddingLeft() + offset;
359+
break;
360+
}
361+
return errorX;
345362
}
346363

347364
/**
@@ -358,16 +375,27 @@ private int getErrorY() {
358375
mTextView.getCompoundPaddingBottom() - compoundPaddingTop;
359376

360377
final Drawables dr = mTextView.mDrawables;
361-
int icontop = compoundPaddingTop +
362-
(vspace - (dr != null ? dr.mDrawableHeightRight : 0)) / 2;
378+
379+
final int layoutDirection = mTextView.getLayoutDirection();
380+
int height;
381+
switch (layoutDirection) {
382+
default:
383+
case View.LAYOUT_DIRECTION_LTR:
384+
height = (dr != null ? dr.mDrawableHeightRight : 0);
385+
break;
386+
case View.LAYOUT_DIRECTION_RTL:
387+
height = (dr != null ? dr.mDrawableHeightLeft : 0);
388+
break;
389+
}
390+
391+
int icontop = compoundPaddingTop + (vspace - height) / 2;
363392

364393
/*
365394
* The "2" is the distance between the point and the top edge
366395
* of the background.
367396
*/
368397
final float scale = mTextView.getResources().getDisplayMetrics().density;
369-
return icontop + (dr != null ? dr.mDrawableHeightRight : 0) - mTextView.getHeight() -
370-
(int) (2 * scale + 0.5f);
398+
return icontop + height - mTextView.getHeight() - (int) (2 * scale + 0.5f);
371399
}
372400

373401
void createInputContentTypeIfNeeded() {
@@ -3726,7 +3754,7 @@ private static class ErrorPopup extends PopupWindow {
37263754
super(v, width, height);
37273755
mView = v;
37283756
// Make sure the TextView has a background set as it will be used the first time it is
3729-
// shown and positionned. Initialized with below background, which should have
3757+
// shown and positioned. Initialized with below background, which should have
37303758
// dimensions identical to the above version for this to work (and is more likely).
37313759
mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId,
37323760
com.android.internal.R.styleable.Theme_errorMessageBackground);

core/java/android/widget/TextView.java

Lines changed: 136 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -284,15 +284,144 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
284284
private TextUtils.TruncateAt mEllipsize;
285285

286286
static class Drawables {
287+
final static int DRAWABLE_NONE = -1;
288+
final static int DRAWABLE_RIGHT = 0;
289+
final static int DRAWABLE_LEFT = 1;
290+
287291
final Rect mCompoundRect = new Rect();
292+
288293
Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight,
289-
mDrawableStart, mDrawableEnd;
294+
mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp;
295+
290296
int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight,
291-
mDrawableSizeStart, mDrawableSizeEnd;
297+
mDrawableSizeStart, mDrawableSizeEnd, mDrawableSizeError, mDrawableSizeTemp;
298+
292299
int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight,
293-
mDrawableHeightStart, mDrawableHeightEnd;
300+
mDrawableHeightStart, mDrawableHeightEnd, mDrawableHeightError, mDrawableHeightTemp;
301+
294302
int mDrawablePadding;
303+
304+
int mDrawableSaved = DRAWABLE_NONE;
305+
306+
public void resolveWithLayoutDirection(int layoutDirection) {
307+
switch(layoutDirection) {
308+
case LAYOUT_DIRECTION_RTL:
309+
if (mDrawableStart != null) {
310+
mDrawableRight = mDrawableStart;
311+
312+
mDrawableSizeRight = mDrawableSizeStart;
313+
mDrawableHeightRight = mDrawableHeightStart;
314+
}
315+
if (mDrawableEnd != null) {
316+
mDrawableLeft = mDrawableEnd;
317+
318+
mDrawableSizeLeft = mDrawableSizeEnd;
319+
mDrawableHeightLeft = mDrawableHeightEnd;
320+
}
321+
break;
322+
323+
case LAYOUT_DIRECTION_LTR:
324+
default:
325+
if (mDrawableStart != null) {
326+
mDrawableLeft = mDrawableStart;
327+
328+
mDrawableSizeLeft = mDrawableSizeStart;
329+
mDrawableHeightLeft = mDrawableHeightStart;
330+
}
331+
if (mDrawableEnd != null) {
332+
mDrawableRight = mDrawableEnd;
333+
334+
mDrawableSizeRight = mDrawableSizeEnd;
335+
mDrawableHeightRight = mDrawableHeightEnd;
336+
}
337+
break;
338+
}
339+
applyErrorDrawableIfNeeded(layoutDirection);
340+
updateDrawablesLayoutDirection(layoutDirection);
341+
}
342+
343+
private void updateDrawablesLayoutDirection(int layoutDirection) {
344+
if (mDrawableLeft != null) {
345+
mDrawableLeft.setLayoutDirection(layoutDirection);
346+
}
347+
if (mDrawableRight != null) {
348+
mDrawableRight.setLayoutDirection(layoutDirection);
349+
}
350+
if (mDrawableTop != null) {
351+
mDrawableTop.setLayoutDirection(layoutDirection);
352+
}
353+
if (mDrawableBottom != null) {
354+
mDrawableBottom.setLayoutDirection(layoutDirection);
355+
}
356+
}
357+
358+
public void setErrorDrawable(Drawable dr, TextView tv) {
359+
if (mDrawableError != dr && mDrawableError != null) {
360+
mDrawableError.setCallback(null);
361+
}
362+
mDrawableError = dr;
363+
364+
final Rect compoundRect = mCompoundRect;
365+
int[] state = tv.getDrawableState();
366+
367+
if (mDrawableError != null) {
368+
mDrawableError.setState(state);
369+
mDrawableError.copyBounds(compoundRect);
370+
mDrawableError.setCallback(tv);
371+
mDrawableSizeError = compoundRect.width();
372+
mDrawableHeightError = compoundRect.height();
373+
} else {
374+
mDrawableSizeError = mDrawableHeightError = 0;
375+
}
376+
}
377+
378+
private void applyErrorDrawableIfNeeded(int layoutDirection) {
379+
// first restore the initial state if needed
380+
switch (mDrawableSaved) {
381+
case DRAWABLE_LEFT:
382+
mDrawableLeft = mDrawableTemp;
383+
mDrawableSizeLeft = mDrawableSizeTemp;
384+
mDrawableHeightLeft = mDrawableHeightTemp;
385+
break;
386+
case DRAWABLE_RIGHT:
387+
mDrawableRight = mDrawableTemp;
388+
mDrawableSizeRight = mDrawableSizeTemp;
389+
mDrawableHeightRight = mDrawableHeightTemp;
390+
break;
391+
case DRAWABLE_NONE:
392+
default:
393+
}
394+
// then, if needed, assign the Error drawable to the correct location
395+
if (mDrawableError != null) {
396+
switch(layoutDirection) {
397+
case LAYOUT_DIRECTION_RTL:
398+
mDrawableSaved = DRAWABLE_LEFT;
399+
400+
mDrawableTemp = mDrawableLeft;
401+
mDrawableSizeTemp = mDrawableSizeLeft;
402+
mDrawableHeightTemp = mDrawableHeightLeft;
403+
404+
mDrawableLeft = mDrawableError;
405+
mDrawableSizeLeft = mDrawableSizeError;
406+
mDrawableHeightLeft = mDrawableHeightError;
407+
break;
408+
case LAYOUT_DIRECTION_LTR:
409+
default:
410+
mDrawableSaved = DRAWABLE_RIGHT;
411+
412+
mDrawableTemp = mDrawableRight;
413+
mDrawableSizeTemp = mDrawableSizeRight;
414+
mDrawableHeightTemp = mDrawableHeightRight;
415+
416+
mDrawableRight = mDrawableError;
417+
mDrawableSizeRight = mDrawableSizeError;
418+
mDrawableHeightRight = mDrawableHeightError;
419+
break;
420+
}
421+
}
422+
}
295423
}
424+
296425
Drawables mDrawables;
297426

298427
private CharWrapper mCharWrapper;
@@ -8264,70 +8393,18 @@ public void onResolveDrawables(int layoutDirection) {
82648393
return;
82658394
}
82668395
mLastLayoutDirection = layoutDirection;
8267-
// No drawable to resolve
8268-
if (mDrawables == null) {
8269-
return;
8270-
}
8271-
// No relative drawable to resolve
8272-
if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) {
8273-
return;
8274-
}
8275-
8276-
Drawables dr = mDrawables;
8277-
switch(layoutDirection) {
8278-
case LAYOUT_DIRECTION_RTL:
8279-
if (dr.mDrawableStart != null) {
8280-
dr.mDrawableRight = dr.mDrawableStart;
8281-
8282-
dr.mDrawableSizeRight = dr.mDrawableSizeStart;
8283-
dr.mDrawableHeightRight = dr.mDrawableHeightStart;
8284-
}
8285-
if (dr.mDrawableEnd != null) {
8286-
dr.mDrawableLeft = dr.mDrawableEnd;
82878396

8288-
dr.mDrawableSizeLeft = dr.mDrawableSizeEnd;
8289-
dr.mDrawableHeightLeft = dr.mDrawableHeightEnd;
8290-
}
8291-
break;
8292-
8293-
case LAYOUT_DIRECTION_LTR:
8294-
default:
8295-
if (dr.mDrawableStart != null) {
8296-
dr.mDrawableLeft = dr.mDrawableStart;
8297-
8298-
dr.mDrawableSizeLeft = dr.mDrawableSizeStart;
8299-
dr.mDrawableHeightLeft = dr.mDrawableHeightStart;
8300-
}
8301-
if (dr.mDrawableEnd != null) {
8302-
dr.mDrawableRight = dr.mDrawableEnd;
8303-
8304-
dr.mDrawableSizeRight = dr.mDrawableSizeEnd;
8305-
dr.mDrawableHeightRight = dr.mDrawableHeightEnd;
8306-
}
8307-
break;
8308-
}
8309-
updateDrawablesLayoutDirection(dr, layoutDirection);
8310-
}
8311-
8312-
private void updateDrawablesLayoutDirection(Drawables dr, int layoutDirection) {
8313-
if (dr.mDrawableLeft != null) {
8314-
dr.mDrawableLeft.setLayoutDirection(layoutDirection);
8315-
}
8316-
if (dr.mDrawableRight != null) {
8317-
dr.mDrawableRight.setLayoutDirection(layoutDirection);
8318-
}
8319-
if (dr.mDrawableTop != null) {
8320-
dr.mDrawableTop.setLayoutDirection(layoutDirection);
8321-
}
8322-
if (dr.mDrawableBottom != null) {
8323-
dr.mDrawableBottom.setLayoutDirection(layoutDirection);
8397+
// Resolve drawables
8398+
if (mDrawables != null) {
8399+
mDrawables.resolveWithLayoutDirection(layoutDirection);
83248400
}
83258401
}
83268402

83278403
/**
83288404
* @hide
83298405
*/
83308406
protected void resetResolvedDrawables() {
8407+
super.resetResolvedDrawables();
83318408
mLastLayoutDirection = -1;
83328409
}
83338410

2.33 KB
Loading
2.35 KB
Loading
940 Bytes
Loading
932 Bytes
Loading
915 Bytes
Loading
919 Bytes
Loading
1.09 KB
Loading
1.12 KB
Loading

0 commit comments

Comments
 (0)