Android Open Source - ScalableLayout Scalable Layout






From Project

Back to project page ScalableLayout.

License

The source code is released under:

Apache License

If you think the Android project ScalableLayout listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.ssomai.android.scalablelayout;
/*from ww  w .ja  v a  2s. co  m*/
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.TextView;

public class ScalableLayout extends FrameLayout {
    private static final float Default_Scale_Base_Width = 100f;
    private static final float Default_Scale_Base_Height = 100f;

    private static final float Default_Scale_Left = 0f;
    private static final float Default_Scale_Top = 0f;
    private static final float Default_Scale_Width = 100f;
    private static final float Default_Scale_Height = 100f;

    private static final float Default_Scale_TextSize = 100f;

    private float mScale_Root_Width   = Default_Scale_Base_Width;
    private float mScale_Root_Height   = Default_Scale_Base_Height;
    private float mRatioOfWidthHeight   = mScale_Root_Height / mScale_Root_Width;

    public float getScaleWidth() { return mScale_Root_Width; }
    public float getScaleHeight() { return mScale_Root_Height; }

    public void setScaleWidth(float pWidth) { setScaleSize(pWidth, mScale_Root_Height); }
    public void setScaleHeight(float pHeight) { setScaleSize(mScale_Root_Width, pHeight); }
    public void setScaleSize(float pWidth, float pHeight) {
        setScaleSize(pWidth, pHeight, true);
    }
    private void setScaleSize(float pWidth, float pHeight, boolean pWithInvalidate) {
        mScale_Root_Width = pWidth;
        mScale_Root_Height = pHeight;
        mRatioOfWidthHeight = mScale_Root_Height / mScale_Root_Width;
        if(pWithInvalidate) {
            postDelayedRequestLayout();
        }
    }
    private long mLastRequestPostTime = 0;
    private void postDelayedRequestLayout() {
        long now = System.currentTimeMillis();
        if(mLastRequestPostTime < now - 50 || now < mLastRequestPostTime) {
            mLastRequestPostTime = now;
//      log("==== postDelayedRequestLayout Start");
//      log(getStackTrace(Thread.currentThread().getStackTrace()));
//      log("==== postDelayedRequestLayout End");
            postDelayed(new Runnable() {
                @Override
                public void run() {
                    requestLayout();
                    forceLayout();
                }
            }, 10);
        }
    }

    /**
     * Simple constructor to use when creating a view from code.
     * Scale_Width, Scale_Height uses a default value of 100.
     * <p>
     * code??? ScalableLayout??? ???? ? ???? constructor.
     * Scale_Width, Scale_Height? ?????? 100?? ???.
     *
     * @param pContext
     */
    public ScalableLayout(Context pContext) {
        this(pContext, Default_Scale_Base_Width, Default_Scale_Base_Height);
    }

    /**
     * Constructor that is called when inflating a view from XML.
     * <p>
     * XML??? ScalableLayout??? ???? ? ???? constructor.
     *
     * @param pContext
     * @param pAttrs
     */
    public ScalableLayout(Context pContext, AttributeSet pAttrs) {
        this(pContext, pAttrs,
                pContext.obtainStyledAttributes(pAttrs, R.styleable.ScalableLayout).getFloat(R.styleable.ScalableLayout_scale_base_width, Default_Scale_Base_Width),
                pContext.obtainStyledAttributes(pAttrs, R.styleable.ScalableLayout).getFloat(R.styleable.ScalableLayout_scale_base_height, Default_Scale_Base_Height)
        );
    }

    /**
     * Simple constructor to use when creating a view from code.
     * <p>
     * code??? ScalableLayout??? ???? ? ???? constructor.
     *
     * @param pContext
     * @param pScale_Width Scale width of ScalableLayout.
     * @param pScale_Height Scale height of ScalableLayout.
     */
    public ScalableLayout(Context pContext, float pScale_Width, float pScale_Height) {
        this(pContext, null, pScale_Width, pScale_Height);
    }

    private ScalableLayout(Context pContext, AttributeSet pAttrs, float pScale_Width, float pScale_Height) {
        super(pContext, pAttrs);
        setScaleSize(pScale_Width, pScale_Height, true);
    }

    /**
     * @param pChild
     * @return Scalable.LayoutParams of pChild
     */
    public ScalableLayout.LayoutParams getChildLayoutParams(View pChild) {
        ViewGroup.LayoutParams lp = pChild.getLayoutParams();
        if(lp == null) {
            lp = generateDefaultLayoutParams();
            pChild.setLayoutParams(lp);
        }
        if(lp instanceof ScalableLayout.LayoutParams == false) {
            throw new IllegalArgumentException("pChild has not ScalableLayout.LayoutParams "+pChild.getLayoutParams());
        }
        return (ScalableLayout.LayoutParams) pChild.getLayoutParams();
    }
    /**
     * sets TextSize of TextView and EdiText to scale automatically
     * @param pTextView
     * @param pScale_TextSize
     */
    public void setScale_TextSize(TextView pTextView, float pScale_TextSize) {
        getChildLayoutParams(pTextView).setScale_TextSize(pScale_TextSize);
    }
    /**
     * @param pTextView target textview to wrap content
     * @param pDirection
     * @param pRescaleSurrounded
     */
    public void setTextView_WrapContent(TextView pTextView, TextView_WrapContent_Direction pDirection, boolean pRescaleSurrounded) {
        setTextView_WrapContent(pTextView, pDirection, pRescaleSurrounded, true);
    }
    public void setTextView_WrapContent(TextView pTextView, TextView_WrapContent_Direction pDirection, boolean pRescaleSurrounded, boolean pMoveSibligs) {
        getChildLayoutParams(pTextView).setTextView_WrapContent(pDirection, pRescaleSurrounded, pMoveSibligs);
        refreshTextChangedListener(pTextView);
    }
    private TextWatcher mTextWatcher = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence pS, int pStart, int pBefore, int pCount) {
            postDelayedRequestLayout();
        }
        @Override
        public void beforeTextChanged(CharSequence pS, int pStart, int pCount, int pAfter) { }
        @Override
        public void afterTextChanged(Editable pS) {
            postDelayedRequestLayout();
        }
    };
    private void refreshTextChangedListener(TextView pTextView) {
        LayoutParams lSLLP = getChildLayoutParams(pTextView);

        try {
            pTextView.removeTextChangedListener(mTextWatcher);
        } catch (Throwable e) {
        }
        if(lSLLP.mTextView_WrapContent_Direction != TextView_WrapContent_Direction.None) {
            pTextView.addTextChangedListener(mTextWatcher);
        }
    }

    @Override
    public void addView(View pChild) { addView(pChild, getChildCount()); }
    @Override
    public void addView(View pChild, int pIndex) { addView(pChild, pIndex, generateDefaultLayoutParams()); }
    @Override
    public void addView(View pChild, int pWidth, int pHeight) { addView(pChild, new ViewGroup.LayoutParams(pWidth, pHeight)); }
    @Override
    public void addView(View pChild, ViewGroup.LayoutParams pParams) { addView(pChild, getChildCount(), pParams); }
    @Override
    public void addView(View pChild, int pIndex, ViewGroup.LayoutParams pParams) {
        if(pParams instanceof ScalableLayout.LayoutParams) {
            addView_Final(pChild, pIndex, (ScalableLayout.LayoutParams) pParams);
        } else {
            addView_Final(pChild, pIndex, generateLayoutParams(pParams));
        }
    }
    public void addView(View pChild, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        addView_Final(pChild, getChildCount(), new ScalableLayout.LayoutParams(pScale_Left, pScale_Top, pScale_Width, pScale_Height));
    }
    private final void addView_Final(View pChild, int pIndex, ScalableLayout.LayoutParams pScaledLayoutParams) {
        super.addView(pChild, pIndex, pScaledLayoutParams);
    }

    @Override
    protected ScalableLayout.LayoutParams generateDefaultLayoutParams() {
        return new ScalableLayout.LayoutParams(0f, 0f, getScaleWidth(), getScaleHeight());
    }
    @Override
    public ScalableLayout.LayoutParams generateLayoutParams(AttributeSet pAttrs) {
        TypedArray attrs = getContext().obtainStyledAttributes(pAttrs, R.styleable.ScalableLayout);

        TextView_WrapContent_Direction dir = TextView_WrapContent_Direction.None;
        String dirstr = attrs.getString(R.styleable.ScalableLayout_textview_wrapcontent_direction);
        if(dirstr != null) {
            for(TextView_WrapContent_Direction d : TextView_WrapContent_Direction.values()) {
                if(dirstr.toLowerCase().compareTo(d.name().toLowerCase()) == 0) {
                    dir = d;
                    break;
                }
            }
        }

        return new ScalableLayout.LayoutParams(
                attrs.getFloat(R.styleable.ScalableLayout_scale_left, Default_Scale_Left),
                attrs.getFloat(R.styleable.ScalableLayout_scale_top, Default_Scale_Top),
                attrs.getFloat(R.styleable.ScalableLayout_scale_width, Default_Scale_Width),
                attrs.getFloat(R.styleable.ScalableLayout_scale_height, Default_Scale_Height),
                attrs.getFloat(R.styleable.ScalableLayout_scale_textsize, Default_Scale_TextSize),
                dir,
                attrs.getBoolean(R.styleable.ScalableLayout_textview_wrapcontent_resizesurrounded, false),
                attrs.getBoolean(R.styleable.ScalableLayout_textview_wrapcontent_movesiblings, true)
        );
    }
    @Override
    protected ScalableLayout.LayoutParams generateLayoutParams(ViewGroup.LayoutParams pP) {
        if(pP instanceof ScalableLayout.LayoutParams) {
            return (ScalableLayout.LayoutParams) pP;
        }
        return new ScalableLayout.LayoutParams(pP);
    }


    /**
     * move childView inside ScalableLayout
     * @param pChildView view to move. should be child of ScaleLayout
     * @param pScale_Left
     * @param pScale_Top
     */
    public void moveChildView(View pChildView, float pScale_Left, float pScale_Top) {
        ScalableLayout.LayoutParams lSLLP = getChildLayoutParams(pChildView);
//    log("moveChildView 1 lSLLP:"+lSLLP+" pChildView:"+pChildView);
        lSLLP.mScale_Left = pScale_Left;
        lSLLP.mScale_Top = pScale_Top;
//    log("moveChildView 2 lSLLP:"+lSLLP+" pChildView:"+pChildView);
//    log("moveChildView "+getStackTrace(Thread.currentThread().getStackTrace()));
        postInvalidate();
    }

    /**
     * move and resize childView inside ScalableLayout
     * @param pChildView view to move. should be child of ScaleLayout
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     */
    public void moveChildView(View pChildView, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        ScalableLayout.LayoutParams lSLLP = getChildLayoutParams(pChildView);
        lSLLP.mScale_Left = pScale_Left;
        lSLLP.mScale_Top = pScale_Top;
        lSLLP.mScale_Width = pScale_Width;
        lSLLP.mScale_Height = pScale_Height;
        postInvalidate();
    }


