4747import android .view .ViewParent ;
4848import android .view .accessibility .AccessibilityNodeInfo ;
4949import android .widget .EditText ;
50+ import android .widget .FrameLayout ;
5051import android .widget .ImageButton ;
5152import android .widget .TextView ;
5253import androidx .annotation .ColorInt ;
@@ -136,6 +137,8 @@ public class SearchBar extends Toolbar {
136137 private static final String NAMESPACE_APP = "http://schemas.android.com/apk/res-auto" ;
137138
138139 private final TextView textView ;
140+ private final TextView placeholderTextView ;
141+ private final FrameLayout textViewContainer ;
139142 private final int backgroundColor ;
140143
141144 private boolean liftOnScroll ;
@@ -228,12 +231,18 @@ public SearchBar(@NonNull Context context, @Nullable AttributeSet attrs, int def
228231 layoutInflated = true ;
229232
230233 textView = findViewById (R .id .open_search_bar_text_view );
234+ placeholderTextView = findViewById (R .id .open_search_bar_placeholder_text_view );
235+ textViewContainer = findViewById (R .id .open_search_bar_text_view_container );
231236
232237 setElevation (elevation );
233238 initTextView (textAppearanceResId , text , hint );
234239 initBackground (shapeAppearanceModel , backgroundColor , elevation , strokeWidth , strokeColor );
235240 }
236241
242+ void setPlaceholderText (String string ) {
243+ placeholderTextView .setText (string );
244+ }
245+
237246 private void validateAttributes (@ Nullable AttributeSet attributeSet ) {
238247 if (attributeSet == null ) {
239248 return ;
@@ -273,6 +282,7 @@ private void initNavigationIcon() {
273282 private void initTextView (@ StyleRes int textAppearanceResId , String text , String hint ) {
274283 if (textAppearanceResId != -1 ) {
275284 TextViewCompat .setTextAppearance (textView , textAppearanceResId );
285+ TextViewCompat .setTextAppearance (placeholderTextView , textAppearanceResId );
276286 }
277287 setText (text );
278288 setHint (hint );
@@ -423,7 +433,7 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
423433 super .onLayout (changed , left , top , right , bottom );
424434
425435 if (centerView != null ) {
426- layoutViewInCenter (centerView , /* absolutePlacement= */ true );
436+ layoutViewInCenter (centerView );
427437 }
428438 setHandwritingBoundsInsets ();
429439 if (textView != null ) {
@@ -432,7 +442,9 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto
432442 // to be pushed to the side. In this case, we want the textview to be still centered on top of
433443 // any center views.
434444 if (textCentered ) {
435- layoutViewInCenter (textView , /* absolutePlacement= */ false );
445+ // Make sure textview does not overlap with any toolbar views (nav icon, menu) or
446+ // padding/insets
447+ layoutTextViewCenterAvoidToolbarViewsAndPadding ();
436448 }
437449 }
438450 }
@@ -581,52 +593,72 @@ private ImageButton findOrGetNavView() {
581593 return navIconButton ;
582594 }
583595
596+ private void layoutTextViewCenterAvoidToolbarViewsAndPadding () {
597+ int textViewContainerLeft = getMeasuredWidth () / 2 - textViewContainer .getMeasuredWidth () / 2 ;
598+ int textViewContainerRight = textViewContainerLeft + textViewContainer .getMeasuredWidth ();
599+ int textViewContainerTop = getMeasuredHeight () / 2 - textViewContainer .getMeasuredHeight () / 2 ;
600+ int textViewContainerBottom = textViewContainerTop + textViewContainer .getMeasuredHeight ();
601+ boolean isRtl = getLayoutDirection () == LAYOUT_DIRECTION_RTL ;
602+ View menuView = findOrGetMenuView ();
603+ View navIconButton = findOrGetNavView ();
604+
605+ int textViewLeft = textViewContainer .getMeasuredWidth () / 2 - textView .getMeasuredWidth () / 2 ;
606+ int textViewRight = textViewLeft + textView .getMeasuredWidth ();
607+
608+ // left and right refer to the textview's left and right coordinates within the searchbar
609+ int left = textViewLeft + textViewContainerLeft ;
610+ int right = textViewContainerLeft + textViewRight ;
611+
612+ View leftView = isRtl ? menuView : navIconButton ;
613+ View rightView = isRtl ? navIconButton : menuView ;
614+ int leftShift = 0 ;
615+ int rightShift = 0 ;
616+ if (leftView != null ) {
617+ leftShift = max (leftView .getRight () - left , 0 );
618+ }
619+ left += leftShift ;
620+ right += leftShift ;
621+ if (rightView != null ) {
622+ rightShift = max (right - rightView .getLeft (), 0 );
623+ }
624+ left -= rightShift ;
625+ right -= rightShift ;
626+ // Make sure to not lay out the view inside the SearchBar padding. paddingLeftAdded and
627+ // paddingRightAdded will never be non-zero at the same time, as Toolbar.measure has already
628+ // measured the children accounting for padding.
629+ int paddingLeftShift = max (getPaddingLeft () - left , getContentInsetLeft () - left );
630+ int paddingRightShift =
631+ max (
632+ right - (getMeasuredWidth () - getPaddingRight ()),
633+ right - (getMeasuredWidth () - getContentInsetRight ()));
634+ paddingLeftShift = max (paddingLeftShift , 0 );
635+ paddingRightShift = max (paddingRightShift , 0 );
636+
637+ int totalShift = leftShift - rightShift + paddingLeftShift - paddingRightShift ;
638+ // Center the textViewContainer and shift over textViewContainer by the amount that textView
639+ // needs to be shifted over; this shifts both the container and the textView, which is necessary so the textView doesn't get
640+ // laid outside of the container.
641+ textViewContainer .layout (
642+ textViewContainerLeft + totalShift ,
643+ textViewContainerTop ,
644+ textViewContainerRight + totalShift ,
645+ textViewContainerBottom );
646+ }
647+
584648 /**
585649 * Lays out the given view in the center of the {@link SearchBar}.
586650 *
587651 * @param view The view to layout in the center.
588- * @param absolutePlacement Whether the view will be placed absolutely in the center (eg. ignoring
589- * other toolbar elements like the nav icon and menu, padding, content insets)
590652 */
591- private void layoutViewInCenter (View view , boolean absolutePlacement ) {
653+ private void layoutViewInCenter (View view ) {
592654 if (view == null ) {
593655 return ;
594656 }
595657
596658 int viewWidth = view .getMeasuredWidth ();
597659 int left = getMeasuredWidth () / 2 - viewWidth / 2 ;
598660 int right = left + viewWidth ;
599- if (!absolutePlacement ) {
600- View menuView = findOrGetMenuView ();
601- if (menuView != null ) {
602- int diff =
603- getLayoutDirection () == LAYOUT_DIRECTION_RTL
604- ? max (menuView .getRight () - left , 0 )
605- : max (right - menuView .getLeft (), 0 );
606- left -= diff ;
607- right -= diff ;
608- }
609- View navIcon = findOrGetNavView ();
610- if (navIcon != null ) {
611- int diff =
612- getLayoutDirection () == LAYOUT_DIRECTION_RTL
613- ? max (right - navIcon .getLeft (), 0 )
614- : max (navIcon .getRight () - left , 0 );
615- left += diff ;
616- right += diff ;
617- }
618661
619- // Make sure to not lay out the view inside the SearchBar padding. paddingLeftAdded and
620- // paddingRightAdded will never be non-zero at the same time, as Toolbar.measure has already
621- // measured the children accounting for padding.
622- int paddingStartAdded = max (getPaddingStart () - left , getContentInsetStart () - left );
623- int paddingEndAdded =
624- max (
625- right - (getMeasuredWidth () - getPaddingEnd ()),
626- right - (getMeasuredWidth () - getContentInsetEnd ()));
627- left += max (paddingStartAdded , 0 ) - max (paddingEndAdded , 0 );
628- right += max (paddingStartAdded , 0 ) - max (paddingEndAdded , 0 );
629- }
630662 int viewHeight = view .getMeasuredHeight ();
631663 int top = getMeasuredHeight () / 2 - viewHeight / 2 ;
632664 int bottom = top + viewHeight ;
@@ -690,6 +722,10 @@ public void setCenterView(@Nullable View view) {
690722 }
691723 }
692724
725+ TextView getPlaceholderTextView () {
726+ return placeholderTextView ;
727+ }
728+
693729 /** Returns the main {@link TextView} which can be used for hint and search text. */
694730 @ NonNull
695731 public TextView getTextView () {
@@ -705,26 +741,31 @@ public CharSequence getText() {
705741 /** Sets the text of main {@link TextView}. */
706742 public void setText (@ Nullable CharSequence text ) {
707743 textView .setText (text );
744+ placeholderTextView .setText (text );
708745 }
709746
710747 /** Sets the text of main {@link TextView}. */
711748 public void setText (@ StringRes int textResId ) {
712749 textView .setText (textResId );
750+ placeholderTextView .setText (textResId );
713751 }
714752
715- /** Whether or not to center the text. */
753+ /** Whether or not to center the text within the TextView . */
716754 public void setTextCentered (boolean textCentered ) {
717755 this .textCentered = textCentered ;
718756 if (textView == null ) {
719757 return ;
720758 }
721- Toolbar .LayoutParams lp = (LayoutParams ) textView .getLayoutParams ();
759+ FrameLayout .LayoutParams lp = (FrameLayout . LayoutParams ) textView .getLayoutParams ();
722760 if (textCentered ) {
761+ lp .gravity = Gravity .CENTER_HORIZONTAL ;
723762 textView .setGravity (Gravity .CENTER_HORIZONTAL );
724763 } else {
764+ lp .gravity = Gravity .NO_GRAVITY ;
725765 textView .setGravity (Gravity .NO_GRAVITY );
726766 }
727767 textView .setLayoutParams (lp );
768+ placeholderTextView .setLayoutParams (lp );
728769 }
729770
730771 /** Whether or not the text is centered. */
@@ -735,6 +776,7 @@ public boolean getTextCentered() {
735776 /** Clears the text of main {@link TextView}. */
736777 public void clearText () {
737778 textView .setText ("" );
779+ placeholderTextView .setText ("" );
738780 }
739781
740782 /** Returns the hint of main {@link TextView}. */
0 commit comments