开发者

Scale a view and its layered subviews relatively

开发者 https://www.devze.com 2023-03-16 05:36 出处:网络
(This is somewhat a follow-up on Android: How do you scale multiple views together?) My task is to port an iPhone / iPad app on Android that consists of a simple image view on which animations are la

(This is somewhat a follow-up on Android: How do you scale multiple views together?)

My task is to port an iPhone / iPad app on Android that consists of a simple image view on which animations are layered on top in absolute positions. While this sounds rather easy on iOS where you only have to target a few possible screen sizes, it gets rather messy with Android.

My current setup is this: A RelativeLayout in which I place my main (background) image on left = 0, top = 0 and multiple ViewFlipper instances used as "animation containers" that are positioned relatively to the upper left corner of the parent layout instance.

This approach has two basic problems:

  1. The positioned "animations" are mis-positioned as soon as the actual size of the layout does not match the size of the main background开发者_运维百科 image.

  2. The positioned "animations" are also mis-sized, because since they usually have "enough space" around themselves, Android doesn't scale them to fit into the RelativeLayout (nor would it scale them relatively to the original background.

Since the animations itself must be interactive, its not a solution to place and position all of the animations on a transparent layer that has the same size as the main (background) image, as they'd overlap each other and only the upper-most would be interactive at all.

I thought of different solutions:

  1. To get the the scale factor of the main image, I could retrieve its measuredWidth and measuredHeight and set this into relation of the original width and height of the view. Then I'd use this scale factor for custom positioning and eventually custom scaling. But, apparently the measuredWidth/-Height properties are only set during the onMeasure() call and this is called after the component tree was built, so I don't know if this solution is feasible at all.

  2. Implement my own layout manager and scale / position the views accordingly. I had a look at the implementation of RelativeLayout, but have to admit that the onMeasure() method scares me a bit.

What would you do in my case? Is there anything I haven't yet taken into account?

Thanks in advance.


Well, answering my own question - here is the way I resolved the issue:

  1. I placed the background image on the top of my ImageView with ImageView.setScaleType(ScaleType.FIT_START)
  2. I calculated the scale factor of my background image like so:
    WindowManager mgr = (WindowManager) context
                .getSystemService(Context.WINDOW_SERVICE);
    DisplayMetrics metrics = new DisplayMetrics();
    mgr.getDefaultDisplay().getMetrics(metrics);
    Drawable image = context.getResources().getDrawable(R.drawables.someImage);
    float scale = metrics.widthPixels / (float) image.getIntrinsicWidth();
  1. Finally, I used this scale in a custom ImageView class that loads the overlays to position and scale the view properly:
    public class OverlayImage extends ImageView
    {
        private int imgWidth, imgHeight;
        private final float scale;

        public OverlayImage(Context context, int xPos, int yPos, float scale)
        {
            super(context);
            this.scale = scale;

            LayoutParams animParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
                    LayoutParams.WRAP_CONTENT);
            animParams.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
            animParams.addRule(RelativeLayout.ALIGN_PARENT_TOP);
            animParams.leftMargin = (int) (scale * xPos);
            animParams.topMargin = (int) (scale * yPos);
            setLayoutParams(animParams);

            Drawable dr = context.getResources().getDrawable(R.id.someImage);
            setBackgroundDrawable(dr);
            imgWidth = dr.getIntrinsicWidth();
            imgHeight = dr.getIntrinsicHeight();
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            setMeasuredDimension((int) (scale * imgWidth),
                    (int) (scale * imgHeight));
        }
    }


I lately needed to do something similar, i also had to port a IPad app to android, the screen had many images that had to be in specific locations.

I solved this slightly differently, absolute layout, and run through all the views and set the coordinated and size of each.

//This gets the scale of the screen change:
DisplayMetrics displaymetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
Drawable image = getResources().getDrawable(R.drawable.background_image);
float scaleW = displaymetrics.widthPixels / (float)image.getIntrinsicWidth();
float scaleH = displaymetrics.heightPixels / (float)image.getIntrinsicHeight();

//And this scales each view accordingly:
for(int i = 0; i < mainLayout.getChildCount(); i++)
{
    View v = mainLayout.getChildAt(i);
    v.setLayoutParams(new AbsoluteLayout.LayoutParams(
            Math.round(scaleW * v.getMeasuredWidth()),
            Math.round(scaleH * v.getMeasuredHeight()),
            Math.round(scaleW * ((AbsoluteLayout.LayoutParams)v.getLayoutParams()).x),
            Math.round(scaleH * ((AbsoluteLayout.LayoutParams)v.getLayoutParams()).y)));
}
0

精彩评论

暂无评论...
验证码 换一张
取 消