/* Copyright (c) 2002-2011 by XMLVM.org * * Project Info: http://www.xmlvm.org * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2.1 of the License, or * (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA. */ package android.view; import java.util.ArrayList; import java.util.List; import java.util.Map; import android.app.Activity; import android.content.Context; import android.graphics.RectF; import android.internal.Assert; import android.internal.Dimension; import android.util.AttributeSet; import android.util.DisplayMetrics; /** * iPhone implementation of Android's ViewGroup class. * * @see http://developer.android.com/reference/android/view/ViewGroup.html */ public class ViewGroup extends View implements ViewParent { private List<View> subViews; private Map<Integer, View> xmlvmViewMap; public static class LayoutParams { public static final int FILL_PARENT = -1; public static final int WRAP_CONTENT = -2; public int width; public int height; public LayoutParams() { } public LayoutParams(Context context, AttributeSet attrs) { width = sizeFromString(context, attrs.getAttributeValue(null, "layout_width"), 0); height = sizeFromString(context, attrs.getAttributeValue(null, "layout_height"), 0); } public LayoutParams(int width, int height) { this.width = width; this.height = height; } public LayoutParams(LayoutParams source) { this.width = source.width; this.height = source.height; } private int sizeFromString(Context context, String str, int defaultValue) { if (str == null || str.length() == 0) { return defaultValue; } else if (str.equalsIgnoreCase("wrap_content")) { return WRAP_CONTENT; } else if (str.equalsIgnoreCase("fill_parent")) { return FILL_PARENT; } else { DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); return (int) Dimension.resolveDimension(context, str, metrics); } } } public static class MarginLayoutParams extends LayoutParams { public int bottomMargin; public int leftMargin; public int rightMargin; public int topMargin; public MarginLayoutParams(Context context, AttributeSet attrs) { super(context, attrs); DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); int margin = (int) Dimension.resolveDimension(context, attrs.getAttributeValue(null, "layout_margin"), metrics); margin = margin < 0 ? 0 : margin; setMargins(margin, margin, margin, margin); margin = (int) Dimension.resolveDimension(context, attrs.getAttributeValue(null, "layout_marginBottom"), metrics); bottomMargin = margin > 0 ? margin : bottomMargin; margin = (int) Dimension.resolveDimension(context, attrs.getAttributeValue(null, "layout_marginLeft"), metrics); leftMargin = margin > 0 ? margin : leftMargin; margin = (int) Dimension.resolveDimension(context, attrs.getAttributeValue(null, "layout_marginRight"), metrics); rightMargin = margin > 0 ? margin : rightMargin; margin = (int) Dimension.resolveDimension(context, attrs.getAttributeValue(null, "layout_marginTop"), metrics); topMargin = margin > 0 ? margin : topMargin; } public MarginLayoutParams(int width, int height) { super(width, height); } public MarginLayoutParams(LayoutParams source) { super(source); } public MarginLayoutParams(MarginLayoutParams source) { this.width = source.width; this.height = source.height; this.leftMargin = source.leftMargin; this.topMargin = source.topMargin; this.rightMargin = source.rightMargin; this.bottomMargin = source.bottomMargin; } public void setMargins(int leftMargin, int topMargin, int rightMargin, int bottomMargin) { this.leftMargin = leftMargin; this.topMargin = topMargin; this.rightMargin = rightMargin; this.bottomMargin = bottomMargin; } } public static final int PERSISTENT_ANIMATION_CACHE = 1; public ViewGroup(Context c) { super(c); initViewGroup(c, null); } public ViewGroup(Context c, AttributeSet attrs) { super(c, attrs); initViewGroup(c, attrs); } public ViewGroup(Context context, AttributeSet attrs, int defStyle) { this(context, attrs); } private void initViewGroup(Context c, AttributeSet attrs) { this.subViews = new ArrayList<View>(); if (c instanceof Activity) { RectF rect = ((Activity) c).getWindow().xmlvmGetRect(); getCommonView().setFrame(rect); } if (attrs != null && attrs.getAttributeCount() > 0) { parseViewGroupAttributes(attrs); } } public void addView(View child) { subViews.add(child); child.xmlvmSetParent(this); getCommonView().addSubview(child.getCommonView()); } public void addView(View child, LayoutParams p) { this.addView(child); child.setLayoutParams(p); } public void addView(View child, int idx) { subViews.add(idx, child); child.xmlvmSetParent(this); getCommonView().insertSubview(child.getCommonView(), idx); } public void removeView(View child) { subViews.remove(child); child.xmlvmSetParent(null); child.getCommonView().removeFromSuperview(); } public void removeAllViews() { while (subViews.size() > 0) { View view = subViews.get(0); removeView(view); } } public int getChildCount() { return subViews.size(); } public View getChildAt(int index) { return subViews.get(index); } protected boolean checkLayoutParams(ViewGroup.LayoutParams p) { return p != null; } /** * Returns a set of default layout parameters. These parameters are * requested when the View passed to {@link #addView(View)} has no layout * parameters already set. If null is returned, an exception is thrown from * addView. * * @return a set of default layout parameters or null */ protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } /** * Returns a safe set of layout parameters based on the supplied layout * params. When a ViewGroup is passed a View whose layout params do not pass * the test of * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this * method is invoked. This method should return a new set of layout params * suitable for this ViewGroup, possibly by copying the appropriate * attributes from the specified set of layout params. * * @param p * The layout parameters to convert into a suitable set of layout * parameters for this ViewGroup. * * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one * of its descendants */ protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return p; } public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { return new LayoutParams(getContext(), attrs); } private void parseViewGroupAttributes(AttributeSet attrs) { setIgnoreRequestLayout(true); // Implementation of attribute parsing setIgnoreRequestLayout(false); } // public View findViewById(int id) { // return xmlvmViewMap.get(new Integer(id)); // } @Override protected View findViewTraversal(int id) { if (id == getId()) { return this; } for (View v : subViews) { v = v.findViewById(id); if (v != null) { return v; } } return null; } public Map<Integer, View> getXmlvmViewMap() { return xmlvmViewMap; } public void setXmlvmViewMap(Map<Integer, View> xmlvmViewMap) { this.xmlvmViewMap = xmlvmViewMap; } /** * Ask one of the children of this view to measure itself, taking into * account both the MeasureSpec requirements for this view and its padding * and margins. The child must have MarginLayoutParams The heavy lifting is * done in getChildMeasureSpec. * * @param child * The child to measure * @param parentWidthMeasureSpec * The width requirements for this view * @param widthUsed * Extra space that has been used up by the parent horizontally * (possibly by other children of the parent) * @param parentHeightMeasureSpec * The height requirements for this view * @param heightUsed * Extra space that has been used up by the parent vertically * (possibly by other children of the parent) */ protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed, int parentHeightMeasureSpec, int heightUsed) { final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec, getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin + widthUsed, lp.width); final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec, getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin + heightUsed, lp.height); child.measure(childWidthMeasureSpec, childHeightMeasureSpec); } /** * Does the hard part of measureChildren: figuring out the MeasureSpec to * pass to a particular child. This method figures out the right MeasureSpec * for one dimension (height or width) of one child view. * * The goal is to combine information from our MeasureSpec with the * LayoutParams of the child to get the best possible results. For example, * if the this view knows its size (because its MeasureSpec has a mode of * EXACTLY), and the child has indicated in its LayoutParams that it wants * to be the same size as the parent, the parent should ask the child to * layout given an exact size. * * @param spec * The requirements for this view * @param padding * The padding of this view for the current dimension and * margins, if applicable * @param childDimension * How big the child wants to be in the current dimension * @return a MeasureSpec integer for the child */ public static int getChildMeasureSpec(int spec, int padding, int childDimension) { int specMode = MeasureSpec.getMode(spec); int specSize = MeasureSpec.getSize(spec); int size = Math.max(0, specSize - padding); int resultSize = 0; int resultMode = 0; switch (specMode) { // Parent has imposed an exact size on us case MeasureSpec.EXACTLY: if (childDimension >= 0) { resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.FILL_PARENT) { // Child wants to be our size. So be it. resultSize = size; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent has imposed a maximum size on us case MeasureSpec.AT_MOST: if (childDimension >= 0) { // Child wants a specific size... so be it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.FILL_PARENT) { // Child wants to be our size, but our size is not fixed. // Constrain child to not be bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size. It can't be // bigger than us. resultSize = size; resultMode = MeasureSpec.AT_MOST; } break; // Parent asked to see how big we want to be case MeasureSpec.UNSPECIFIED: if (childDimension >= 0) { // Child wants a specific size... let him have it resultSize = childDimension; resultMode = MeasureSpec.EXACTLY; } else if (childDimension == LayoutParams.FILL_PARENT) { // Child wants to be our size... find out how big it should // be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } else if (childDimension == LayoutParams.WRAP_CONTENT) { // Child wants to determine its own size.... find out how // big it should be resultSize = 0; resultMode = MeasureSpec.UNSPECIFIED; } break; } return MeasureSpec.makeMeasureSpec(resultSize, resultMode); } protected int makeMeasureSpec(int layoutSize, int sizeConstraint) { int mode = layoutSize == LayoutParams.WRAP_CONTENT ? MeasureSpec.AT_MOST : MeasureSpec.EXACTLY; int size; if (mode == MeasureSpec.AT_MOST) { size = sizeConstraint; } else { size = layoutSize > 0 ? Math.min(layoutSize, sizeConstraint) : sizeConstraint; } return MeasureSpec.makeMeasureSpec(size, mode); } public void setPersistentDrawingCache(int drawingCacheToKeep) { Assert.NOT_IMPLEMENTED(); } }