4848import androidx .annotation .NonNull ;
4949import androidx .annotation .Nullable ;
5050import androidx .annotation .RequiresApi ;
51+ import androidx .annotation .StyleableRes ;
5152import com .google .android .material .resources .MaterialAttributes ;
5253import com .google .android .material .shape .MaterialShapeDrawable ;
5354import com .google .android .material .shape .ShapeAppearance ;
@@ -270,58 +271,238 @@ public void inflate(
270271 } else {
271272 a = res .obtainAttributes (attrs , R .styleable .FocusRingDrawable );
272273 }
273- updateStateFromTypedArray ( a , res , /* useDefaults= */ false );
274+ updateStateFromTypedArrayWithoutThemeAttrsOrDefaults ( a );
274275 a .recycle ();
275276
276277 inflateChildDrawable (res , parser , attrs , theme );
277278 }
278279
279- private void updateStateFromTypedArray (
280- @ NonNull TypedArray a , @ NonNull Resources res , boolean useDefaults ) {
281- if (state .ringOuterColor == Integer .MIN_VALUE ) {
282- int defaultOuterColor = useDefaults ? Color .BLACK : Integer .MIN_VALUE ;
280+ private void updateStateFromTypedArrayWithoutThemeAttrsOrDefaults (@ NonNull TypedArray a ) {
281+ state .ringEnabledAttr = getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsEnabled );
282+ if (state .ringEnabledAttr == Integer .MIN_VALUE
283+ && a .hasValue (R .styleable .FocusRingDrawable_focusRingsEnabled )) {
284+ state .ringEnabled =
285+ a .getBoolean (R .styleable .FocusRingDrawable_focusRingsEnabled , state .ringEnabled );
286+ state .ringEnabledInflated = true ;
287+ }
288+
289+ state .ringOuterColorAttr =
290+ getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsOuterStrokeColor );
291+ if (state .ringOuterColorAttr == Integer .MIN_VALUE ) {
283292 state .ringOuterColor =
284- a .getColor (R .styleable .FocusRingDrawable_focusRingsOuterStrokeColor , defaultOuterColor );
293+ a .getColor (R .styleable .FocusRingDrawable_focusRingsOuterStrokeColor , Integer . MIN_VALUE );
285294 }
286- if (state .ringInnerColor == Integer .MIN_VALUE ) {
287- int defaultInnerColor = useDefaults ? Color .WHITE : Integer .MIN_VALUE ;
295+
296+ state .ringInnerColorAttr =
297+ getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsInnerStrokeColor );
298+ if (state .ringInnerColorAttr == Integer .MIN_VALUE ) {
288299 state .ringInnerColor =
289- a .getColor (R .styleable .FocusRingDrawable_focusRingsInnerStrokeColor , defaultInnerColor );
290- }
291- if (DEBUG_COLORS ) {
292- state .ringOuterColor = Color .RED ;
293- state .ringInnerColor = Color .GREEN ;
300+ a .getColor (R .styleable .FocusRingDrawable_focusRingsInnerStrokeColor , Integer .MIN_VALUE );
294301 }
295- if (Float .isNaN (state .ringOuterStrokeWidth )) {
296- float defaultStrokeWidth =
297- useDefaults
298- ? res .getDimensionPixelSize (R .dimen .mtrl_focus_ring_outer_stroke_width )
299- : Float .NaN ;
302+
303+ state .ringOuterStrokeWidthAttr =
304+ getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsOuterStrokeWidth );
305+ if (state .ringOuterStrokeWidthAttr == Integer .MIN_VALUE ) {
300306 state .ringOuterStrokeWidth =
301- a .getDimension (
302- R .styleable .FocusRingDrawable_focusRingsOuterStrokeWidth , defaultStrokeWidth );
303- }
304- if (Float .isNaN (state .ringInnerStrokeWidth )) {
305- float defaultStrokeWidth =
306- useDefaults
307- ? res .getDimensionPixelSize (R .dimen .mtrl_focus_ring_outer_stroke_width )
308- : Float .NaN ;
307+ a .getDimension (R .styleable .FocusRingDrawable_focusRingsOuterStrokeWidth , Float .NaN );
308+ }
309+
310+ state .ringInnerStrokeWidthAttr =
311+ getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsInnerStrokeWidth );
312+ if (state .ringInnerStrokeWidthAttr == Integer .MIN_VALUE ) {
313+ state .ringInnerStrokeWidth =
314+ a .getDimension (R .styleable .FocusRingDrawable_focusRingsInnerStrokeWidth , Float .NaN );
315+ }
316+
317+ state .ringInnerStrokeWidthAttr =
318+ getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsInnerStrokeWidth );
319+ if (state .ringInnerStrokeWidthAttr == Integer .MIN_VALUE ) {
309320 state .ringInnerStrokeWidth =
310- a .getDimension (
311- R .styleable .FocusRingDrawable_focusRingsInnerStrokeWidth , defaultStrokeWidth );
321+ a .getDimension (R .styleable .FocusRingDrawable_focusRingsInnerStrokeWidth , Float .NaN );
312322 }
313- if (Float .isNaN (state .ringRadius )) {
323+
324+ state .ringRadiusAttr = getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsRadius );
325+ if (state .ringRadiusAttr == Integer .MIN_VALUE ) {
314326 state .ringRadius = a .getDimension (R .styleable .FocusRingDrawable_focusRingsRadius , Float .NaN );
315327 }
316- if (Float .isNaN (state .ringInset )) {
317- float defaultInset = useDefaults ? 0f : Float .NaN ;
318- state .ringInset = a .getDimension (R .styleable .FocusRingDrawable_focusRingsInset , defaultInset );
328+
329+ state .ringInsetAttr = getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsInset );
330+ if (state .ringInsetAttr == Integer .MIN_VALUE ) {
331+ state .ringInset = a .getDimension (R .styleable .FocusRingDrawable_focusRingsInset , Float .NaN );
319332 }
320- if (Float .isNaN (state .ringInnerInset )) {
321- float defaultInset = useDefaults ? 0f : Float .NaN ;
333+
334+ state .ringInnerInsetAttr =
335+ getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsInnerStrokeInset );
336+ if (state .ringInnerInsetAttr == Integer .MIN_VALUE ) {
322337 state .ringInnerInset =
323- a .getDimension (R .styleable .FocusRingDrawable_focusRingsInnerStrokeInset , defaultInset );
338+ a .getDimension (R .styleable .FocusRingDrawable_focusRingsInnerStrokeInset , Float .NaN );
339+ }
340+
341+ state .ringShapeAppearanceAttr =
342+ getValueDataIfAttr (a , R .styleable .FocusRingDrawable_focusRingsShapeAppearance );
343+ state .ringShapeAppearanceResId =
344+ getResIdIfReference (a , R .styleable .FocusRingDrawable_focusRingsShapeAppearance );
345+ }
346+
347+ private void updateStateFromTypedArrayWithThemeAttrsAndDefaults (
348+ @ NonNull TypedArray a , @ NonNull Theme theme ) {
349+ Resources res = theme .getResources ();
350+
351+ if (state .ringEnabledAttr != Integer .MIN_VALUE ) {
352+ TypedValue typedValue = MaterialAttributes .resolve (theme , state .ringEnabledAttr );
353+ if (typedValue != null ) {
354+ state .ringEnabled = typedValue .data != 0 ;
355+ state .ringEnabledInflated = true ;
356+ }
357+ }
358+ if (!state .ringEnabledInflated ) {
359+ state .ringEnabled =
360+ MaterialAttributes .resolveBoolean (theme , R .attr .focusRingsEnabled , state .ringEnabled );
361+ }
362+ if (!state .ringEnabled ) {
363+ return ;
364+ }
365+
366+ state .ringOuterColor =
367+ maybeResolveColor (
368+ state .ringOuterColor ,
369+ theme ,
370+ state .ringOuterColorAttr ,
371+ a ,
372+ R .styleable .FocusRingDrawable_focusRingsOuterStrokeColor ,
373+ Color .BLACK );
374+
375+ state .ringInnerColor =
376+ maybeResolveColor (
377+ state .ringInnerColor ,
378+ theme ,
379+ state .ringInnerColorAttr ,
380+ a ,
381+ R .styleable .FocusRingDrawable_focusRingsInnerStrokeColor ,
382+ Color .WHITE );
383+
384+ float defaultStrokeWidth =
385+ res .getDimensionPixelSize (R .dimen .mtrl_focus_ring_outer_stroke_width );
386+
387+ state .ringOuterStrokeWidth =
388+ maybeResolveDimension (
389+ state .ringOuterStrokeWidth ,
390+ theme ,
391+ state .ringOuterStrokeWidthAttr ,
392+ a ,
393+ R .styleable .FocusRingDrawable_focusRingsOuterStrokeWidth ,
394+ defaultStrokeWidth );
395+
396+ state .ringInnerStrokeWidth =
397+ maybeResolveDimension (
398+ state .ringInnerStrokeWidth ,
399+ theme ,
400+ state .ringInnerStrokeWidthAttr ,
401+ a ,
402+ R .styleable .FocusRingDrawable_focusRingsInnerStrokeWidth ,
403+ defaultStrokeWidth );
404+
405+ state .ringRadius =
406+ maybeResolveDimension (
407+ state .ringRadius ,
408+ theme ,
409+ state .ringRadiusAttr ,
410+ a ,
411+ R .styleable .FocusRingDrawable_focusRingsRadius ,
412+ Float .NaN );
413+
414+ state .ringInset =
415+ maybeResolveDimension (
416+ state .ringInset ,
417+ theme ,
418+ state .ringInsetAttr ,
419+ a ,
420+ R .styleable .FocusRingDrawable_focusRingsInset ,
421+ 0f );
422+
423+ state .ringInnerInset =
424+ maybeResolveDimension (
425+ state .ringInnerInset ,
426+ theme ,
427+ state .ringInnerInsetAttr ,
428+ a ,
429+ R .styleable .FocusRingDrawable_focusRingsInnerStrokeInset ,
430+ 0f );
431+
432+ if (state .ringShapeAppearanceResId != Integer .MIN_VALUE ) {
433+ state .ringShapeAppearance =
434+ ShapeAppearanceModel .builder (theme , state .ringShapeAppearanceResId ).build ();
435+ } else {
436+ int shapeAppearanceAttr =
437+ state .ringShapeAppearanceAttr != Integer .MIN_VALUE
438+ ? state .ringShapeAppearanceAttr
439+ : R .attr .focusRingsShapeAppearance ;
440+ TypedValue typedValue = MaterialAttributes .resolve (theme , shapeAppearanceAttr );
441+ if (typedValue != null ) {
442+ state .ringShapeAppearance =
443+ ShapeAppearanceModel .builder (theme , typedValue .resourceId ).build ();
444+ }
324445 }
446+
447+ if (DEBUG_COLORS ) {
448+ state .ringOuterColor = Color .RED ;
449+ state .ringInnerColor = Color .GREEN ;
450+ }
451+ }
452+
453+ private int getValueDataIfAttr (TypedArray a , @ StyleableRes int index ) {
454+ if (a .getType (index ) == TypedValue .TYPE_ATTRIBUTE ) {
455+ TypedValue value = new TypedValue ();
456+ if (a .getValue (index , value )) {
457+ return value .data ;
458+ }
459+ }
460+ return Integer .MIN_VALUE ;
461+ }
462+
463+ private int getResIdIfReference (TypedArray a , @ StyleableRes int index ) {
464+ if (a .getType (index ) == TypedValue .TYPE_REFERENCE ) {
465+ return a .getResourceId (index , Integer .MIN_VALUE );
466+ }
467+ return Integer .MIN_VALUE ;
468+ }
469+
470+ private int maybeResolveColor (
471+ int currentValue ,
472+ @ NonNull Theme theme ,
473+ @ StyleableRes int attrIndex ,
474+ @ NonNull TypedArray a ,
475+ @ StyleableRes int regularIndex ,
476+ int defaultValue ) {
477+ if (currentValue != Integer .MIN_VALUE ) {
478+ return currentValue ;
479+ }
480+ if (attrIndex != Integer .MIN_VALUE ) {
481+ TypedValue value = new TypedValue ();
482+ if (theme .resolveAttribute (attrIndex , value , true )) {
483+ return value .data ;
484+ }
485+ }
486+ return a .getColor (regularIndex , defaultValue );
487+ }
488+
489+ private float maybeResolveDimension (
490+ float currentValue ,
491+ @ NonNull Theme theme ,
492+ @ StyleableRes int attrIndex ,
493+ @ NonNull TypedArray a ,
494+ @ StyleableRes int regularIndex ,
495+ float defaultValue ) {
496+ if (!Float .isNaN (currentValue )) {
497+ return currentValue ;
498+ }
499+ if (attrIndex != Float .MIN_VALUE ) {
500+ TypedValue value = new TypedValue ();
501+ if (theme .resolveAttribute (attrIndex , value , true )) {
502+ return value .getDimension (theme .getResources ().getDisplayMetrics ());
503+ }
504+ }
505+ return a .getDimension (regularIndex , defaultValue );
325506 }
326507
327508 private void inflateChildDrawable (
@@ -356,29 +537,18 @@ private void init(@NonNull Theme theme) {
356537 return ;
357538 }
358539
359- state .ringEnabled = MaterialAttributes .resolveBoolean (theme , R .attr .focusRingsEnabled , false );
360-
361- if (!state .ringEnabled ) {
362- return ;
363- }
364-
365- // Shape appearance is currently only supported from theme / theme overlay.
366- TypedValue typedValue = MaterialAttributes .resolve (theme , R .attr .focusRingsShapeAppearance );
367- if (typedValue != null ) {
368- state .ringShapeAppearance =
369- ShapeAppearanceModel .builder (theme , typedValue .resourceId ).build ();
370- }
371-
372540 TypedArray a = theme .obtainStyledAttributes (R .styleable .FocusRingDrawable );
373- updateStateFromTypedArray (a , theme . getResources (), /* useDefaults= */ true );
541+ updateStateFromTypedArrayWithThemeAttrsAndDefaults (a , theme );
374542 a .recycle ();
375543
376544 updateLocalState ();
377545 }
378546
379547 private void updateLocalState () {
380548 paint .setStyle (Style .STROKE );
381- paint .setStrokeWidth (state .ringOuterStrokeWidth );
549+ if (!Float .isNaN (state .ringOuterStrokeWidth )) {
550+ paint .setStrokeWidth (state .ringOuterStrokeWidth );
551+ }
382552 }
383553
384554 @ Override
@@ -697,15 +867,25 @@ private static final class FocusRingState extends ConstantState {
697867 int mChangingConfigurations = 0 ;
698868
699869 private boolean ringEnabled = false ;
870+ private int ringEnabledAttr = Integer .MIN_VALUE ;
871+ private boolean ringEnabledInflated = false ;
700872 private int ringOuterColor = Integer .MIN_VALUE ;
873+ private int ringOuterColorAttr = Integer .MIN_VALUE ;
701874 private int ringInnerColor = Integer .MIN_VALUE ;
875+ private int ringInnerColorAttr = Integer .MIN_VALUE ;
702876 private float ringOuterStrokeWidth = Float .NaN ;
877+ private int ringOuterStrokeWidthAttr = Integer .MIN_VALUE ;
703878 private float ringInnerStrokeWidth = Float .NaN ;
879+ private int ringInnerStrokeWidthAttr = Integer .MIN_VALUE ;
704880 private float ringRadius = Float .NaN ;
881+ private int ringRadiusAttr = Integer .MIN_VALUE ;
705882 private float ringInset = Float .NaN ;
883+ private int ringInsetAttr = Integer .MIN_VALUE ;
706884 private float ringInnerInset = Float .NaN ;
885+ private int ringInnerInsetAttr = Integer .MIN_VALUE ;
707886 @ Nullable private ShapeAppearance ringShapeAppearance = null ;
708- private int ringBoundsMode = -1 ;
887+ private int ringShapeAppearanceResId = Integer .MIN_VALUE ;
888+ private int ringShapeAppearanceAttr = Integer .MIN_VALUE ;
709889 @ Nullable private Rect ringCustomBounds = null ;
710890
711891 FocusRingState (@ Nullable FocusRingState orig ) {
@@ -714,15 +894,25 @@ private static final class FocusRingState extends ConstantState {
714894 mChangingConfigurations = orig .mChangingConfigurations ;
715895
716896 this .ringEnabled = orig .ringEnabled ;
897+ this .ringEnabledAttr = orig .ringEnabledAttr ;
898+ this .ringEnabledInflated = orig .ringEnabledInflated ;
717899 this .ringOuterColor = orig .ringOuterColor ;
900+ this .ringOuterColorAttr = orig .ringOuterColorAttr ;
718901 this .ringInnerColor = orig .ringInnerColor ;
902+ this .ringInnerColorAttr = orig .ringInnerColorAttr ;
719903 this .ringOuterStrokeWidth = orig .ringOuterStrokeWidth ;
904+ this .ringOuterStrokeWidthAttr = orig .ringOuterStrokeWidthAttr ;
720905 this .ringInnerStrokeWidth = orig .ringInnerStrokeWidth ;
906+ this .ringInnerStrokeWidthAttr = orig .ringInnerStrokeWidthAttr ;
721907 this .ringRadius = orig .ringRadius ;
908+ this .ringRadiusAttr = orig .ringRadiusAttr ;
722909 this .ringInset = orig .ringInset ;
910+ this .ringInsetAttr = orig .ringInsetAttr ;
723911 this .ringInnerInset = orig .ringInnerInset ;
912+ this .ringInnerInsetAttr = orig .ringInnerInsetAttr ;
724913 this .ringShapeAppearance = orig .ringShapeAppearance ;
725- this .ringBoundsMode = orig .ringBoundsMode ;
914+ this .ringShapeAppearanceResId = orig .ringShapeAppearanceResId ;
915+ this .ringShapeAppearanceAttr = orig .ringShapeAppearanceAttr ;
726916 if (orig .ringCustomBounds != null ) {
727917 this .ringCustomBounds = new Rect (orig .ringCustomBounds );
728918 }
0 commit comments