FrameLayout: replace with AOSPs implementation

There are many Widgets extending FrameLayout and adding custom behaviour
on top. For example NavigationView. This didn't realy work with our
custom implementation
This commit is contained in:
Julian Winkler
2023-11-08 18:13:47 +01:00
parent 6ef1e523cc
commit d025fd3ce3
5 changed files with 357 additions and 321 deletions

View File

@@ -34,6 +34,12 @@ public class ViewGroup extends View implements ViewParent, ViewManager {
children = new ArrayList<View>();
}
public ViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr);
children = new ArrayList<View>();
}
public ViewGroup(int _id) { // FIXME
children = new ArrayList<View>();

View File

@@ -1,58 +1,386 @@
/*
* Copyright (C) 2006 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 android.widget;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import com.android.internal.R;
import java.util.ArrayList;
/**
* FrameLayout is designed to block out an area on the screen to display
* a single item. Generally, FrameLayout should be used to hold a single child view, because it can
* be difficult to organize child views in a way that's scalable to different screen sizes without
* the children overlapping each other. You can, however, add multiple children to a FrameLayout
* and control their position within the FrameLayout by assigning gravity to each child, using the
* <a href="FrameLayout.LayoutParams.html#attr_android:layout_gravity">{@code
* android:layout_gravity}</a> attribute.
* <p>Child views are drawn in a stack, with the most recently added child on top.
* The size of the FrameLayout is the size of its largest child (plus padding), visible
* or not (if the FrameLayout's parent permits). Views that are {@link android.view.View#GONE} are
* used for sizing
* only if {@link #setMeasureAllChildren(boolean) setConsiderGoneChildrenWhenMeasuring()}
* is set to true.
*
* @attr ref android.R.styleable#FrameLayout_measureAllChildren
*/
public class FrameLayout extends ViewGroup {
public FrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
private static final int DEFAULT_CHILD_GRAVITY = Gravity.TOP | Gravity.START;
boolean mMeasureAllChildren = false;
// private int mForegroundPaddingLeft = 0;
// private int mForegroundPaddingTop = 0;
// private int mForegroundPaddingRight = 0;
// private int mForegroundPaddingBottom = 0;
private final ArrayList<View> mMatchParentChildren = new ArrayList<>(1);
public FrameLayout(Context context) {
super(context);
}
public FrameLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
public FrameLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
@Override
protected native long native_constructor(Context context, AttributeSet attrs);
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
public FrameLayout(Context context, AttributeSet attrs,
int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public FrameLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.FrameLayout, defStyleAttr, defStyleRes);
// saveAttributeDataForStyleable(context, R.styleable.FrameLayout,
// attrs, a, defStyleAttr, defStyleRes);
if (a.getBoolean(R.styleable.FrameLayout_measureAllChildren, false)) {
setMeasureAllChildren(true);
}
a.recycle();
}
/**
* Returns a set of layout parameters with a width of
* {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT},
* and a height of {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}.
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
}
public static class LayoutParams extends ViewGroup.MarginLayoutParams {
public LayoutParams (Context c, AttributeSet attrs) {
int getPaddingLeftWithForeground() {
// return isForegroundInsidePadding() ? Math.max(mPaddingLeft, mForegroundPaddingLeft) :
// mPaddingLeft + mForegroundPaddingLeft;
return 0;
}
int getPaddingRightWithForeground() {
// return isForegroundInsidePadding() ? Math.max(mPaddingRight, mForegroundPaddingRight) :
// mPaddingRight + mForegroundPaddingRight;
return 0;
}
private int getPaddingTopWithForeground() {
// return isForegroundInsidePadding() ? Math.max(mPaddingTop, mForegroundPaddingTop) :
// mPaddingTop + mForegroundPaddingTop;
return 0;
}
private int getPaddingBottomWithForeground() {
// return isForegroundInsidePadding() ? Math.max(mPaddingBottom, mForegroundPaddingBottom) :
// mPaddingBottom + mForegroundPaddingBottom;
return 0;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int count = getChildCount();
final boolean measureMatchParentChildren =
MeasureSpec.getMode(widthMeasureSpec) != MeasureSpec.EXACTLY ||
MeasureSpec.getMode(heightMeasureSpec) != MeasureSpec.EXACTLY;
mMatchParentChildren.clear();
int maxHeight = 0;
int maxWidth = 0;
int childState = 0;
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (mMeasureAllChildren || child.getVisibility() != GONE) {
measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
maxWidth = Math.max(maxWidth,
child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
maxHeight = Math.max(maxHeight,
child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
childState = combineMeasuredStates(childState, child.getMeasuredState());
if (measureMatchParentChildren) {
if (lp.width == LayoutParams.MATCH_PARENT ||
lp.height == LayoutParams.MATCH_PARENT) {
mMatchParentChildren.add(child);
}
}
}
}
// Account for padding too
maxWidth += getPaddingLeftWithForeground() + getPaddingRightWithForeground();
maxHeight += getPaddingTopWithForeground() + getPaddingBottomWithForeground();
// Check against our minimum height and width
maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
// Check against our foreground's minimum height and width
// final Drawable drawable = getForeground();
// if (drawable != null) {
// maxHeight = Math.max(maxHeight, drawable.getMinimumHeight());
// maxWidth = Math.max(maxWidth, drawable.getMinimumWidth());
// }
setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState),
resolveSizeAndState(maxHeight, heightMeasureSpec,
childState << MEASURED_HEIGHT_STATE_SHIFT));
count = mMatchParentChildren.size();
if (count > 1) {
for (int i = 0; i < count; i++) {
final View child = mMatchParentChildren.get(i);
final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
final int childWidthMeasureSpec;
if (lp.width == LayoutParams.MATCH_PARENT) {
final int width = Math.max(0, getMeasuredWidth()
- getPaddingLeftWithForeground() - getPaddingRightWithForeground()
- lp.leftMargin - lp.rightMargin);
childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(
width, MeasureSpec.EXACTLY);
} else {
childWidthMeasureSpec = getChildMeasureSpec(widthMeasureSpec,
getPaddingLeftWithForeground() + getPaddingRightWithForeground() +
lp.leftMargin + lp.rightMargin,
lp.width);
}
final int childHeightMeasureSpec;
if (lp.height == LayoutParams.MATCH_PARENT) {
final int height = Math.max(0, getMeasuredHeight()
- getPaddingTopWithForeground() - getPaddingBottomWithForeground()
- lp.topMargin - lp.bottomMargin);
childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(
height, MeasureSpec.EXACTLY);
} else {
childHeightMeasureSpec = getChildMeasureSpec(heightMeasureSpec,
getPaddingTopWithForeground() + getPaddingBottomWithForeground() +
lp.topMargin + lp.bottomMargin,
lp.height);
}
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
layoutChildren(left, top, right, bottom, false /* no force left gravity */);
}
void layoutChildren(int left, int top, int right, int bottom, boolean forceLeftGravity) {
final int count = getChildCount();
final int parentLeft = getPaddingLeftWithForeground();
final int parentRight = right - left - getPaddingRightWithForeground();
final int parentTop = getPaddingTopWithForeground();
final int parentBottom = bottom - top - getPaddingBottomWithForeground();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final int width = child.getMeasuredWidth();
final int height = child.getMeasuredHeight();
int childLeft;
int childTop;
int gravity = lp.gravity;
if (gravity == -1) {
gravity = DEFAULT_CHILD_GRAVITY;
}
final int layoutDirection = getLayoutDirection();
final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection);
final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK;
switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
case Gravity.CENTER_HORIZONTAL:
childLeft = parentLeft + (parentRight - parentLeft - width) / 2 +
lp.leftMargin - lp.rightMargin;
break;
case Gravity.RIGHT:
if (!forceLeftGravity) {
childLeft = parentRight - width - lp.rightMargin;
break;
}
case Gravity.LEFT:
default:
childLeft = parentLeft + lp.leftMargin;
}
switch (verticalGravity) {
case Gravity.TOP:
childTop = parentTop + lp.topMargin;
break;
case Gravity.CENTER_VERTICAL:
childTop = parentTop + (parentBottom - parentTop - height) / 2 +
lp.topMargin - lp.bottomMargin;
break;
case Gravity.BOTTOM:
childTop = parentBottom - height - lp.bottomMargin;
break;
default:
childTop = parentTop + lp.topMargin;
}
child.layout(childLeft, childTop, childLeft + width, childTop + height);
}
}
}
/**
* Sets whether to consider all children, or just those in
* the VISIBLE or INVISIBLE state, when measuring. Defaults to false.
*
* @param measureAll true to consider children marked GONE, false otherwise.
* Default value is false.
*
* @attr ref android.R.styleable#FrameLayout_measureAllChildren
*/
public void setMeasureAllChildren(boolean measureAll) {
mMeasureAllChildren = measureAll;
}
/**
* Determines whether all children, or just those in the VISIBLE or
* INVISIBLE state, are considered when measuring.
*
* @return Whether all children are considered when measuring.
*
* @deprecated This method is deprecated in favor of
* {@link #getMeasureAllChildren() getMeasureAllChildren()}, which was
* renamed for consistency with
* {@link #setMeasureAllChildren(boolean) setMeasureAllChildren()}.
*/
@Deprecated
public boolean getConsiderGoneChildrenWhenMeasuring() {
return getMeasureAllChildren();
}
/**
* Determines whether all children, or just those in the VISIBLE or
* INVISIBLE state, are considered when measuring.
*
* @return Whether all children are considered when measuring.
*/
public boolean getMeasureAllChildren() {
return mMeasureAllChildren;
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new FrameLayout.LayoutParams(getContext(), attrs);
}
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return p instanceof LayoutParams;
}
@Override
protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
// if (sPreserveMarginParamsInLayoutParamConversion) {
// if (lp instanceof LayoutParams) {
// return new LayoutParams((LayoutParams) lp);
// } else if (lp instanceof MarginLayoutParams) {
// return new LayoutParams((MarginLayoutParams) lp);
// }
// }
return new LayoutParams(lp);
}
/**
* Per-child layout information for layouts that support margins.
* See {@link android.R.styleable#FrameLayout_Layout FrameLayout Layout Attributes}
* for a list of all child view attributes that this class supports.
*
* @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
*/
public static class LayoutParams extends MarginLayoutParams {
/**
* Value for {@link #gravity} indicating that a gravity has not been
* explicitly specified.
*/
public static final int UNSPECIFIED_GRAVITY = -1;
/**
* The gravity to apply with the View to which these layout parameters
* are associated.
* <p>
* The default value is {@link #UNSPECIFIED_GRAVITY}, which is treated
* by FrameLayout as {@code Gravity.TOP | Gravity.START}.
*
* @see android.view.Gravity
* @attr ref android.R.styleable#FrameLayout_Layout_layout_gravity
*/
public int gravity = UNSPECIFIED_GRAVITY;
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
final TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.FrameLayout_Layout);
gravity = a.getInt(R.styleable.FrameLayout_Layout_layout_gravity, UNSPECIFIED_GRAVITY);
a.recycle();
}
public LayoutParams(int width, int height) {
super(width, height);
}
public LayoutParams(int width, int height, float weight) {
super(width, height, weight);
}
/**
* Creates a new set of layout parameters with the specified width, height
* and gravity.
*
* @param width the width, either {@link #MATCH_PARENT},
* {@link #WRAP_CONTENT} or a fixed size in pixels
* @param height the height, either {@link #MATCH_PARENT},
* {@link #WRAP_CONTENT} or a fixed size in pixels
* @param gravity the gravity
*
* @see android.view.Gravity
*/
public LayoutParams(int width, int height, int gravity) {
this.width = width;
this.height = height;
super(width, height);
this.gravity = gravity;
}
public LayoutParams (ViewGroup.LayoutParams params) {
this.width = params.width;
this.height = params.height;
public LayoutParams(ViewGroup.LayoutParams source) {
super(source);
}
public LayoutParams(ViewGroup.MarginLayoutParams source) {
super(source);
}
/**
* Copy constructor. Clones the width, height, margin values, and
* gravity of the source.
*
* @param source The layout params to copy from.
*/
public LayoutParams(LayoutParams source) {
super(source);
this.gravity = source.gravity;
}
}
}