Fix GalleryHeader
This commit is contained in:
1 parent
21ef5897ef
commit
06fd28290c
1 file changed
+151
-52
@@ -20,21 +20,30 @@ import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.DisplayCutout;
|
||||
import android.view.View;
|
||||
import android.widget.FrameLayout;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.view.ViewGroup;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import com.hippo.ehviewer.R;
|
||||
import com.hippo.yorozuya.ObjectUtils;
|
||||
|
||||
// Offset widgets to avoid notch
|
||||
public class GalleryHeader extends FrameLayout {
|
||||
public class GalleryHeader extends ViewGroup {
|
||||
|
||||
private DisplayCutout displayCutout;
|
||||
|
||||
private View battery;
|
||||
private View progress;
|
||||
private View clock;
|
||||
|
||||
private Rect batteryRect = new Rect();
|
||||
private Rect progressRect = new Rect();
|
||||
private Rect clockRect = new Rect();
|
||||
|
||||
private SparseIntArray originalTopMargins = new SparseIntArray();
|
||||
private DisplayCutout displayCutout = null;
|
||||
private int[] location = new int[2];
|
||||
private Rect rect = new Rect();
|
||||
|
||||
private int lastX = 0;
|
||||
private int lastY = 0;
|
||||
|
||||
public GalleryHeader(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
@@ -42,67 +51,157 @@ public class GalleryHeader extends FrameLayout {
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.P)
|
||||
public void setDisplayCutout(@Nullable DisplayCutout displayCutout) {
|
||||
// Backup top margins
|
||||
if (originalTopMargins.size() == 0) {
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
originalTopMargins.append(child.getId(), ((MarginLayoutParams) child.getLayoutParams()).topMargin);
|
||||
}
|
||||
}
|
||||
|
||||
if (displayCutout != null) {
|
||||
// Update top margins
|
||||
if (getWidth() == 0 && getHeight() == 0) {
|
||||
this.displayCutout = displayCutout;
|
||||
} else {
|
||||
updateTopMargins(displayCutout);
|
||||
}
|
||||
} else if (originalTopMargins.size() != 0) {
|
||||
// Reset
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
((MarginLayoutParams) child.getLayoutParams()).topMargin = originalTopMargins.get(child.getId());
|
||||
}
|
||||
// Layout
|
||||
if (!ObjectUtils.equal(this.displayCutout, displayCutout)) {
|
||||
this.displayCutout = displayCutout;
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
|
||||
if (displayCutout != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
updateTopMargins(displayCutout);
|
||||
displayCutout = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
battery = findViewById(R.id.battery);
|
||||
progress = findViewById(R.id.progress);
|
||||
clock = findViewById(R.id.clock);
|
||||
}
|
||||
}
|
||||
|
||||
private void measureChild(Rect rect, View view, int width, int paddingLeft, int paddingRight) {
|
||||
int left;
|
||||
MarginLayoutParams lp = (MarginLayoutParams) view.getLayoutParams();
|
||||
if (view == battery) {
|
||||
left = paddingLeft + lp.leftMargin;
|
||||
} else if (view == progress) {
|
||||
left = paddingLeft + (width - paddingLeft - paddingRight) / 2 - view.getMeasuredWidth() / 2;
|
||||
} else {
|
||||
left = width - paddingRight - lp.rightMargin - view.getMeasuredWidth();
|
||||
}
|
||||
rect.set(left, lp.topMargin, left + view.getMeasuredWidth(), lp.topMargin + view.getMeasuredHeight());
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.P)
|
||||
private boolean offsetVertically(Rect rect, View view, int width) {
|
||||
int offset = 0;
|
||||
|
||||
measureChild(rect, view, width, 0, 0);
|
||||
rect.offset(lastX, lastY);
|
||||
|
||||
for (Rect notch : displayCutout.getBoundingRects()) {
|
||||
if (Rect.intersects(notch, rect)) {
|
||||
offset = Math.max(offset, notch.bottom - lastY);
|
||||
}
|
||||
}
|
||||
|
||||
if (offset != 0) {
|
||||
rect.offset(-lastX, -lastY);
|
||||
rect.offset(0, offset);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.P)
|
||||
private void updateTopMargins(@NonNull DisplayCutout displayCutout) {
|
||||
getLocationOnScreen(location);
|
||||
int y = location[1];
|
||||
private int getOffsetLeft(Rect rect, View view, int width) {
|
||||
int offset = 0;
|
||||
|
||||
measureChild(rect, view, width, 0, 0);
|
||||
rect.offset(lastX, lastY);
|
||||
|
||||
for (Rect notch : displayCutout.getBoundingRects()) {
|
||||
if (Rect.intersects(notch, rect)) {
|
||||
offset = Math.max(offset, notch.right - lastX);
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.P)
|
||||
private int getOffsetRight(Rect rect, View view, int width) {
|
||||
int offset = 0;
|
||||
|
||||
measureChild(rect, view, width, 0, 0);
|
||||
rect.offset(lastX, lastY);
|
||||
|
||||
for (Rect notch : displayCutout.getBoundingRects()) {
|
||||
if (Rect.intersects(notch, rect)) {
|
||||
offset = Math.max(offset, lastX + width - notch.left);
|
||||
}
|
||||
}
|
||||
|
||||
return offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
if (MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||
|
||||
int height = 0;
|
||||
for (int i = 0; i < getChildCount(); i++) {
|
||||
View child = getChildAt(i);
|
||||
child.getLocationOnScreen(location);
|
||||
measureChild(child, widthMeasureSpec, heightMeasureSpec);
|
||||
MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
|
||||
height = Math.max(height, child.getMeasuredHeight() + lp.topMargin);
|
||||
}
|
||||
|
||||
// Fix top margin
|
||||
boolean intersected = false;
|
||||
for (Rect notch : displayCutout.getBoundingRects()) {
|
||||
rect.set(location[0], location[1], location[0] + child.getWidth(), location[1] + child.getHeight());
|
||||
if (Rect.intersects(notch, rect)) {
|
||||
intersected = true;
|
||||
((MarginLayoutParams) child.getLayoutParams()).topMargin = originalTopMargins.get(child.getId()) + (notch.bottom - y);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!intersected) {
|
||||
((MarginLayoutParams) child.getLayoutParams()).topMargin = originalTopMargins.get(child.getId());
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && displayCutout != null) {
|
||||
// Check progress covered
|
||||
if (offsetVertically(progressRect, progress, width)) {
|
||||
offsetVertically(batteryRect, battery, width);
|
||||
offsetVertically(clockRect, clock, width);
|
||||
height = Math.max(progressRect.bottom, Math.max(batteryRect.bottom, clockRect.bottom));
|
||||
} else {
|
||||
// Clamp left and right
|
||||
int paddingLeft = getOffsetLeft(batteryRect, battery, width);
|
||||
int paddingRight = getOffsetRight(clockRect, clock, width);
|
||||
measureChild(batteryRect, battery, width, paddingLeft, paddingRight);
|
||||
measureChild(progressRect, progress, width, paddingLeft, paddingRight);
|
||||
measureChild(clockRect, clock, width, paddingLeft, paddingRight);
|
||||
}
|
||||
} else {
|
||||
measureChild(batteryRect, battery, width, 0, 0);
|
||||
measureChild(progressRect, progress, width, 0, 0);
|
||||
measureChild(clockRect, clock, width, 0, 0);
|
||||
}
|
||||
|
||||
// Layout
|
||||
setMeasuredDimension(width, height);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||
battery.layout(batteryRect.left, batteryRect.top, batteryRect.right, batteryRect.bottom);
|
||||
progress.layout(progressRect.left, progressRect.top, progressRect.right, progressRect.bottom);
|
||||
clock.layout(clockRect.left, clockRect.top, clockRect.right, clockRect.bottom);
|
||||
|
||||
getLocationOnScreen(location);
|
||||
if (lastX != location[0] || lastY != location[1]) {
|
||||
lastX = location[0];
|
||||
lastY = location[1];
|
||||
requestLayout();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarginLayoutParams generateLayoutParams(AttributeSet attrs) {
|
||||
return new MarginLayoutParams(getContext(), attrs);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
|
||||
return p instanceof MarginLayoutParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MarginLayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
|
||||
if (lp instanceof MarginLayoutParams) {
|
||||
return new MarginLayoutParams((MarginLayoutParams) lp);
|
||||
}
|
||||
return new MarginLayoutParams(lp);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user