//  private float updateTextViewWidth(TextView pTV_Text, TextView_WrapContent_Direction pTextView_WrapContent_Direction, ScalableLayout.LayoutParams pTV_SLLP, float pBGWidth, float pBGHeight) {
//    float lTextView_OldScaleWidth = pTV_SLLP.getScale_Width();
//    float lTextView_OldWidth = pTV_Text.getWidth();
//
////    log("updateTextViewWidth 1 lOldViewWidth:"+lTextView_OldWidth+" lOldViewScaleWidth:"+lTextView_OldScaleWidth+" pBGWidth:"+pBGWidth+" getScaleWidth:"+getScaleWidth() +"pTextView_WrapContent_Direction:"+pTextView_WrapContent_Direction);
//    if(lTextView_OldScaleWidth <= 0 || pBGWidth <= 0 || getScaleWidth() <= 0) {
//      postDelayedRequestLayout();
//
//      return pBGWidth;
//    }
//
//    int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
//    int heightMeasureSpec = MeasureSpec.makeMeasureSpec((int)(pTV_Text.getHeight()/1.1f), MeasureSpec.EXACTLY);
//    pTV_Text.measure(widthMeasureSpec, heightMeasureSpec);
//    float lTextView_NewWidth = pTV_Text.getMeasuredWidth();
//    float lTextView_NewScaleWidth = (lTextView_NewWidth * getScaleWidth() / pBGWidth)*1.05f; // ???? ???? ???? ???? ??? ????? ? ???? ??????. ??? ? ?????????
//
//    float lTextView_DiffScaleWidth = lTextView_NewScaleWidth - lTextView_OldScaleWidth;
//
////    log("updateTextViewWidth 2 lOldViewWidth:"+lTextView_OldWidth+" lOldViewScaleWidth:"+lTextView_OldScaleWidth+" pBGWidth:"+pBGWidth+" getScaleWidth:"+getScaleWidth() +"pTextView_WrapContent_Direction:"+pTextView_WrapContent_Direction);
//
//    float lRootView_OldScaleWidth = getScaleWidth();
//    if(Math.abs(lTextView_NewWidth - lTextView_OldWidth) * 100 > pBGWidth && Math.abs(lTextView_DiffScaleWidth) > getScaleWidth() / 100f) {
////      log("updateTextViewWidth 3 lOldViewWidth:"+lTextView_OldWidth+" lOldViewScaleWidth:"+lTextView_OldScaleWidth+" pBGWidth:"+pBGWidth+" getScaleWidth:"+getScaleWidth() +"pTextView_WrapContent_Direction:"+pTextView_WrapContent_Direction);
//      for(int i=0;i<getChildCount();i++) {
//        View v = getChildAt(i);
//        if(v == pTV_Text) {
//          continue;
//        }
//        ScalableLayout.LayoutParams lV_SLLP = (ScalableLayout.LayoutParams) v.getLayoutParams();
//        switch (pTextView_WrapContent_Direction) {
//        case Left: {
//          // move views on left
//          // ???? ?? ?? ?? ??????
//          if(
//            lV_SLLP.getScale_Right() <= pTV_SLLP.getScale_Right() &&
//            (
//              ( pTV_SLLP.getScale_Top() <= lV_SLLP.getScale_Top() && lV_SLLP.getScale_Top() <= pTV_SLLP.getScale_Bottom())
//              ||
//              ( pTV_SLLP.getScale_Top() <= lV_SLLP.getScale_Bottom() && lV_SLLP.getScale_Bottom() <= pTV_SLLP.getScale_Bottom())
//              ||
//              ( pTV_SLLP.getScale_Top() >= lV_SLLP.getScale_Top() &&  lV_SLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom())
//              )) {
//            moveChildView(v, lV_SLLP.getScale_Left()-lTextView_DiffScaleWidth, lV_SLLP.getScale_Top());
//          }
//          else if( pTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
//            lV_SLLP.getScale_Top() <= pTV_SLLP.getScale_Top() &&
//            lV_SLLP.getScale_Left() <= pTV_SLLP.getScale_Left() &&
//            lV_SLLP.getScale_Right() >= pTV_SLLP.getScale_Right() &&
//            lV_SLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom()) {
//
//            moveChildView(v, lV_SLLP.getScale_Left()-lTextView_DiffScaleWidth, lV_SLLP.getScale_Top(), lV_SLLP.getScale_Width()+lTextView_DiffScaleWidth, lV_SLLP.getScale_Height());
//          }
//        } break;
//        case Right: {
//          // move views on right
//          // ????? ?? ?? ?? ??????
//          if(
//            lV_SLLP.getScale_Left() >= pTV_SLLP.getScale_Left() &&
//            (
//              ( pTV_SLLP.getScale_Top() <= lV_SLLP.getScale_Top() && lV_SLLP.getScale_Top() <= pTV_SLLP.getScale_Bottom())
//              ||
//              ( pTV_SLLP.getScale_Top() <= lV_SLLP.getScale_Bottom() && lV_SLLP.getScale_Bottom() <= pTV_SLLP.getScale_Bottom())
//              ||
//              ( pTV_SLLP.getScale_Top() >= lV_SLLP.getScale_Top() &&  lV_SLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom())
//              )) {
//
//            moveChildView(v, lV_SLLP.getScale_Left()+lTextView_DiffScaleWidth, lV_SLLP.getScale_Top());
//          }
//          else if( pTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
//            lV_SLLP.getScale_Top() <= pTV_SLLP.getScale_Top() &&
//            lV_SLLP.getScale_Left() <= pTV_SLLP.getScale_Left() &&
//            lV_SLLP.getScale_Right() >= pTV_SLLP.getScale_Right() &&
//            lV_SLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom()) {
//
//            moveChildView(v, lV_SLLP.getScale_Left(), lV_SLLP.getScale_Top(), lV_SLLP.getScale_Width()+lTextView_DiffScaleWidth, lV_SLLP.getScale_Height());
//          }
//        } break;
//        case Center_Horizontal: {
//          // ???? ?? ?? ?? ??????
//          if(
//            lV_SLLP.getScale_Right() <= pTV_SLLP.getScale_Right() &&
//            (
//              ( pTV_SLLP.getScale_Top() <= lV_SLLP.getScale_Top() && lV_SLLP.getScale_Top() <= pTV_SLLP.getScale_Bottom())
//              ||
//              ( pTV_SLLP.getScale_Top() <= lV_SLLP.getScale_Bottom() && lV_SLLP.getScale_Bottom() <= pTV_SLLP.getScale_Bottom())
//              ||
//              ( pTV_SLLP.getScale_Top() >= lV_SLLP.getScale_Top() &&  lV_SLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom())
//              )) {
//
//            moveChildView(v, lV_SLLP.getScale_Left()-lTextView_DiffScaleWidth/2, lV_SLLP.getScale_Top());
//          }
//          else if( pTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
//            lV_SLLP.getScale_Top() <= pTV_SLLP.getScale_Top() &&
//            lV_SLLP.getScale_Left() <= pTV_SLLP.getScale_Left() &&
//            lV_SLLP.getScale_Right() >= pTV_SLLP.getScale_Right() &&
//            lV_SLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom()) {
//
//            moveChildView(v, lV_SLLP.getScale_Left()-lTextView_DiffScaleWidth/2, lV_SLLP.getScale_Top(), lV_SLLP.getScale_Width()+lTextView_DiffScaleWidth/2, lV_SLLP.getScale_Height());
//          }
//
//          // ????? ?? ?? ?? ??????
//          if(
//            lV_SLLP.getScale_Left() >= pTV_SLLP.getScale_Left() &&
//            (
//              ( pTV_SLLP.getScale_Top() <= lV_SLLP.getScale_Top() && lV_SLLP.getScale_Top() <= pTV_SLLP.getScale_Bottom())
//              ||
//              ( pTV_SLLP.getScale_Top() <= lV_SLLP.getScale_Bottom() && lV_SLLP.getScale_Bottom() <= pTV_SLLP.getScale_Bottom())
//              ||
//              ( pTV_SLLP.getScale_Top() >= lV_SLLP.getScale_Top() &&  lV_SLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom())
//              )) {
//
//            moveChildView(v, lV_SLLP.getScale_Left()+lTextView_DiffScaleWidth/2, lV_SLLP.getScale_Top());
//          }
//          else if( pTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
//            lV_SLLP.getScale_Top() <= pTV_SLLP.getScale_Top() &&
//            lV_SLLP.getScale_Left() <= pTV_SLLP.getScale_Left() &&
//            lV_SLLP.getScale_Right() >= pTV_SLLP.getScale_Right() &&
//            lV_SLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom()) {
//
//            moveChildView(v, lV_SLLP.getScale_Left(), lV_SLLP.getScale_Top(), lV_SLLP.getScale_Width()+lTextView_DiffScaleWidth/2, lV_SLLP.getScale_Height());
//          }
//        } break;
//        }
//      }
//
//      switch (pTextView_WrapContent_Direction) {
//      case Left: {
//        moveChildView(pTV_Text, pTV_SLLP.getScale_Left()-(lTextView_DiffScaleWidth), pTV_SLLP.getScale_Top(), lTextView_NewScaleWidth, pTV_SLLP.getScale_Height());
//      } break;
//      case Right: {
//        moveChildView(pTV_Text, pTV_SLLP.getScale_Left(), pTV_SLLP.getScale_Top(), lTextView_NewScaleWidth, pTV_SLLP.getScale_Height());
//      } break;
//      case Center_Horizontal: {
//        moveChildView(pTV_Text, pTV_SLLP.getScale_Left()-(lTextView_DiffScaleWidth)/2, pTV_SLLP.getScale_Top(), lTextView_NewScaleWidth, pTV_SLLP.getScale_Height());
//      } break;
//      }
//
//      if(pTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
//        setScaleSize(getScaleWidth()+lTextView_NewScaleWidth-lTextView_OldScaleWidth, getScaleHeight(), true);
//      }
//    }
////    log("updateTextViewWidth 4 ret "+(pBGHeight*getScaleHeight()/lRootView_OldScaleWidth)+" = "+pBGHeight+"*"+getScaleHeight()+"/"+lRootView_OldScaleWidth);
//    return pBGWidth*getScaleWidth()/lRootView_OldScaleWidth;
//  }
//  private float updateTextViewHeight(TextView pTV_Text, TextView_WrapContent_Direction pTextView_WrapContent_Direction, ScalableLayout.LayoutParams pTV_SLLP, float pBGWidth, float pBGHeight) {
//    float lTextView_OldHeight = pTV_Text.getHeight();
//    float lTextView_OldScaleHeight = pTV_SLLP.getScale_Height();
//
//    log("updateTextViewHeight 1 lOldViewHeight:"+lTextView_OldHeight+" lOldViewScaleHeight:"+lTextView_OldScaleHeight+" pBGHeight:"+pBGHeight+" getScaleHeight:"+getScaleHeight());
//
////    return pBGHeight;
//
//
////    if(lTextView_OldHeight <= 0 || lTextView_OldScaleHeight <= 0 || pBGHeight <= 0 || getScaleHeight() <= 0) {
//    if(lTextView_OldScaleHeight <= 0 || pBGHeight <= 0 || getScaleHeight() <= 0) {
//      postDelayedRequestLayout();
//      return pBGHeight;
//    }
//
////    int widthMeasureSpec = MeasureSpec.makeMeasureSpec(pTV_Text.getWidth(), MeasureSpec.AT_MOST);
//    int widthMeasureSpec = MeasureSpec.makeMeasureSpec((int)(pTV_Text.getWidth()/1.1f), MeasureSpec.EXACTLY);
//    int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
////    int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.AT_MOST);
//    pTV_Text.measure(widthMeasureSpec, heightMeasureSpec);
//    float lTextView_NewHeight = pTV_Text.getMeasuredHeight();
//    float lTextView_NewScaleHeight = (lTextView_NewHeight * getScaleHeight() / pBGHeight);
////    log(String.format("updateTextViewHeight %f = %f * %f / %f TextSize:%f", lTextView_NewScaleHeight, lTextView_NewHeight, getScaleHeight(), pBGHeight, pTV_Text.getTextSize()));
////
//////    log("updateTextViewHeight "+lOldViewHeight+","+lNewViewHeight+" "+lOldScaleHeight+","+lNewScaleHeight);
//    log("updateTextViewHeight 2 lOldViewHeight:"+lTextView_OldHeight+" lOldViewScaleHeight:"+lTextView_OldScaleHeight+" pBGHeight:"+pBGHeight+" getScaleHeight:"+getScaleHeight());
////
////    log(String.format("updateTextViewHeight1 View: %5.3f -> %5.3f Scale: %5.3f -> %5.3f", lTextView_OldHeight, lTextView_NewHeight, lTextView_OldScaleHeight, lTextView_NewScaleHeight));
////    log(String.format("updateTextViewHeight2 Scalable Scale: %5.3f,%5.3f ", getScaleWidth(), getScaleHeight()));
////    log(String.format("updateTextViewHeight3 Scalable View: %5.3f,%5.3f", pBGWidth, pBGHeight));
//
//    float lTextView_DiffScaleHeight = lTextView_NewScaleHeight - lTextView_OldScaleHeight;
//    float lRootView_OldScaleHeight = getScaleHeight();
//
//    if(Math.abs(lTextView_NewHeight - lTextView_OldHeight) * 100 > pBGHeight && Math.abs(lTextView_DiffScaleHeight) > getScaleHeight() / 100f) {
//      log("updateTextViewHeight 3 lOldViewHeight:"+lTextView_OldHeight+" lOldViewScaleHeight:"+lTextView_OldScaleHeight+" pBGHeight:"+pBGHeight+" getScaleHeight:"+getScaleHeight());
//      for(int i=0;i<getChildCount();i++) {
//        View v = getChildAt(i);
//        if(v == pTV_Text) {
//          continue;
//        }
//        ScalableLayout.LayoutParams lSLLP = (ScalableLayout.LayoutParams) v.getLayoutParams();
//
////        log(String.format("lSLLP:"+lSLLP.toString()));
////        log(String.format("pTV_SLLP:"+pTV_SLLP.toString()));
////          getScaleHeight(), getScaleHeight()+lNewViewScaleHeight-lOldViewScaleHeight));
//        switch (pTextView_WrapContent_Direction) {
//        case Top: {
//          // ?????? ??? ?? ?? ?? ??????
//          if(
//            lSLLP.getScale_Bottom() <= pTV_SLLP.getScale_Bottom() &&
//            (
//              ( pTV_SLLP.getScale_Left() <= lSLLP.getScale_Left() && lSLLP.getScale_Left() <= pTV_SLLP.getScale_Right())
//              ||
//              ( pTV_SLLP.getScale_Left() <= lSLLP.getScale_Right() && lSLLP.getScale_Right() <= pTV_SLLP.getScale_Right())
//              ||
//              ( lSLLP.getScale_Left() <= pTV_SLLP.getScale_Left() && pTV_SLLP.getScale_Right() <= lSLLP.getScale_Right())
//              )) {
//
//            moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top()-lTextView_DiffScaleHeight);
//          }
//          // ?????? ?????? ?? ???? ??
//          else if( pTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
//            lSLLP.getScale_Top() <= pTV_SLLP.getScale_Top() &&
//            lSLLP.getScale_Left() <= pTV_SLLP.getScale_Left() &&
//            lSLLP.getScale_Right() >= pTV_SLLP.getScale_Right() &&
//            lSLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom()) {
//
//            moveChildView(v,
//              lSLLP.getScale_Left(), lSLLP.getScale_Top()-lTextView_DiffScaleHeight,
//              lSLLP.getScale_Width(), lSLLP.getScale_Height()+lTextView_DiffScaleHeight);
//          }
//        } break;
//        case Bottom: {
//          // ?????? ???? ?? ?? ?? ??????
//          if(
//            lSLLP.getScale_Top() >= pTV_SLLP.getScale_Top() &&
//            (
//              ( pTV_SLLP.getScale_Left() <= lSLLP.getScale_Left() && lSLLP.getScale_Left() <= pTV_SLLP.getScale_Right())
//              ||
//              ( pTV_SLLP.getScale_Left() <= lSLLP.getScale_Right() && lSLLP.getScale_Right() <= pTV_SLLP.getScale_Right())
//              ||
//              ( lSLLP.getScale_Left() <= pTV_SLLP.getScale_Left() && pTV_SLLP.getScale_Right() <= lSLLP.getScale_Right())
////              ( pTV_SLLP.getScale_Top() >= lSLLP.getScale_Top() &&  lSLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom())
//              )) {
//
//            moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top()+lTextView_DiffScaleHeight);
//          }
//          // ?????? ?????? ?? ???? ??
//          else if( pTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
//            lSLLP.getScale_Top() <= pTV_SLLP.getScale_Top() &&
//            lSLLP.getScale_Left() <= pTV_SLLP.getScale_Left() &&
//            lSLLP.getScale_Right() >= pTV_SLLP.getScale_Right() &&
//            lSLLP.getScale_Bottom() >= pTV_SLLP.getScale_Bottom()) {
//
//            moveChildView(v,
//              lSLLP.getScale_Left(), lSLLP.getScale_Top(),
//              lSLLP.getScale_Width(), lSLLP.getScale_Height()+lTextView_DiffScaleHeight);
//          }
//        } break;
//        }
//      }
//
//      switch (pTextView_WrapContent_Direction) {
//      case Top: {
//      } break;
//      case Bottom: {
//        moveChildView(pTV_Text, pTV_SLLP.getScale_Left(), pTV_SLLP.getScale_Top(), pTV_SLLP.getScale_Width(), lTextView_NewScaleHeight);
//      } break;
//      }
//
//      if(pTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
//        log(String.format("setScaleSize From:%5.3f To:%5.3f", getScaleHeight(), getScaleHeight()+lTextView_DiffScaleHeight));
//        setScaleSize(getScaleWidth(), getScaleHeight()+lTextView_DiffScaleHeight, true);
//      }
//    }
//    log("updateTextViewHeight 4 ret "+(pBGHeight*getScaleHeight()/lRootView_OldScaleHeight)+" = "+pBGHeight+"*"+getScaleHeight()+"/"+lRootView_OldScaleHeight);
//    return pBGHeight*getScaleHeight()/lRootView_OldScaleHeight;
//  }

    @Override
    protected void onMeasure(int pWidthMeasureSpec, int pHeightMeasureSpec) {
//    log("onMeasure ================ Start "+this.toString());

//    float   lRoot_Width           = 0;
        float   lRoot_Width_Max         = 0;
        int   lRoot_MeasureSpec_WidthMode   = MeasureSpec.getMode(pWidthMeasureSpec);

//    float   lRoot_Height           = 0;
        float   lRoot_Height_Max         = 0;
        int   lRoot_MeasureSpec_HeightMode   = MeasureSpec.getMode(pHeightMeasureSpec);

        // get condition of ScalableLayout
        // ScalableLayout??? ??? ???? ?????? ?????
        {
            int lRoot_MeasureSpec_Width   = MeasureSpec.getSize(pWidthMeasureSpec);
            int lRoot_MeasureSpec_Height   = MeasureSpec.getSize(pHeightMeasureSpec);
            switch (lRoot_MeasureSpec_WidthMode) {
                case MeasureSpec.EXACTLY:
//        log("  onMeasure Width  Exactly "+lRoot_Width+" = min("+mScale_Root_Width+", "+lRoot_MeasureSpec_Width+")");
                    lRoot_Width_Max   = lRoot_MeasureSpec_Width;
//        log("  onMeasure Width  Exactly = "+lRoot_Width_Max);
//        lRoot_Width     = lRoot_MeasureSpec_Width;
                    break;
                case MeasureSpec.AT_MOST:
                    if(lRoot_MeasureSpec_HeightMode == MeasureSpec.EXACTLY) {
//          lRoot_Width_Max   = lRoot_MeasureSpec_Width;
                        lRoot_Width_Max   = Float.MAX_VALUE;
                    } else {
                        lRoot_Width_Max   = lRoot_MeasureSpec_Width;
                    }
//        lRoot_Width     = lRoot_MeasureSpec_Width;
//        log("  onMeasure Width  AtMost  = "+lRoot_Width_Max);
                    break;
                default:
//        log("  onMeasure Width  Unspecified "+lRoot_Width_Max+" = "+mScale_Root_Width);
//        lRoot_Width     = mScale_Root_Width;
//        lRoot_Width_Max   = Float.MAX_VALUE;
//        lRoot_Width     = lRoot_WidthSize;
//        lRoot_Width_Max   = lRoot_WidthSize;
//        break;
//        throw new IllegalArgumentException(String.format("  onMeasure Width  Unspecified %08X", lRoot_MeasureSpec_Width));
                    lRoot_Width_Max   = Float.MAX_VALUE;
//        throw new IllegalArgumentException(String.format("  onMeasure Width Unspecified %08X lRoot_MeasureSpec_WidthMode:%d lRoot_MeasureSpec_Width:%d",
//          lRoot_MeasureSpec_Width, lRoot_MeasureSpec_WidthMode, lRoot_MeasureSpec_Width));
            }

            switch (lRoot_MeasureSpec_HeightMode) {
                case MeasureSpec.EXACTLY:
                    lRoot_Height_Max   = lRoot_MeasureSpec_Height;
//        log("  onMeasure Height Exactly = "+lRoot_Height_Max);
                    break;
                case MeasureSpec.AT_MOST:
                    if(lRoot_MeasureSpec_WidthMode == MeasureSpec.EXACTLY) {
//          lRoot_Height_Max   = lRoot_MeasureSpec_Height;
                        lRoot_Height_Max = Float.MAX_VALUE;
                    } else {
                        lRoot_Height_Max   = lRoot_MeasureSpec_Height;
                    }
//        log("  onMeasure Height AtMost  = "+lRoot_Height_Max);
                    break;
                default:
//        lRoot_Height = lRoot_Width * mRatioOfWidthHeight;
////      log("  onMeasure Height Unspecified "+lRootHeight+" = "+lRootWidth+"*"+mRatioOfWidthHeight);
//        break;
                    lRoot_Height_Max = Float.MAX_VALUE;
//        throw new IllegalArgumentException(String.format("  onMeasure Height Unspecified %08X lRoot_MeasureSpec_HeightMode:%d lRoot_MeasureSpec_Height:%d",
//          lRoot_MeasureSpec_Height, lRoot_MeasureSpec_HeightMode, lRoot_MeasureSpec_Height));
            }
        }

//    log(String.format("onMeasure 1 lScale:%5.3f ChildCount:%d Root:(%5.3f, %5.3f)", (lRootWidth/mScale_Full_Width), getChildCount(), lRootWidth, lRootHeight));
//    log(String.format("onMeasure 1 lScale:%5.3f ChildCount:%d", (lRootWidth/mScale_Full_Width), getChildCount()));

        float   lRoot_Width           = 0;
        float   lRoot_Height           = 0;
        for(int tryi = 0 ; tryi < 3 ; tryi++) {
            float lScale_Ratio_Pre = Math.min(lRoot_Width_Max / mScale_Root_Width, lRoot_Height_Max / mScale_Root_Height);
//      log(String.format("onMeasure 1 lScale_Ratio:%5.3f Scale_Ratio:(%5.3f, %5.3f) Scale:(%5.3f, %5.3f)",
//        lScale_Ratio_Pre, (lRoot_Width_Max/mScale_Root_Width), (lRoot_Height_Max / mScale_Root_Height), mScale_Root_Width, mScale_Root_Height));

            //////////////////////// Text Measure
            // TODO english
            // ScalableLayout???? TextView??? TextView_WrapContent_Direction ?????? ???? ?? ScalableLayout??? Scale Size? ????? View???? Scalable LayoutParams? ???
            for (int i=0;i<getChildCount();i++) {
                View lView = getChildAt(i);

                if(lView instanceof TextView) {
                    updateTextViewSize((TextView)lView, lScale_Ratio_Pre);
                }
            }



            // calculate final size of ScalableLayout
            // ScalableLayout??? ??????? ????? ??????
            float lScale_Ratio_Post = Math.min(lRoot_Width_Max / mScale_Root_Width, lRoot_Height_Max / mScale_Root_Height);

//      if(mHaveBeenLayouted == true && isDifferentSufficiently(lScale_Ratio_Post, lScale_Ratio_Pre) == false) {
//        break;
//      }

//      log(String.format("onMeasure 2 lScale_Ratio:%5.3f->%5.3f Scale_Ratio:(%5.3f, %5.3f) Scale:(%5.3f, %5.3f)",
//        lScale_Ratio_Pre, lScale_Ratio_Post, (lRoot_Width_Max/mScale_Root_Width), (lRoot_Height_Max / mScale_Root_Height), mScale_Root_Width, mScale_Root_Height));
            {
                lRoot_Width   = mScale_Root_Width * lScale_Ratio_Post;
                lRoot_Height   = mScale_Root_Height * lScale_Ratio_Post;
//        log(String.format("onMeasure 2 Root_Max:(%10.7f, %10.7f) Root:(%10.7f, %10.7f)", lRoot_Width_Max, lRoot_Height_Max, lRoot_Width, lRoot_Height));

//      if(Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1 || Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1) {
//        log(String.format("onMeasure 2.1 %b %b", Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1,  Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1));
//        if(lRoot_MeasureSpec_WidthMode == MeasureSpec.EXACTLY && lRoot_MeasureSpec_HeightMode == MeasureSpec.EXACTLY) {
//          // ???? ?? ? ????
//          if(lForTextViewRootWidth / lRoot_Width > lForTextViewRootHeight / lRoot_Height) {
//            lRoot_Height /= (lForTextViewRootWidth / lRoot_Width);
//            lRoot_Width = lForTextViewRootWidth;
//            log(String.format("onMeasure 2.1.1 %b %b", Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1,  Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1));
//          } else {
//            lRoot_Width /= (lForTextViewRootHeight / lRoot_Height);
//            lRoot_Height = lForTextViewRootHeight;
//            log(String.format("onMeasure 2.1.2 %b %b", Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1,  Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1));
//          }
////      } else if(lWidthMode == MeasureSpec.EXACTLY && lHeightMode != MeasureSpec.EXACTLY) {
////        lRootHeight /= (lForTextViewRootWidth / lRootWidth);
////        lRootWidth = lForTextViewRootWidth;
////      } else if(lWidthMode != MeasureSpec.EXACTLY && lHeightMode == MeasureSpec.EXACTLY) {
////        lRootWidth /= (lForTextViewRootHeight / lRootHeight);
////        lRootHeight = lForTextViewRootHeight;
//        } else {
//          lRoot_Width = lForTextViewRootWidth;
//          lRoot_Height = lForTextViewRootHeight;
//          log(String.format("onMeasure 2.1.3 %b %b", Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1,  Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1));
//        }
//
////      postDelayedRequestLayout();
//      }
            }

            {
//      log(String.format("onMeasure 2 lScale:%5.3f", (lRoot_Width/mScale_Root_Width)));
//      log(String.format("onMeasure 3 lScale:%5.3f ChildCount:%d Root:(%5.3f, %5.3f) ForTextView:(%5.3f, %5.3f)",
//        (lRoot_Width/mScale_Root_Width), getChildCount(), lRoot_Width, lRoot_Height, lForTextViewRootWidth, lForTextViewRootHeight));

//      float lScale_TextViewMeasure_Post = Math.min(lRoot_Width_Max / mScale_Root_Width, lRoot_Height_Max / mScale_Root_Height);
//      float lScale_TextViewMeasure_Post = Math.min(lRoot_Width / mScale_Root_Width, lRoot_Height / mScale_Root_Height);
//      float lScale_TextViewMeasure_Post = lRoot_Width / mScale_Root_Width;



                // ?????? ?? ??? ???? ?? ?? ???? ????? layoutparams? ?????????
                // update layoutparams of each child view to arrange finally
                float lTopMarginFromWeight = (lRoot_Height - (lRoot_Width * mRatioOfWidthHeight))/4;
//        log("  onMeasure ("+lRoot_Width+","+lRoot_Height+") Ratio:"+mRatioOfWidthHeight+" lScale_TextViewMeasure_Post:"+lScale_Ratio_Post+" lTopMarginFromWeight:"+lTopMarginFromWeight);
                for (int i=0;i<getChildCount();i++) {
                    View lView = getChildAt(i);

                    ScalableLayout.LayoutParams lParams = getChildLayoutParams(lView);

//          log("  onMeasure lSLLP:"+lParams+"\n");
                    boolean dif = false;
//          log("  onMeasure bef v:"+lView+" pos:("+lParams.leftMargin+","+lParams.topMargin+") size:("+lParams.width+","+lParams.height+") dif:"+dif);

                    int lParams_Left = Math.round(lScale_Ratio_Post * lParams.mScale_Left);
//          log("  onMeasure Left: "+lParams.leftMargin+" "+lParams_Left+" "+lScale_Ratio_Post+" "+lParams.mScale_Left);
                    if(lParams.leftMargin != lParams_Left)
                        dif = true;
                    lParams.leftMargin = lParams_Left;

                    int lParams_Top = Math.round(lScale_Ratio_Post * lParams.mScale_Top + lTopMarginFromWeight);
                    if(lParams.topMargin != lParams_Top)
                        dif = true;
                    lParams.topMargin = lParams_Top;

                    int lParams_Width = Math.round(lScale_Ratio_Post * (lParams.mScale_Left + lParams.mScale_Width)) - lParams.leftMargin;
                    if(lParams.width != lParams_Width)
                        dif = true;
                    lParams.width = lParams_Width;

                    int lParams_Height = Math.round(lScale_Ratio_Post * (lParams.mScale_Top + lParams.mScale_Height)) - lParams.topMargin;
                    if(lParams.height != lParams_Height)
                        dif = true;
                    lParams.height = lParams_Height;

//          log("  onMeasure aft v:"+lView+" pos:("+lParams.leftMargin+","+lParams.topMargin+") size:("+lParams.width+","+lParams.height+") dif:"+dif);

//        if(lParams.mScale_TextSize != -1 && lParams.mTextView_WrapContent_Direction == TextView_WrapContent_Direction.None) {
                    if(lParams.mScale_TextSize != -1) {
                        if(lView instanceof TextView) {
                            TextView v = (TextView) lView;
//            if(v.getTextSize() != lParams.mScale_TextSize * lScale_Ratio_Post) {
                            if(isDifferentSufficiently(lParams.mScale_TextSize * lScale_Ratio_Post, v.getTextSize())) {
                                v.setTextSize(TypedValue.COMPLEX_UNIT_PX, lParams.mScale_TextSize * lScale_Ratio_Post);
                            }
                        }
                    }

                    if(dif) {
                        lView.setLayoutParams(lParams);
                    }
                }
            }
//      log(String.format("onMeasure end lScale_Ratio:%5.3f->%5.3f Root:(%5.3f,%5.3f) Scale_Root:(%5.3f,%5.3f) ",
//        lScale_Ratio_Pre, lScale_Ratio_Post,
//        lRoot_Width, lRoot_Height,
//        mScale_Root_Width, mScale_Root_Height));
            if(isDifferentSufficiently(lScale_Ratio_Post, lScale_Ratio_Pre, 1.01f) == false) {
                break;
            }
        }

//    log("onMeasure ================ End   "+this.toString());
        super.onMeasure(MeasureSpec.makeMeasureSpec(Math.round(lRoot_Width), lRoot_MeasureSpec_WidthMode), MeasureSpec.makeMeasureSpec(Math.round(lRoot_Height), lRoot_MeasureSpec_HeightMode));
        setMeasuredDimension(Math.round(lRoot_Width), Math.round(lRoot_Height));
    }


