Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my image clipped after scaling?

I am having a strange problem. I scale an image and, while scaling works correctly, the image always gets clipped. I tried different scale types - things changed but I never could make it work.

Just to be clear, here's what I need to solve: 1. I have a HorizontalScrollView around the ImageView and a ScrollView around the HorizontalView. 2. I scroll around (using scrollTo of both scroll views) and, upon a certain event, zoom in. 3. What I'd like to happen is for the ImageView to scale around my current scroll position.

Here's the layout:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none"
        android:overScrollMode="never">

        <HorizontalScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:scrollbars="none"
            android:overScrollMode="never">

            <ImageView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_margin="3dp"
                android:scaleType="fitCenter" />
        </HorizontalScrollView>
    </ScrollView>
</FrameLayout>

And here's the scaling code (originalWidth/originalHeight are calculated at scale of 1; targetView points to the ImageView):

public synchronized void changeScale(float newScaleFactor) {
    this.scaleFactor = Math.max(min_zoom, Math.min(newScaleFactor, max_zoom));
    if (targetView != null && originalWidth > 0) {
        int newWidth = (int)(originalWidth * scaleFactor);
        int newHeight = (int)(originalHeight * scaleFactor);
        onScaleChanged(targetView, scaleFactor, newWidth, newHeight);
    }
}


public void onScaleChanged(View targetView, float scaleFactor, int newWidth, int newHeight) {
    ViewGroup.LayoutParams layoutParams = targetView.getLayoutParams();
    layoutParams.width = newWidth;
    layoutParams.height = newHeight;

    // This is needed to increase the pane size (rather than zoom within the initial layout)
    targetView.setLayoutParams(layoutParams);
    // Tell the system to recalculate the layout
    targetView.requestLayout();

    // This is needed to specify the center of scaling
    HorizontalScrollView horizontalScrollView = (HorizontalScrollView)targetView.getParent();
    ScrollView vertScrollView = (ScrollView)horizontalScrollView.getParent();
    // ~~~ the pivot points are probably wrong 
    targetView.setPivotX(horizontalScrollView.getScrollX() * scaleFactor);
    targetView.setPivotY(vertScrollView.getScrollY() * scaleFactor);

    // This is needed for actual zooming
    targetView.setScaleX(scaleFactor);
    targetView.setScaleY(scaleFactor);
};


public void zoomIn(float scaleDelta) {
    changeScale(scaleFactor + scaleDelta);
}

public void zoomOut(float scaleDelta) {
    changeScale(scaleFactor - scaleDelta);
}

Question 1: How do I prevent clipping? I can't find the right combination of scaleType and layout resizing.

Question 2: When I use setScaleX/setScaleY, should my pivot be calculated after applying the new scale factor or does the renderer take care of that automatically?

like image 296
Yevgeniy Avatar asked Apr 21 '14 02:04

Yevgeniy


1 Answers

After updating the scale you need to invalidate(), and requestLayout() the views.

targetView.invalidate();
targetView.requestLayout();

I usually calculate the scale differently for images. You could try to scale the image view using the MATRIX scale type. You will need to know the size of your bound DPI.

// Get the scaled DPI
int boundBoxInDp = getResources().getDisplayMetrics().densityDpi * scaleFactor

// Determine how much to scale: the dimension requiring less scaling is
// closer to its side. This way the image always stays inside your
// bound AND either x/y axis touches the bound but neither will go over.
float xScale = ((float) boundBoxInDp) / newWidth;
float yScale = ((float) boundBoxInDp) / newHeight;
float scale = (xScale <= yScale) ? xScale : yScale;

// scale using our calculated scale
targetView.setScaleX(scale);
targetView.setScaleY(scale);

As to your second question about the Pivot. That will probably need to be set to the center of the visible scroll area. The the scrollable area should be increased when you change the image size since you are using FIT_CENTER;

like image 138
NeoVance Avatar answered Oct 16 '22 14:10

NeoVance