diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml index 59d21203e..f4e77e037 100644 --- a/samples/ApiDemos/AndroidManifest.xml +++ b/samples/ApiDemos/AndroidManifest.xml @@ -1390,76 +1390,93 @@ - + - + - + - + - + - + - + - + - + - + + + + + + + diff --git a/samples/ApiDemos/res/drawable/box.xml b/samples/ApiDemos/res/drawable/box.xml index 825de8495..482648bbb 100644 --- a/samples/ApiDemos/res/drawable/box.xml +++ b/samples/ApiDemos/res/drawable/box.xml @@ -18,7 +18,7 @@ - + diff --git a/samples/ApiDemos/res/drawable/box_white.xml b/samples/ApiDemos/res/drawable/box_white.xml index 9b9b1921c..40b3cfc18 100644 --- a/samples/ApiDemos/res/drawable/box_white.xml +++ b/samples/ApiDemos/res/drawable/box_white.xml @@ -17,5 +17,5 @@ --> - + diff --git a/samples/ApiDemos/res/drawable/filled_box.xml b/samples/ApiDemos/res/drawable/filled_box.xml index 75d15a00b..f3839009f 100644 --- a/samples/ApiDemos/res/drawable/filled_box.xml +++ b/samples/ApiDemos/res/drawable/filled_box.xml @@ -18,7 +18,7 @@ - + diff --git a/samples/ApiDemos/res/drawable/hover_background_active.xml b/samples/ApiDemos/res/drawable/hover_background_active.xml index 8819ce213..0881491f6 100644 --- a/samples/ApiDemos/res/drawable/hover_background_active.xml +++ b/samples/ApiDemos/res/drawable/hover_background_active.xml @@ -16,7 +16,7 @@ - + diff --git a/samples/ApiDemos/res/drawable/hover_background_inactive.xml b/samples/ApiDemos/res/drawable/hover_background_inactive.xml index 5ddab3a24..0f6cb303f 100644 --- a/samples/ApiDemos/res/drawable/hover_background_inactive.xml +++ b/samples/ApiDemos/res/drawable/hover_background_inactive.xml @@ -16,7 +16,7 @@ - + diff --git a/samples/ApiDemos/res/layout/custom_layout.xml b/samples/ApiDemos/res/layout/custom_layout.xml new file mode 100644 index 000000000..ff3cfc4bc --- /dev/null +++ b/samples/ApiDemos/res/layout/custom_layout.xml @@ -0,0 +1,94 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/ApiDemos/res/values/attrs.xml b/samples/ApiDemos/res/values/attrs.xml index 35f224ef5..bf33788cb 100644 --- a/samples/ApiDemos/res/values/attrs.xml +++ b/samples/ApiDemos/res/values/attrs.xml @@ -33,6 +33,19 @@ + + + + + + + + + + + + diff --git a/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java b/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java new file mode 100644 index 000000000..dfd886a60 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/view/CustomLayout.java @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2013 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.example.android.apis.view; + +// Need the following import to get access to the app resources, since this +// class is in a sub-package. +import android.graphics.Rect; +import com.example.android.apis.R; + +//BEGIN_INCLUDE(Complete) +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 android.widget.RemoteViews; + +/** + * Example of writing a custom layout manager. This is a fairly full-featured + * layout manager that is relatively general, handling all layout cases. You + * can simplify it for more specific cases. + */ +@RemoteViews.RemoteView +public class CustomLayout extends ViewGroup { + /** The amount of space used by children in the left gutter. */ + private int mLeftWidth; + + /** The amount of space used by children in the right gutter. */ + private int mRightWidth; + + /** These are used for computing child frames based on their gravity. */ + private final Rect mTmpContainerRect = new Rect(); + private final Rect mTmpChildRect = new Rect(); + + public CustomLayout(Context context) { + super(context); + } + + public CustomLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public CustomLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + /** + * Any layout manager that doesn't scroll will want this. + */ + @Override + public boolean shouldDelayChildPressedState() { + return false; + } + + /** + * Ask all children to measure themselves and compute the measurement of this + * layout based on the children. + */ + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + int count = getChildCount(); + + // These keep track of the space we are using on the left and right for + // views positioned there; we need member variables so we can also use + // these for layout later. + mLeftWidth = 0; + mRightWidth = 0; + + // Measurement will ultimately be computing these values. + int maxHeight = 0; + int maxWidth = 0; + int childState = 0; + + // Iterate through all children, measuring them and computing our dimensions + // from their size. + for (int i = 0; i < count; i++) { + final View child = getChildAt(i); + if (child.getVisibility() != GONE) { + // Measure the child. + measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0); + + // Update our size information based on the layout params. Children + // that asked to be positioned on the left or right go in those gutters. + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.position == LayoutParams.POSITION_LEFT) { + mLeftWidth += Math.max(maxWidth, + child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); + } else if (lp.position == LayoutParams.POSITION_RIGHT) { + mRightWidth += Math.max(maxWidth, + child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin); + } else { + 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()); + } + } + + // Total width is the maximum width of all inner children plus the gutters. + maxWidth += mLeftWidth + mRightWidth; + + // Check against our minimum height and width + maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight()); + maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth()); + + // Report our final dimensions. + setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, childState), + resolveSizeAndState(maxHeight, heightMeasureSpec, + childState << MEASURED_HEIGHT_STATE_SHIFT)); + } + + /** + * Position all children within this layout. + */ + @Override + protected void onLayout(boolean changed, int left, int top, int right, int bottom) { + final int count = getChildCount(); + + // These are the far left and right edges in which we are performing layout. + int leftPos = getPaddingLeft(); + int rightPos = right - left - getPaddingRight(); + + // This is the middle region inside of the gutter. + final int middleLeft = leftPos + mLeftWidth; + final int middleRight = rightPos - mRightWidth; + + // These are the top and bottom edges in which we are performing layout. + final int parentTop = getPaddingTop(); + final int parentBottom = bottom - top - getPaddingBottom(); + + 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(); + + // Compute the frame in which we are placing this child. + if (lp.position == LayoutParams.POSITION_LEFT) { + mTmpContainerRect.left = leftPos + lp.leftMargin; + mTmpContainerRect.right = leftPos + width + lp.rightMargin; + leftPos = mTmpContainerRect.right; + } else if (lp.position == LayoutParams.POSITION_RIGHT) { + mTmpContainerRect.right = rightPos - lp.rightMargin; + mTmpContainerRect.left = rightPos - width - lp.leftMargin; + rightPos = mTmpContainerRect.left; + } else { + mTmpContainerRect.left = middleLeft + lp.leftMargin; + mTmpContainerRect.right = middleRight - lp.rightMargin; + } + mTmpContainerRect.top = parentTop + lp.topMargin; + mTmpContainerRect.bottom = parentBottom - lp.bottomMargin; + + // Use the child's gravity and size to determine its final + // frame within its container. + Gravity.apply(lp.gravity, width, height, mTmpContainerRect, mTmpChildRect); + + // Place the child. + child.layout(mTmpChildRect.left, mTmpChildRect.top, + mTmpChildRect.right, mTmpChildRect.bottom); + } + } + } + + // ---------------------------------------------------------------------- + // The rest of the implementation is for custom per-child layout parameters. + // If you do not need these (for example you are writing a layout manager + // that does fixed positioning of its children), you can drop all of this. + + @Override + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new CustomLayout.LayoutParams(getContext(), attrs); + } + + @Override + protected LayoutParams generateDefaultLayoutParams() { + return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); + } + + @Override + protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + return new LayoutParams(p); + } + + @Override + protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { + return p instanceof LayoutParams; + } + + /** + * Custom per-child layout information. + */ + public static class LayoutParams extends MarginLayoutParams { + /** + * The gravity to apply with the View to which these layout parameters + * are associated. + */ + public int gravity = Gravity.TOP | Gravity.START; + + public static int POSITION_MIDDLE = 0; + public static int POSITION_LEFT = 1; + public static int POSITION_RIGHT = 2; + + public int position = POSITION_MIDDLE; + + public LayoutParams(Context c, AttributeSet attrs) { + super(c, attrs); + + // Pull the layout param values from the layout XML during + // inflation. This is not needed if you don't care about + // changing the layout behavior in XML. + TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.CustomLayoutLP); + gravity = a.getInt(R.styleable.CustomLayoutLP_android_layout_gravity, gravity); + position = a.getInt(R.styleable.CustomLayoutLP_layout_position, position); + a.recycle(); + } + + public LayoutParams(int width, int height) { + super(width, height); + } + + public LayoutParams(ViewGroup.LayoutParams source) { + super(source); + } + } +} +//END_INCLUDE(Complete) diff --git a/samples/ApiDemos/src/com/example/android/apis/view/CustomLayoutActivity.java b/samples/ApiDemos/src/com/example/android/apis/view/CustomLayoutActivity.java new file mode 100644 index 000000000..171656933 --- /dev/null +++ b/samples/ApiDemos/src/com/example/android/apis/view/CustomLayoutActivity.java @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2013 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.example.android.apis.view; + +// Need the following import to get access to the app resources, since this +// class is in a sub-package. +import com.example.android.apis.R; + +import android.app.Activity; +import android.os.Bundle; + +public class CustomLayoutActivity extends Activity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.custom_layout); + } +}