//  @Override
//  protected void onMeasure(int pWidthMeasureSpec, int pHeightMeasureSpec) {
//    log("onMeasure ================ Start "+this.toString());
//
//    float   lRoot_Width           = 0;
//    float   lRoot_Width_Max         = 0;
//    int   lRoot_MeasureSpec_WidthMode   = MeasureSpec.getMode(pWidthMeasureSpec);
//
//    float   lRoot_Height           = 0;
//    float   lRoot_Height_Max         = 0;
//    int   lRoot_MeasureSpec_HeightMode   = MeasureSpec.getMode(pHeightMeasureSpec);
//
//    // get condition of ScalableLayout
//    // ScalableLayout??? ??? ???? ?????? ?????
//    {
//      int lRoot_MeasureSpec_Width   = MeasureSpec.getSize(pWidthMeasureSpec);
//      int lRoot_MeasureSpec_Height   = MeasureSpec.getSize(pHeightMeasureSpec);
//      switch (lRoot_MeasureSpec_WidthMode) {
//      case MeasureSpec.EXACTLY:
//        log("  onMeasure Width  Exactly "+lRoot_Width+" = min("+mScale_Root_Width+", "+lRoot_MeasureSpec_Width+")");
//        lRoot_Width     = lRoot_MeasureSpec_Width;
//        lRoot_Width_Max   = lRoot_MeasureSpec_Width;
//        break;
//      case MeasureSpec.AT_MOST:
//        log("  onMeasure Width  AtMost  "+lRoot_Width+" = min("+mScale_Root_Width+", "+lRoot_MeasureSpec_Width+")");
//        lRoot_Width     = lRoot_MeasureSpec_Width;
//        lRoot_Width_Max   = lRoot_MeasureSpec_Width;
//        break;
//      default:
//        log("  onMeasure Width  Unspecified "+lRoot_Width+" = "+mScale_Root_Width);
//        // TODO ???? ??? ?????? ?
////        lRoot_Width     = mScale_Root_Width;
////        lRoot_Width_Max   = Float.MAX_VALUE;
////        lRoot_Width     = lRoot_WidthSize;
////        lRoot_Width_Max   = lRoot_WidthSize;
////        break;
//        throw new IllegalArgumentException("  onMeasure Width  Unspecified "+lRoot_MeasureSpec_Width);
//      }
//
//      switch (lRoot_MeasureSpec_HeightMode) {
//      case MeasureSpec.EXACTLY:
//        switch (lRoot_MeasureSpec_WidthMode) {
//        case MeasureSpec.EXACTLY:
//          log("  onMeasure Height Exactly Width Exactly "+lRoot_Height+" = min("+lRoot_Width+"*"+mRatioOfWidthHeight+", "+lRoot_MeasureSpec_Height+")");
//          lRoot_Height = Math.min(lRoot_Width * mRatioOfWidthHeight, lRoot_MeasureSpec_Height);
//          lRoot_Height_Max = lRoot_MeasureSpec_Height;
//          break;
//        case MeasureSpec.AT_MOST:
//          log("  onMeasure Height AtMost  Width Exactly "+lRoot_Height+" = min("+lRoot_Width+"*"+mRatioOfWidthHeight+", "+lRoot_MeasureSpec_Height+")");
//          lRoot_Height = Math.min(lRoot_Width * mRatioOfWidthHeight, lRoot_MeasureSpec_Height);
//          lRoot_Height_Max = lRoot_MeasureSpec_Height;
//          break;
//        default:
//          log("  onMeasure Height Unspecified "+lRoot_Height+" = min("+lRoot_Width+"*"+mRatioOfWidthHeight+", "+lRoot_MeasureSpec_Height+")");
//          lRoot_Height = lRoot_MeasureSpec_Height;
//          lRoot_Width = lRoot_Height / mRatioOfWidthHeight;
//          lRoot_Height_Max = Float.MAX_VALUE;
//          break;
//        }
//
//        break;
//      case MeasureSpec.AT_MOST:
//        switch (lRoot_MeasureSpec_WidthMode) {
//        case MeasureSpec.EXACTLY:
//          lRoot_Height = lRoot_Width * mRatioOfWidthHeight;
//          break;
//        case MeasureSpec.AT_MOST:
//          lRoot_Height = Math.min(lRoot_Width * mRatioOfWidthHeight, lRoot_MeasureSpec_Height);
//          break;
//        default:
//          lRoot_Height = Math.min(lRoot_Width * mRatioOfWidthHeight, lRoot_MeasureSpec_Height);
//          break;
//        }
//
////      log("  onMeasure Height AtMost "+lRootHeight+" = min("+(lRootWidth * mRatioOfWidthHeight)+", "+lHeightSize+")");
//        break;
//      default:
//        lRoot_Height = lRoot_Width * mRatioOfWidthHeight;
////      log("  onMeasure Height Unspecified "+lRootHeight+" = "+lRootWidth+"*"+mRatioOfWidthHeight);
//        break;
//      }
//
//      if(lRoot_Height/mRatioOfWidthHeight < lRoot_Width) {
////      log("  onMeasure Height Exactly Replace Width "+lRootWidth+" to "+(lRootHeight/mRatioOfWidthHeight));
//        lRoot_Width = lRoot_Height/mRatioOfWidthHeight;
//      }
//    }
//
////    log(String.format("onMeasure 1 lScale:%5.3f ChildCount:%d Root:(%5.3f, %5.3f)", (lRootWidth/mScale_Full_Width), getChildCount(), lRootWidth, lRootHeight));
////    log(String.format("onMeasure 1 lScale:%5.3f ChildCount:%d", (lRootWidth/mScale_Full_Width), getChildCount()));
//
////    float lScale = lRootWidth / mScale_Full_Width;
//    float lScale_TextViewMeasure_Pre = lRoot_Width / mScale_Root_Width;
//
//    float lForTextViewRootWidth = lRoot_Width;
//    float lForTextViewRootHeight = lRoot_Height;
//
//    //////////////////////// Text Measure
//    for (int i=0;i<getChildCount();i++) {
//      View lView = getChildAt(i);
//
//      if(lView instanceof TextView) {
//        lForTextViewRootHeight = updateTextViewSize((TextView)lView, lForTextViewRootWidth, lForTextViewRootHeight, lScale_TextViewMeasure_Pre);
//
////        TextView v = (TextView) lView;
////
////        ScalableLayout.LayoutParams lParams = getChildLayoutParams(v);
//////        float lNewTextSize = lParams.mScale_TextSize * lScale_TextViewMeasure_Pre;
//////        if( lNewTextSize < v.getTextSize() / 1.1f || v.getTextSize() * 1.1f < lNewTextSize ) {
//////          v.setTextSize(TypedValue.COMPLEX_UNIT_PX, lParams.mScale_TextSize * lScale_TextViewMeasure_Pre);
//////        }
//////        refreshTextChangedListener(v);
////
//////        log(String.format("onMeasure 1 lParams:%s TVIndex:%d lForTextViewRootHeight:%f", lParams.mTextView_WrapContent_Direction.name(), i, lForTextViewRootHeight));
////        switch (lParams.mTextView_WrapContent_Direction) {
////        case Left: {
////          lForTextViewRootWidth  = updateTextViewWidth( v, lParams.mTextView_WrapContent_Direction, lParams, lForTextViewRootWidth, lForTextViewRootHeight);
////        } break;
////        case Right: {
////          lForTextViewRootWidth  = updateTextViewWidth( v, lParams.mTextView_WrapContent_Direction, lParams, lForTextViewRootWidth, lForTextViewRootHeight);
////        } break;
////        case Center_Horizontal: {
////          lForTextViewRootWidth  = updateTextViewWidth( v, lParams.mTextView_WrapContent_Direction, lParams, lForTextViewRootWidth, lForTextViewRootHeight);
////        } break;
////        case Top: {
////          lForTextViewRootHeight = updateTextViewSize(v, lForTextViewRootWidth, lForTextViewRootHeight, lScale_TextViewMeasure_Pre);
////        } break;
////        case Bottom: {
////          lForTextViewRootHeight = updateTextViewSize(v, lForTextViewRootWidth, lForTextViewRootHeight, lScale_TextViewMeasure_Pre);
////        } break;
////        }
//////        log(String.format("onMeasure 2 lParams:%s TVIndex:%d lForTextViewRootHeight:%f", lParams.mTextView_WrapContent_Direction.name(), i, lForTextViewRootHeight));
//      }
//    }
//
//
//
//    // calculate final size of ScalableLayout
//    // ScalableLayout??? ??????? ????? ??????
//    {
//      log(String.format("onMeasure 2 lScale:%5.3f ChildCount:%d Root:(%10.7f, %10.7f) ForTextView:(%10.7f, %10.7f)",
//        (lRoot_Width/mScale_Root_Width), getChildCount(), lRoot_Width, lRoot_Height, lForTextViewRootWidth, lForTextViewRootHeight));
//      if(Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1 || Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1) {
//        log(String.format("onMeasure 2.1 %b %b", Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1,  Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1));
//        if(lRoot_MeasureSpec_WidthMode == MeasureSpec.EXACTLY && lRoot_MeasureSpec_HeightMode == MeasureSpec.EXACTLY) {
//          // ???? ?? ? ????
//          if(lForTextViewRootWidth / lRoot_Width > lForTextViewRootHeight / lRoot_Height) {
//            lRoot_Height /= (lForTextViewRootWidth / lRoot_Width);
//            lRoot_Width = lForTextViewRootWidth;
//            log(String.format("onMeasure 2.1.1 %b %b", Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1,  Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1));
//          } else {
//            lRoot_Width /= (lForTextViewRootHeight / lRoot_Height);
//            lRoot_Height = lForTextViewRootHeight;
//            log(String.format("onMeasure 2.1.2 %b %b", Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1,  Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1));
//          }
////      } else if(lWidthMode == MeasureSpec.EXACTLY && lHeightMode != MeasureSpec.EXACTLY) {
////        lRootHeight /= (lForTextViewRootWidth / lRootWidth);
////        lRootWidth = lForTextViewRootWidth;
////      } else if(lWidthMode != MeasureSpec.EXACTLY && lHeightMode == MeasureSpec.EXACTLY) {
////        lRootWidth /= (lForTextViewRootHeight / lRootHeight);
////        lRootHeight = lForTextViewRootHeight;
//        } else {
//          lRoot_Width = lForTextViewRootWidth;
//          lRoot_Height = lForTextViewRootHeight;
//          log(String.format("onMeasure 2.1.3 %b %b", Math.abs(lForTextViewRootWidth - lRoot_Width) >= 1,  Math.abs(lForTextViewRootHeight - lRoot_Height) >= 1));
//        }
//
////      postDelayedRequestLayout();
//      }
//    }
//
//    {
//      log(String.format("onMeasure 2 lScale:%5.3f", (lRoot_Width/mScale_Root_Width)));
//      log(String.format("onMeasure 3 lScale:%5.3f ChildCount:%d Root:(%5.3f, %5.3f) ForTextView:(%5.3f, %5.3f)",
//        (lRoot_Width/mScale_Root_Width), getChildCount(), lRoot_Width, lRoot_Height, lForTextViewRootWidth, lForTextViewRootHeight));
//
//      float lScale_TextViewMeasure_Post = lRoot_Width / mScale_Root_Width;
//
//
//
//      // ?????? ?? ??? ???? ?? ?? ???? ????? layoutparams? ?????????
//      // update layoutparams of each child view to arrange finally
//      float lTopMarginFromWeight = (lRoot_Height - (lRoot_Width * mRatioOfWidthHeight))/4;
//      log("  onMeasure ("+lRoot_Width+","+lRoot_Height+") Ratio:"+mRatioOfWidthHeight+" lScale_TextViewMeasure_Post:"+lScale_TextViewMeasure_Post+" lTopMarginFromWeight:"+lTopMarginFromWeight);
//      for (int i=0;i<getChildCount();i++) {
//        View lView = getChildAt(i);
//
//        ScalableLayout.LayoutParams lParams = getChildLayoutParams(lView);
//
//        boolean dif = false;
//
//        if(lParams.leftMargin != Math.round(lScale_TextViewMeasure_Post * lParams.mScale_Left))
//          dif = true;
//        lParams.leftMargin = Math.round(lScale_TextViewMeasure_Post * lParams.mScale_Left);
//
//        if(lParams.topMargin != Math.round(lScale_TextViewMeasure_Post * lParams.mScale_Top + lTopMarginFromWeight))
//          dif = true;
//        lParams.topMargin = Math.round(lScale_TextViewMeasure_Post * lParams.mScale_Top + lTopMarginFromWeight);
//
//        int nParamsWidth = Math.round(lScale_TextViewMeasure_Post * (lParams.mScale_Left + lParams.mScale_Width)) - lParams.leftMargin;
//        if(lParams.width != nParamsWidth)
//          dif = true;
//        lParams.width = nParamsWidth;
//
//        int nParamsHeight = Math.round(lScale_TextViewMeasure_Post * (lParams.mScale_Top + lParams.mScale_Height)) - lParams.topMargin;
//        if(lParams.height != nParamsHeight)
//          dif = true;
//        lParams.height = nParamsHeight;
//
////      if(lView instanceof ScalableLayout) {
////        ScalableLayout lSL = (ScalableLayout) lView;
////        float lDif = ((float)lParams.width/(float)lParams.height) / lSL.mScale_Full_Width/lSL.mScale_Full_Height;
////        if(0.98f < lDif && lDif < 1.02f) {
////
////        }
////      }
//
////      lParams.rightMargin = lWidthSize - lParams.width - lParams.leftMargin;
////      lParams.bottomMargin = lHeightSize - lParams.height - lParams.topMargin;
////
//////      lParams.setMargins(Math.round(lScale * lParams.mScale_Left), Math.round(lScale * lParams.mScale_Top + lTopMarginFromWeight), 0, 0);
////      log("  "+lView.toString()+" "+
////        String.format("(%f, %f, %f, %f)", lParams.mScale_Left, lParams.mScale_Top, lParams.mScale_Width, lParams.mScale_Height)+
////        "->"+
////        String.format("(%f, %f, %f, %f)",
////          lScale * lParams.mScale_Left, lScale * lParams.mScale_Top + lTopMarginFromWeight,
////          lScale * lParams.mScale_Width, lScale * lParams.mScale_Height));
////      log("  "+lView.toString()+" "+
////        String.format("(%f, %f, %f, %f)", lParams.mScale_Left, lParams.mScale_Top, lParams.mScale_Width, lParams.mScale_Height)+
////        "->"+
////        String.format("(%d, %d, %d, %d)", lParams.leftMargin, lParams.topMargin, lParams.width, lParams.height));
//
//
//
//        if(dif) {
//          lView.setLayoutParams(lParams);
//        }
//
////      if(lView instanceof TextView) {
//////        TextView v = (TextView) lView;
//////        if(v.getTextSize() != lParams.mScale_TextSize * lScale) {
//////          v.setTextSize(TypedValue.COMPLEX_UNIT_PX, lParams.mScale_TextSize * lScale);
//////        }
////
////
//////        switch (lParams.mScale_TextViewWrapContentMode) {
//////        case Horizontal: {
//////        } break;
//////        case Vertical: {
//////          dif = updateTextViewHeight(v, lParams);
//////        } break;
//////        }
////      }
////
//////      else if(lView instanceof EditText) {
//////        EditText v = (EditText) lView;
//////        if(v.getTextSize() != lParams.mScale_TextSize * lScale) {
//////          v.setTextSize(TypedValue.COMPLEX_UNIT_PX, lParams.mScale_TextSize * lScale);
//////        }
//////      }
//      }
//    }
//
//    log("onMeasure ================ End   "+this.toString());
//    super.onMeasure(MeasureSpec.makeMeasureSpec(Math.round(lRoot_Width), lRoot_MeasureSpec_WidthMode), MeasureSpec.makeMeasureSpec(Math.round(lRoot_Height), lRoot_MeasureSpec_HeightMode));
//    setMeasuredDimension(Math.round(lRoot_Width), Math.round(lRoot_Height));
//  }


    private boolean isDifferentSufficiently(float pNew, float pOld) {
        return isDifferentSufficiently(pNew, pOld, 1.1f);
    }
    private boolean isDifferentSufficiently(float pNew, float pOld, float pDiffDelta) {
        if( pNew < pOld / pDiffDelta || pOld * pDiffDelta < pNew ) {
            return true;
        }
        return false;
//    float val = (Math.abs(pNew - pOld)*100f/Math.max(pNew + pOld, 200f));
//    boolean ret = val >= 5f;
//    log("isDifferentSufficiently pNew:"+pNew+" pOld:"+pOld+" val:"+val+" ret:"+ret);
//    return ret;
    }

    private void updateTextViewSize(TextView pTV_Text, float pScale_TextViewMeasure_Pre) {
        refreshTextChangedListener(pTV_Text);

        ScalableLayout.LayoutParams lTV_SLLP = getChildLayoutParams(pTV_Text);
        TextView_WrapContent_Direction lTextView_WrapContent_Direction = lTV_SLLP.mTextView_WrapContent_Direction;
        if(lTextView_WrapContent_Direction == TextView_WrapContent_Direction.None) {
            return;
        }

//    switch (lTV_SLLP.mTextView_WrapContent_Direction) {
//    case Left: {
//      lForTextViewRootWidth  = updateTextViewWidth( v, lTV_SLLP.mTextView_WrapContent_Direction, lTV_SLLP, lForTextViewRootWidth, lForTextViewRootHeight);
//    } break;
//    case Right: {
//      lForTextViewRootWidth  = updateTextViewWidth( v, lTV_SLLP.mTextView_WrapContent_Direction, lTV_SLLP, lForTextViewRootWidth, lForTextViewRootHeight);
//    } break;
//    case Center_Horizontal: {
//      lForTextViewRootWidth  = updateTextViewWidth( v, lTV_SLLP.mTextView_WrapContent_Direction, lTV_SLLP, lForTextViewRootWidth, lForTextViewRootHeight);
//    } break;
//    case Top: {
//      lForTextViewRootHeight = updateTextViewSize(v, lForTextViewRootWidth, lForTextViewRootHeight, lScale_TextViewMeasure_Pre);
//    } break;
//    case Bottom: {
//      lForTextViewRootHeight = updateTextViewSize(v, lForTextViewRootWidth, lForTextViewRootHeight, lScale_TextViewMeasure_Pre);
//    } break;
//    }

        float lTextView_ScaleWidth_Old     = lTV_SLLP.getScale_Width();
        float lTextView_ScaleHeight_Old   = lTV_SLLP.getScale_Height();

        float lTextView_ScaleWidth_New     = lTextView_ScaleWidth_Old;
        float lTextView_ScaleHeight_New   = lTextView_ScaleHeight_Old;
        {
            // get new scale size
            // ??? Scale ????? ???
            float lTextSize_New = lTV_SLLP.mScale_TextSize * pScale_TextViewMeasure_Pre;
//      log("updateTextViewHeight 1.1 lTextView_ScaleHeight_Old:"+lTextView_ScaleHeight_Old+" getScaleHeight():"+getScaleHeight()+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
//      log("updateTextViewHeight 1.2 lTextSize_New:"+lTextSize_New+" pTV_Text.getTextSize():"+pTV_Text.getTextSize());
            if(isDifferentSufficiently(lTextSize_New, pTV_Text.getTextSize())) {
//        log("updateTextViewHeight 1.2.1 TextSize:"+pTV_Text.getTextSize()+"->"+lTextSize_New);
                pTV_Text.setTextSize(TypedValue.COMPLEX_UNIT_PX, lTextSize_New);
            }

            switch (lTextView_WrapContent_Direction) {
                case Top:
                case Bottom: {
                    int widthMeasureSpec     = MeasureSpec.makeMeasureSpec((int) (lTextView_ScaleWidth_Old * pScale_TextViewMeasure_Pre), MeasureSpec.EXACTLY);
                    int heightMeasureSpec     = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                    pTV_Text.measure(widthMeasureSpec, heightMeasureSpec);

                    float lTextView_Height_New   = pTV_Text.getMeasuredHeight() * 1.01f;
                    lTextView_ScaleHeight_New   = lTextView_Height_New / pScale_TextViewMeasure_Pre;
//        log("updateTextViewHeight 1.3 lTextSize:"+pTV_Text.getTextSize()+"->"+lTextSize_New);
//        log("updateTextViewHeight 1.4 lTextView_ScaleHeight:"+lTextView_ScaleHeight_Old+"->"+lTextView_ScaleHeight_New);
//        log("updateTextViewHeight 1.5 lTextView_Height:"+pTV_Text.getHeight()+"->"+lTextView_Height_New);
//        " getScaleHeight:"+getScaleHeight()+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
                    // +" lTextView_ScaleHeight_New:"+lTextView_ScaleHeight_New+" getScaleHeight:"+getScaleHeight()+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
                } break;
                case Center_Horizontal:
                case Left:
                case Right: {
                    int widthMeasureSpec     = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
                    int heightMeasureSpec     = MeasureSpec.makeMeasureSpec((int) (lTextView_ScaleHeight_Old * pScale_TextViewMeasure_Pre), MeasureSpec.EXACTLY);
                    pTV_Text.measure(widthMeasureSpec, heightMeasureSpec);

                    float lTextView_Width_New   = pTV_Text.getMeasuredWidth() * 1.01f;
                    lTextView_ScaleWidth_New   = lTextView_Width_New / pScale_TextViewMeasure_Pre;
//        log("updateTextViewWidth 1.3 lTextView_NewWidth:"+lTextView_Width_New+" lTextView_ScaleWidth_New:"+lTextView_ScaleWidth_New);
                } break;
                default:
                    break;
            }
        }
//    log("updateTextViewWidth 1.4 " +
//      " lTextView_ScaleWidth_New:"+lTextView_ScaleWidth_New+" lTextView_ScaleWidth_Old:"+lTextView_ScaleWidth_Old+
//      " lTextView_ScaleHeight_New:"+lTextView_ScaleHeight_New+" lTextView_ScaleHeight_Old:"+lTextView_ScaleHeight_Old);

        if(  isDifferentSufficiently(lTextView_ScaleWidth_New, lTextView_ScaleWidth_Old) == false &&
                isDifferentSufficiently(lTextView_ScaleHeight_New, lTextView_ScaleHeight_Old) == false) {
            return;
        }

        float lTextView_ScaleWidth_Diff   = lTextView_ScaleWidth_New - lTextView_ScaleWidth_Old;
        float lTextView_ScaleHeight_Diff   = lTextView_ScaleHeight_New - lTextView_ScaleHeight_Old;
        if(lTV_SLLP.mTextView_WrapContent_MoveSiblings == true) {
//      log("updateTextViewHeight 1.3 lTextSize_New:"+lTextSize_New+" lTextView_OldScaleHeight:"+lTextView_ScaleHeight_Old+" lTextView_ScaleHeight_New:"+lTextView_ScaleHeight_New+" getScaleHeight:"+getScaleHeight()+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
//      log("updateTextViewHeight 3 lTextSize_New:"+lTextSize_New+" lTextView_Width:"+lTextView_Width+" lTextView_OldScaleHeight:"+lTextView_ScaleHeight_Old+" pRootHeight:"+pRootHeight+" getScaleHeight:"+getScaleHeight()+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
//      log("updateTextViewHeight 3 (lTextView_NewHeight - lTextView_OldHeight):"+(lTextView_NewHeight+"-"+lTextView_Height_Old));
//      log("updateTextViewHeight 3 (lTextView_NewHeight - lTextView_OldHeight):"+(lTextView_NewHeight - lTextView_Height_Old));
//      log("updateTextViewHeight 3 (lTextView_NewHeight - lTextView_OldHeight):"+(Math.abs(lTextView_NewHeight - lTextView_Height_Old)*100f/Math.max(lTextView_NewHeight + lTextView_Height_Old, 200f)));

            for(int i = 0; i < getChildCount(); i++) {
                View v = getChildAt(i);
                if(v == pTV_Text) {
                    continue;
                }
                ScalableLayout.LayoutParams lSLLP = getChildLayoutParams(v);

                switch (lTextView_WrapContent_Direction) {
                    case Top: {
                        // ?????? ??? ?? ?? ?? ??????
                        if(
                                lSLLP.getScale_Bottom() <= lTV_SLLP.getScale_Bottom() &&
                                        (
                                                ( lTV_SLLP.getScale_Left() <= lSLLP.getScale_Left() && lSLLP.getScale_Left() <= lTV_SLLP.getScale_Right())
                                                        ||
                                                        ( lTV_SLLP.getScale_Left() <= lSLLP.getScale_Right() && lSLLP.getScale_Right() <= lTV_SLLP.getScale_Right())
                                                        ||
                                                        ( lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left() && lTV_SLLP.getScale_Right() <= lSLLP.getScale_Right())
                                        )) {
                            moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top()-lTextView_ScaleHeight_Diff);
                        }
                        // ?????? ?????? ?? ???? ??
                        else if( lTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
                                lSLLP.getScale_Top() <= lTV_SLLP.getScale_Top() &&
                                lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left() &&
                                lSLLP.getScale_Right() >= lTV_SLLP.getScale_Right() &&
                                lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom()) {

                            moveChildView(v,
                                    lSLLP.getScale_Left(), lSLLP.getScale_Top()-lTextView_ScaleHeight_Diff,
                                    lSLLP.getScale_Width(), lSLLP.getScale_Height()+lTextView_ScaleHeight_Diff);
                        }
                    } break;
                    case Bottom: {
                        // ?????? ???? ?? ?? ?? ??????
                        if(
                                lSLLP.getScale_Top() >= lTV_SLLP.getScale_Top() &&
                                        (
                                                ( lTV_SLLP.getScale_Left() <= lSLLP.getScale_Left() && lSLLP.getScale_Left() <= lTV_SLLP.getScale_Right())
                                                        ||
                                                        ( lTV_SLLP.getScale_Left() <= lSLLP.getScale_Right() && lSLLP.getScale_Right() <= lTV_SLLP.getScale_Right())
                                                        ||
                                                        ( lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left() && lTV_SLLP.getScale_Right() <= lSLLP.getScale_Right())
                                        )) {
                            moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top()+lTextView_ScaleHeight_Diff);
                        }
                        // ?????? ?????? ?? ???? ??
                        else if( lTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
                                lSLLP.getScale_Top() <= lTV_SLLP.getScale_Top() &&
                                lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left() &&
                                lSLLP.getScale_Right() >= lTV_SLLP.getScale_Right() &&
                                lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom()) {

                            moveChildView(v,
                                    lSLLP.getScale_Left(), lSLLP.getScale_Top(),
                                    lSLLP.getScale_Width(), lSLLP.getScale_Height()+lTextView_ScaleHeight_Diff);
                        }
                    } break;
                    case Left: {
                        // move views on left
                        // ???? ?? ?? ?? ??????
                        if(
                                lSLLP.getScale_Right() <= lTV_SLLP.getScale_Right() &&
                                        (
                                                ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Top() && lSLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom())
                                                        ||
                                                        ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Bottom() && lSLLP.getScale_Bottom() <= lTV_SLLP.getScale_Bottom())
                                                        ||
                                                        ( lTV_SLLP.getScale_Top() >= lSLLP.getScale_Top() &&  lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom())
                                        )) {
                            moveChildView(v, lSLLP.getScale_Left()-lTextView_ScaleWidth_Diff, lSLLP.getScale_Top());
                        }
                        else if( lTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
                                lSLLP.getScale_Top() <= lTV_SLLP.getScale_Top() &&
                                lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left() &&
                                lSLLP.getScale_Right() >= lTV_SLLP.getScale_Right() &&
                                lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom()) {

                            moveChildView(v, lSLLP.getScale_Left()-lTextView_ScaleWidth_Diff, lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Height());
                        }
                    } break;
                    case Right: {
                        // move views on right
                        // ????? ?? ?? ?? ??????
                        if(
                                lSLLP.getScale_Left() >= lTV_SLLP.getScale_Left() &&
                                        (
                                                ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Top() && lSLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom())
                                                        ||
                                                        ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Bottom() && lSLLP.getScale_Bottom() <= lTV_SLLP.getScale_Bottom())
                                                        ||
                                                        ( lTV_SLLP.getScale_Top() >= lSLLP.getScale_Top() &&  lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom())
                                        )) {

                            moveChildView(v, lSLLP.getScale_Left()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Top());
                        }
                        else if( lTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
                                lSLLP.getScale_Top() <= lTV_SLLP.getScale_Top() &&
                                lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left() &&
                                lSLLP.getScale_Right() >= lTV_SLLP.getScale_Right() &&
                                lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom()) {

                            moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff, lSLLP.getScale_Height());
                        }
                    } break;
                    case Center_Horizontal: {
                        // ???? ?? ?? ?? ??????
//          log("Center_Horizontal "
//              + "v:"+v+"\n"
//              + "lSLLP:"+lSLLP+" \n"
//              + "lTV_SLLP:"+lTV_SLLP+" \n"
//              + "1 "+ (lSLLP.getScale_Right() <= lTV_SLLP.getScale_Right()) + "\n"
//              + "2 " + ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Top() && lSLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom()) + "\n"
//              + "3 " + ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Top() && lSLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom()) + "\n"
//              + "4 " + ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Top() && lSLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom())
//              );
                        if(
                                lSLLP.getScale_Right() <= lTV_SLLP.getScale_Right() &&
                                        (
                                                ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Top() && lSLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom())
                                                        ||
                                                        ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Bottom() && lSLLP.getScale_Bottom() <= lTV_SLLP.getScale_Bottom())
                                                        ||
                                                        ( lTV_SLLP.getScale_Top() >= lSLLP.getScale_Top() &&  lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom())
                                        )) {

//            log("moveChildView "
//                + "v:"+v+"\n"
//                + "1 " + ( lSLLP.getScale_Left()-lTextView_ScaleWidth_Diff/2)+"\n"
//                + "2 " + ( lSLLP.getScale_Top() ) + "\n"
//                );
                            moveChildView(v, lSLLP.getScale_Left()-lTextView_ScaleWidth_Diff/2, lSLLP.getScale_Top());
                        }
                        else if( lTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
                                lSLLP.getScale_Top() <= lTV_SLLP.getScale_Top() &&
                                lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left() &&
                                lSLLP.getScale_Right() >= lTV_SLLP.getScale_Right() &&
                                lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom()) {

                            moveChildView(v, lSLLP.getScale_Left()-lTextView_ScaleWidth_Diff/2, lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff/2, lSLLP.getScale_Height());
                        }

