mirror of
https://github.com/material-components/material-components-android.git
synced 2026-01-19 03:21:36 +08:00
Will follow-up with TabLayout absolute elevation separately Resolves https://github.com/material-components/material-components-android/issues/381 PiperOrigin-RevId: 255404830
185 lines
6.3 KiB
Java
185 lines
6.3 KiB
Java
/*
|
|
* Copyright (C) 2015 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.google.android.material.internal;
|
|
|
|
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
|
|
|
|
import android.content.Context;
|
|
import android.content.res.Resources;
|
|
import android.graphics.PorterDuff;
|
|
import androidx.annotation.Dimension;
|
|
import androidx.annotation.RestrictTo;
|
|
import androidx.core.view.ViewCompat;
|
|
import androidx.core.view.WindowInsetsCompat;
|
|
import android.util.TypedValue;
|
|
import android.view.View;
|
|
import android.view.View.OnAttachStateChangeListener;
|
|
import android.view.ViewParent;
|
|
import android.view.WindowInsets;
|
|
import android.view.inputmethod.InputMethodManager;
|
|
|
|
/**
|
|
* Utils class for custom views.
|
|
*
|
|
* @hide
|
|
*/
|
|
@RestrictTo(LIBRARY_GROUP)
|
|
public class ViewUtils {
|
|
|
|
private ViewUtils() {}
|
|
|
|
public static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) {
|
|
switch (value) {
|
|
case 3:
|
|
return PorterDuff.Mode.SRC_OVER;
|
|
case 5:
|
|
return PorterDuff.Mode.SRC_IN;
|
|
case 9:
|
|
return PorterDuff.Mode.SRC_ATOP;
|
|
case 14:
|
|
return PorterDuff.Mode.MULTIPLY;
|
|
case 15:
|
|
return PorterDuff.Mode.SCREEN;
|
|
case 16:
|
|
return PorterDuff.Mode.ADD;
|
|
default:
|
|
return defaultMode;
|
|
}
|
|
}
|
|
|
|
public static boolean isLayoutRtl(View view) {
|
|
return ViewCompat.getLayoutDirection(view) == ViewCompat.LAYOUT_DIRECTION_RTL;
|
|
}
|
|
|
|
public static float dpToPx(Context context, @Dimension(unit = Dimension.DP) int dp) {
|
|
Resources r = context.getResources();
|
|
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics());
|
|
}
|
|
|
|
public static void requestFocusAndShowKeyboard(final View view) {
|
|
view.requestFocus();
|
|
view.post(
|
|
() -> {
|
|
InputMethodManager inputMethodManager =
|
|
(InputMethodManager) view.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
|
|
inputMethodManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT);
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Wrapper around {@link androidx.core.view.OnApplyWindowInsetsListener} which also passes
|
|
* the initial padding set on the view. Used with {@link #doOnApplyWindowInsets(View,
|
|
* OnApplyWindowInsetsListener)}.
|
|
*/
|
|
public interface OnApplyWindowInsetsListener {
|
|
|
|
/**
|
|
* When {@link View#setOnApplyWindowInsetsListener(View.OnApplyWindowInsetsListener) set} on a
|
|
* View, this listener method will be called instead of the view's own {@link
|
|
* View#onApplyWindowInsets(WindowInsets)} method. The {@code initialPadding} is the view's
|
|
* original padding which can be updated and will be applied to the view automatically. This
|
|
* method should return a new {@link WindowInsetsCompat} with any insets consumed.
|
|
*/
|
|
WindowInsetsCompat onApplyWindowInsets(
|
|
View view, WindowInsetsCompat insets, RelativePadding initialPadding);
|
|
}
|
|
|
|
/** Simple data object to store the initial padding for a view. */
|
|
public static class RelativePadding {
|
|
public int start;
|
|
public int top;
|
|
public int end;
|
|
public int bottom;
|
|
|
|
public RelativePadding(int start, int top, int end, int bottom) {
|
|
this.start = start;
|
|
this.top = top;
|
|
this.end = end;
|
|
this.bottom = bottom;
|
|
}
|
|
|
|
public RelativePadding(RelativePadding other) {
|
|
this.start = other.start;
|
|
this.top = other.top;
|
|
this.end = other.end;
|
|
this.bottom = other.bottom;
|
|
}
|
|
|
|
/** Applies this relative padding to the view. */
|
|
public void applyToView(View view) {
|
|
ViewCompat.setPaddingRelative(view, start, top, end, bottom);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Wrapper around {@link androidx.core.view.OnApplyWindowInsetsListener} that records the
|
|
* initial padding of the view and requests that insets are applied when attached.
|
|
*/
|
|
public static void doOnApplyWindowInsets(View view, final OnApplyWindowInsetsListener listener) {
|
|
// Create a snapshot of the view's padding state.
|
|
final RelativePadding initialPadding =
|
|
new RelativePadding(
|
|
ViewCompat.getPaddingStart(view),
|
|
view.getPaddingTop(),
|
|
ViewCompat.getPaddingEnd(view),
|
|
view.getPaddingBottom());
|
|
// Set an actual OnApplyWindowInsetsListener which proxies to the given callback, also passing
|
|
// in the original padding state.
|
|
ViewCompat.setOnApplyWindowInsetsListener(
|
|
view,
|
|
(insetsView, insets) ->
|
|
listener.onApplyWindowInsets(insetsView, insets, new RelativePadding(initialPadding)));
|
|
// Request some insets.
|
|
requestApplyInsetsWhenAttached(view);
|
|
}
|
|
|
|
/** Requests that insets should be applied to this view once it is attached. */
|
|
public static void requestApplyInsetsWhenAttached(View view) {
|
|
if (ViewCompat.isAttachedToWindow(view)) {
|
|
// We're already attached, just request as normal.
|
|
ViewCompat.requestApplyInsets(view);
|
|
} else {
|
|
// We're not attached to the hierarchy, add a listener to request when we are.
|
|
view.addOnAttachStateChangeListener(
|
|
new OnAttachStateChangeListener() {
|
|
@Override
|
|
public void onViewAttachedToWindow(View v) {
|
|
v.removeOnAttachStateChangeListener(this);
|
|
ViewCompat.requestApplyInsets(v);
|
|
}
|
|
|
|
@Override
|
|
public void onViewDetachedFromWindow(View v) {}
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the absolute elevation of the parent of the provided {@code view}, or in other words,
|
|
* the sum of the elevations of all ancestors of the {@code view}.
|
|
*/
|
|
public static float getParentAbsoluteElevation(View view) {
|
|
float absoluteElevation = 0;
|
|
ViewParent viewParent = view.getParent();
|
|
while (viewParent instanceof View) {
|
|
absoluteElevation += ViewCompat.getElevation((View) viewParent);
|
|
viewParent = viewParent.getParent();
|
|
}
|
|
return absoluteElevation;
|
|
}
|
|
}
|