Fix GalleryHeader

This commit is contained in:
seven332 committed 2019-06-14 20:33:41 +08:00
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);
}
}