//          // ????? ?? ?? ?? ??????
//          if(
//            lSLLP.getScale_Left() >= lTV_SLLP.getScale_Left() &&
//            (
//              ( lTV_SLLP.getScale_Top() <= lTV_SLLP.getScale_Top() && lSLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom())
//              ||
//              ( lTV_SLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom() && lTV_SLLP.getScale_Bottom() <= lTV_SLLP.getScale_Bottom())
//              ||
//              ( lTV_SLLP.getScale_Top() >= lTV_SLLP.getScale_Top() &&  lTV_SLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom())
//              )) {
//
//            moveChildView(v, lTV_SLLP.getScale_Left()+lTextView_ScaleWidth_Diff/2, lTV_SLLP.getScale_Top());
//          }
//          else if( lTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
//            lTV_SLLP.getScale_Top() <= lTV_SLLP.getScale_Top() &&
//            lTV_SLLP.getScale_Left() <= lTV_SLLP.getScale_Left() &&
//            lTV_SLLP.getScale_Right() >= lTV_SLLP.getScale_Right() &&
//            lTV_SLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom()) {
//
////            moveChildView(v, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTV_SLLP.getScale_Width()+lTextView_ScaleWidth_Diff/2, lTV_SLLP.getScale_Height());
//            moveChildView(v, lSLLP.getScale_Left()-lTextView_ScaleWidth_Diff/2, lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff/2, lSLLP.getScale_Height());
//          }


                        // ????? ?? ?? ?? ??????
                        else if(
                                lSLLP.getScale_Left() >= lTV_SLLP.getScale_Left() &&
                                        (
                                                ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Top() && lSLLP.getScale_Top() <= lTV_SLLP.getScale_Bottom())
                                                        ||
                                                        ( lTV_SLLP.getScale_Top() <= lSLLP.getScale_Bottom() && lSLLP.getScale_Bottom() <= lTV_SLLP.getScale_Bottom())
                                                        ||
                                                        ( lTV_SLLP.getScale_Top() >= lSLLP.getScale_Top() &&  lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom())
                                        )) {
                            moveChildView(v, lSLLP.getScale_Left()+lTextView_ScaleWidth_Diff/2, lSLLP.getScale_Top());
                        }
                        else if( lTV_SLLP.mTextView_WrapContent_ResizeSurrounded &&
                                lSLLP.getScale_Top() <= lTV_SLLP.getScale_Top() &&
                                lSLLP.getScale_Left() <= lTV_SLLP.getScale_Left() &&
                                lSLLP.getScale_Right() >= lTV_SLLP.getScale_Right() &&
                                lSLLP.getScale_Bottom() >= lTV_SLLP.getScale_Bottom()) {

                            moveChildView(v, lSLLP.getScale_Left(), lSLLP.getScale_Top(), lSLLP.getScale_Width()+lTextView_ScaleWidth_Diff/2, lSLLP.getScale_Height());
                        }
                    } break;
                    default:
                        break;
                }
            }
        }

        switch (lTextView_WrapContent_Direction) {
            case Top: {
                moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top()-lTextView_ScaleHeight_Diff, lTV_SLLP.getScale_Width(), lTextView_ScaleHeight_New);
            } break;
            case Bottom: {
                moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTV_SLLP.getScale_Width(), lTextView_ScaleHeight_New);
            } break;
            case Left: {
                moveChildView(pTV_Text, lTV_SLLP.getScale_Left()-lTextView_ScaleWidth_Diff, lTV_SLLP.getScale_Top(), lTextView_ScaleWidth_New, lTV_SLLP.getScale_Height());
            } break;
            case Right: {
                moveChildView(pTV_Text, lTV_SLLP.getScale_Left(), lTV_SLLP.getScale_Top(), lTextView_ScaleWidth_New, lTV_SLLP.getScale_Height());
            } break;
            case Center_Horizontal: {
                moveChildView(pTV_Text, lTV_SLLP.getScale_Left()-lTextView_ScaleWidth_Diff/2, lTV_SLLP.getScale_Top(), lTextView_ScaleWidth_New, lTV_SLLP.getScale_Height());
            } break;
            default:
                break;
        }

        if(lTV_SLLP.mTextView_WrapContent_ResizeSurrounded) {
//      log("setScaleSize From:"+getScaleHeight()+" To:"+(getScaleHeight()+lTextView_ScaleHeight_Diff)+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
            setScaleSize(getScaleWidth()+lTextView_ScaleWidth_Diff, getScaleHeight()+lTextView_ScaleHeight_Diff, false);
        }

