package com.marshalchen.common.uimodule.tileView.layouts; import android.content.Context; import android.view.View; import android.view.ViewGroup; /** * The AnchorLayout positions it's children using absolute pixel values, * offset by anchors. An anchor exists on each axis (x and y), and is * determined by multiplying the relevant dimension of the child (width for x, * height for y) by a float. This float can be supplied to each child via * LayoutParams, or to the AnchorLayout ViewGroup directly. If a child's * LayoutParams are not specified (null), then it will be positioned using * the Layout's anchor values. * * For example, passing an -0.5f anchorX and -1.0f anchorY will position the * view entirely above, and centered horizontally, relative to the pixel * coordinates supplied. * * This is useful for positioning elements as indicators for another view, * or graphical feature. Tooltips, map markers, instructional elements, etc * could benefit from anchored layouts. */ public class AnchorLayout extends ViewGroup { protected float anchorX = 0f; protected float anchorY = 0f; public AnchorLayout( Context context ) { super( context ); } /** * Sets the anchor values used by this ViewGroup if it's children do not * have anchor values supplied directly (via LayoutParams) * @param aX (float) x-axis anchor value (offset computed by multiplying this value by the child's width * @param aY (float) y-axis anchor value (offset computed by multiplying this value by the child's height */ public void setAnchors( float aX, float aY ) { anchorX = aX; anchorY = aY; requestLayout(); } @Override protected void onMeasure( int widthMeasureSpec, int heightMeasureSpec ) { measureChildren( widthMeasureSpec, heightMeasureSpec ); int width = 0; int height = 0; int count = getChildCount(); for ( int i = 0; i < count; i++ ) { View child = getChildAt( i ); if ( child.getVisibility() != GONE ) { LayoutParams lp = (LayoutParams) child.getLayoutParams(); // get anchor offsets float aX = ( lp.anchorX == null ) ? anchorX : lp.anchorX; float aY = ( lp.anchorY == null ) ? anchorY : lp.anchorY; // offset dimensions by anchor values int computedWidth = (int) ( child.getMeasuredWidth() * aX ); int computedHeight = (int) ( child.getMeasuredHeight() * aY ); // add computed dimensions to actual position int right = lp.x + computedWidth; int bottom = lp.y + computedHeight; // if it's larger, use that width = Math.max( width, right ); height = Math.max( height, bottom ); } } height = Math.max( height, getSuggestedMinimumHeight() ); width = Math.max( width, getSuggestedMinimumWidth() ); width = resolveSize( width, widthMeasureSpec ); height = resolveSize( height, heightMeasureSpec ); setMeasuredDimension( width, height ); } @Override protected void onLayout( boolean changed, int l, int t, int r, int b ) { int count = getChildCount(); for ( int i = 0; i < count; i++ ) { View child = getChildAt( i ); if ( child.getVisibility() != GONE ) { LayoutParams lp = (LayoutParams) child.getLayoutParams(); // get sizes int w = child.getMeasuredWidth(); int h = child.getMeasuredHeight(); // user child's layout params anchor position if set, otherwise // default to anchor position of layout float aX = ( lp.anchorX == null ) ? anchorX : lp.anchorX; float aY = ( lp.anchorY == null ) ? anchorY : lp.anchorY; // apply anchor offset to position int x = lp.x + (int) ( w * aX ); int y = lp.y + (int) ( h * aY ); // set it child.layout( x, y, x + w, y + h ); } } } @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { return new LayoutParams( LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0 ); } @Override protected boolean checkLayoutParams( ViewGroup.LayoutParams p ) { return p instanceof LayoutParams; } @Override protected ViewGroup.LayoutParams generateLayoutParams( ViewGroup.LayoutParams p ) { return new LayoutParams( p ); } /** * Per-child layout information associated with AnchorLayout. */ public static class LayoutParams extends ViewGroup.LayoutParams { /** * The absolute left position of the child in pixels */ public int x = 0; /** * The absolute right position of the child in pixels */ public int y = 0; /** * Float value to determine the child's horizontal offset. This float is multiplied by the child's width. If null, the containing AnchorLayout's anchor values will be used. */ public Float anchorX = null; /** * Float value to determine the child's vertical offset. This float is multiplied by the child's height. If null, the containing AnchorLayout's anchor values will be used. */ public Float anchorY = null; /** * Copy constructor * @param source (LayoutParams) LayoutParams instance to copy properties from */ public LayoutParams( ViewGroup.LayoutParams source ) { super( source ); } /** * Creates a new set of layout parameters with the specified values. * @param width (int) Information about how wide the view wants to be. This should generally be WRAP_CONTENT or a fixed value. * @param height (int) Information about how tall the view wants to be. This should generally be WRAP_CONTENT or a fixed value. */ public LayoutParams( int width, int height ) { super( width, height ); } /** * Creates a new set of layout parameters with the specified values. * @param width (int) Information about how wide the view wants to be. This should generally be WRAP_CONTENT or a fixed value. * @param height (int) Information about how tall the view wants to be. This should generally be WRAP_CONTENT or a fixed value. * @param left (int) Sets the absolute x value of the view's position in pixels * @param top (int) Sets the absolute y value of the view's position in pixels */ public LayoutParams( int width, int height, int left, int top ) { super( width, height ); x = left; y = top; } /** * Creates a new set of layout parameters with the specified values. * @param width (int) Information about how wide the view wants to be. This should generally be WRAP_CONTENT or a fixed value. * @param height (int) Information about how tall the view wants to be. This should generally be WRAP_CONTENT or a fixed value. * @param left (int) Sets the absolute x value of the view's position in pixels * @param top (int) Sets the absolute y value of the view's position in pixels * @param aX (float) Sets the relative horizontal offset of the view (multiplied by the view's width) * @param aY (float) Sets the relative vertical offset of the view (multiplied by the view's height) */ public LayoutParams( int width, int height, int left, int top, float aX, float aY ) { super( width, height ); x = left; y = top; anchorX = aX; anchorY = aY; } } }