//    log("updateTextViewHeight 4 lTextView_ScaleHeight_Old:"+lTextView_ScaleHeight_Old+" getScaleHeight():"+getScaleHeight()+" pScale_TextViewMeasure_Pre:"+pScale_TextViewMeasure_Pre);
//    log("updateTextViewHeight ret "+(pRootHeight+"*"+getScaleHeight()+"/"+lRootView_OldScaleHeight));
//    log("updateTextViewHeight ret "+(pRootHeight*getScaleHeight()/lRootView_OldScaleHeight));
//    return pRootHeight*getScaleHeight()/lRootView_OldScaleHeight;
    }




    public static enum TextView_WrapContent_Direction {
        None, Left, Right, Center_Horizontal, Top, Bottom,
    }
    /**
     * ScalableLayout.LayoutParams
     */
    public static class LayoutParams extends FrameLayout.LayoutParams {
        public LayoutParams(Context pContext, AttributeSet pAttrs) { super(pContext, pAttrs); }
        public LayoutParams(
                float pScale_Left, float pScale_Top,
                float pScale_Width, float pScale_Height) {
            this(pScale_Left, pScale_Top, pScale_Width, pScale_Height, Default_Scale_TextSize, TextView_WrapContent_Direction.None, false, true);
        }
        private LayoutParams(
                float pScale_Left, float pScale_Top,
                float pScale_Width, float pScale_Height, float pScale_TextSize,
                TextView_WrapContent_Direction pTextView_WrapContent_Direction, boolean pTextView_WrapContent_ResizeSurrounded, boolean pTextView_WrapContent_MoveSiblings ) {
            super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, Gravity.LEFT | Gravity.TOP);

            setScale_Left(pScale_Left);
            setScale_Top(pScale_Top);
            setScale_Width(pScale_Width);
            setScale_Height(pScale_Height);

            setScale_TextSize(pScale_TextSize);

            setTextView_WrapContent(pTextView_WrapContent_Direction, pTextView_WrapContent_ResizeSurrounded, pTextView_WrapContent_MoveSiblings);
        }

        private LayoutParams(android.view.ViewGroup.LayoutParams pLayoutParams) {
            this(
                    Default_Scale_Left, Default_Scale_Top,
                    Default_Scale_Width, Default_Scale_Height,
                    Default_Scale_TextSize, TextView_WrapContent_Direction.None, false, true);

            width = pLayoutParams.width;
            height = pLayoutParams.height;
            layoutAnimationParameters = pLayoutParams.layoutAnimationParameters;
            gravity = Gravity.LEFT | Gravity.TOP;
        }

        @Override
        public String toString() {
            return String.format("%08x (%6.3f, %6.3f) (%6.3f, %6.3f)", hashCode(), getScale_Left(), getScale_Top(), getScale_Right(), getScale_Bottom());
        }
        private float mScale_Left = Default_Scale_Left;
        public float getScale_Left() { return mScale_Left; }
        public void setScale_Left(float pScale_Left) { mScale_Left = pScale_Left; }
        public float getScale_Right() { return getScale_Left()+getScale_Width(); }

        private float mScale_Top = Default_Scale_Top;
        public float getScale_Top() { return mScale_Top; }
        public void setScale_Top(float pScale_Top) { mScale_Top = pScale_Top; }
        public float getScale_Bottom() { return getScale_Top()+getScale_Height(); }

        private float mScale_Width = Default_Scale_Width;
        public float getScale_Width() { return mScale_Width; }
        public void setScale_Width(float pScale_Width) { mScale_Width = pScale_Width; }

        private float mScale_Height = Default_Scale_Height;
        public float getScale_Height() { return mScale_Height; }
        public void setScale_Height(float pScale_Height) { mScale_Height = pScale_Height; }

        private float mScale_TextSize = -1f;
        public float getScale_TextSize() { return mScale_TextSize; }
        public void setScale_TextSize(float pScale_TextSize) { mScale_TextSize = pScale_TextSize; }

        private TextView_WrapContent_Direction     mTextView_WrapContent_Direction     = TextView_WrapContent_Direction.None;
        private boolean               mTextView_WrapContent_ResizeSurrounded   = false;
        private boolean               mTextView_WrapContent_MoveSiblings     = true;
        public void setTextView_WrapContent(TextView_WrapContent_Direction pTextView_WrapContent_Direction, boolean pTextView_WrapContent_ResizeSurrounded, boolean pTextView_WrapContent_MoveSiblings) {
            mTextView_WrapContent_Direction     = pTextView_WrapContent_Direction;
            mTextView_WrapContent_ResizeSurrounded   = pTextView_WrapContent_ResizeSurrounded;
            mTextView_WrapContent_MoveSiblings     = pTextView_WrapContent_MoveSiblings;
        }
    }










    //////////////// ???? View? ???? ?? ??? //////////////
    /**
     * adds new TextView with (TextSize, Left, Top, Width, Height) parameters
     * DuplicateState is false by default.
     * @param pText
     * @param pScale_TextSize
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created TextView instance
     */
    public TextView addNewTextView(String pText,
                                   float pScale_TextSize,
                                   float pScale_Left, float pScale_Top,
                                   float pScale_Width, float pScale_Height) {
        return addNewTextView(pText, pScale_TextSize, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }

    /**
     * adds new TextView with (TextSize, Left, Top, Width, Height, duplicateState) parameters
     * @param pText
     * @param pScale_TextSize
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @param pDuplicateState
     * @return created TextView instance
     */
    public TextView addNewTextView(String pText,
                                   float pScale_TextSize,
                                   float pScale_Left, float pScale_Top,
                                   float pScale_Width, float pScale_Height,
                                   boolean pDuplicateState) {
        TextView ret = new TextView(getContext());
        addView(ret, pScale_Left, pScale_Top, pScale_Width, pScale_Height);

        setScale_TextSize(ret, pScale_TextSize);
        ret.setText(pText);
        ret.setGravity(Gravity.CENTER);
        ret.setTextColor(Color.BLACK);
        ret.setDuplicateParentStateEnabled(pDuplicateState);

        return ret;
    }

    /**
     * adds new EditText with (TextSize, Left, Top, Width, Height) parameters
     * @param pScale_TextSize
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created EditText instance
     */
    public EditText addNewEditText(float pScale_TextSize, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        EditText ret = new EditText(getContext());
        addView(ret, pScale_Left, pScale_Top, pScale_Width, pScale_Height);

        setScale_TextSize(ret, pScale_TextSize);
        ret.setGravity(Gravity.CENTER);
        ret.setTextColor(Color.BLACK);
        return ret;
    }

    /**
     * adds new ImageView with (Left, Top, Width, Height) parameters
     * Drawable is null by default.
     * DuplicateState is false by default.
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created ImageView instance
     */
    public ImageView addNewImageView(float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        return addNewImageView((Drawable)null, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }

    /**
     * adds new ImageView with (Bitmap, Left, Top, Width, Height) parameters
     * sets Image of ImageView with Bitmap parameter
     * DuplicateState is false by default.
     * @param pBitmap
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created ImageView instance
     */
    public ImageView addNewImageView(Bitmap pBitmap, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        BitmapDrawable bm = new BitmapDrawable(getResources(), pBitmap);
        return addNewImageView(bm, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }
    /**
     * adds new ImageView with (ResourceID, Left, Top, Width, Height) parameters
     * sets Image of ImageView with ResourceID parameter
     * DuplicateState is false by default.
     * @param pResID
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created ImageView instance
     */
    public ImageView addNewImageView(int pResID, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        return addNewImageView(pResID, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }
    /**
     * adds new ImageView with (ResourceID, Left, Top, Width, Height, DuplicateState) parameters
     * sets Image of ImageView with ResourceID parameter
     * @param pResID
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @param pDuplicateState
     * @return created ImageView instance
     */
    public ImageView addNewImageView(int pResID, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height, boolean pDuplicateState) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.ARGB_8888;
        return addNewImageView(
                new BitmapDrawable(getResources(), BitmapFactory.decodeResource(getResources(), pResID, options)),
                pScale_Left, pScale_Top, pScale_Width, pScale_Height, pDuplicateState);
    }
    /**
     * adds new ImageView with (Drawable, Left, Top, Width, Height) parameters
     * sets Image of ImageView with Drawable parameter
     * DuplicateState is false by default.
     * @param drawable
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @return created ImageView instance
     */
    public ImageView addNewImageView(Drawable drawable, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height) {
        return addNewImageView(drawable, pScale_Left, pScale_Top, pScale_Width, pScale_Height, false);
    }
    /**
     * adds new ImageView with (Drawable, Left, Top, Width, Height, DuplicateState) parameters
     * sets Image of ImageView with Drawable parameter
     * @param drawable
     * @param pScale_Left
     * @param pScale_Top
     * @param pScale_Width
     * @param pScale_Height
     * @param pDuplicateState
     * @return
     */
    public ImageView addNewImageView(Drawable drawable, float pScale_Left, float pScale_Top, float pScale_Width, float pScale_Height, boolean pDuplicateState) {
        ImageView lNewImageView = new ImageView(getContext());
        lNewImageView.setImageDrawable(drawable);
        lNewImageView.setScaleType(ScaleType.FIT_XY);
        lNewImageView.setDuplicateParentStateEnabled(pDuplicateState);

        addView(lNewImageView, pScale_Left, pScale_Top, pScale_Width, pScale_Height);
        return lNewImageView;
    }




















    ////////////////////////// Log
    @Override
    public String toString() {
        return String.format("{ScalableLayout:%08x}", this.hashCode());
    }
    void log(String pLog) {
        if(sLogTag_Global != null) {
            Log.e(sLogTag_Global, this+"] "+pLog);
        }
        if(mLogTag_This != null) {
            Log.e(mLogTag_This, this+"] "+pLog);
        }
    }


    private String mLogTag_This = null;
    public String getLogTag_This() {
        return mLogTag_This;
    }
    /**
     * setLoggable("ScalableLayout");
     */
    public void setThisLoggable() {
        setThisLoggable("ScalableLayout");
    }
    /**
     * Log? ???? ???
     * @param pLogTag DDMS Log Tag? ??
     */
    public void setThisLoggable(String pLogTag) {
        mLogTag_This = pLogTag;
    }

    private static String getStackTrace(StackTraceElement[] ste) {
        StringBuilder sb = new StringBuilder();
        for(int i=0;i<ste.length;i++){
            sb.append("\t"+ste[i].toString()+"\n");
        }
        return sb.toString();
    }
//  private static String getStackTrace(Throwable e) {
//    StringBuilder sb = new StringBuilder();
//    printStackTrace(sb, e);
//    while(e.getCause() != null){
//      e = e.getCause();
//      printStackTrace(sb, e);
//    }
//    return sb.toString();
//  }
//  private static void printStackTrace(StringBuilder sb, Throwable e) {
//    sb.append("Caused by: "+e.getClass().getName()+", "+e.getMessage()+"\n");
//    StackTraceElement[] ste = e.getStackTrace();
//    for(int i=0;i<ste.length;i++){
//      sb.append("\t"+ste[i].toString()+"\n");
//    }
//  }



    private static String sLogTag_Global = null;
    /**
     * setLoggable("ScalableLayout");
     */
    public static void setLoggable() {
        setLoggable("ScalableLayout");
    }
    /**
     * Log? ???? ???
     * @param pLogTag DDMS Log Tag? ??
     */
    public static void setLoggable(String pLogTag) {
        sLogTag_Global = pLogTag;
    }

}




Java Source Code List

com.ssomai.android.sample_scalablelayout.ApplicationTest.java
com.ssomai.android.sample_scalablelayout.MainActivity.java
com.ssomai.android.scalablelayout.ScalableLayout.java