问题追踪:ImageView执行缩放动画ScaleAnimation之后,图像显示不全的问题。

问题:我有一个ScrollView,嵌套了一个垂直向的LinearLayout,然后这个LinearLayout中有多个ImageView控件,分别显示自己的图像。

接着我新建了一个放大的ScaleAnimation动画,然后设置给LinearLayout或ScrollView。现在我通过监听ScrollView来滚动LinearLayout,使得放大后的ImageView可以全部看得见。

但是当我fling滑动之后,重新设置ImageView的Bitmap,此时的Bitmap虽然是跟着放大了的效果,但是被裁减了,显示不全。

解决方案:一开始,我去ScrollView和LinearLayout的onDraw方法和draw方法中查看,没有发现有什么异常,(by the way,我发现LinearLayout竟然真的能像ListView一样可以设置Divider,我之前没有发现过哟!),然后我去ImageView的onDraw方法中查看,发现有一个mDrawable,这个就是背景Drawable,还有一个mDrawMatrix,如果mDrawMatrix==null,则直接绘制mDrawable,接着我去看了一下这个mDrawMatrix是怎么赋值的。

    @Override
    protected boolean setFrame(int l, int t, int r, int b) {
        boolean changed = super.setFrame(l, t, r, b);
        mHaveFrame = true;
        configureBounds();
        return changed;
    }

    private void configureBounds() {
        if (mDrawable == null || !mHaveFrame) {
            return;
        }

        int dwidth = mDrawableWidth;
        int dheight = mDrawableHeight;

        int vwidth = getWidth() - mPaddingLeft - mPaddingRight;
        int vheight = getHeight() - mPaddingTop - mPaddingBottom;

        boolean fits = (dwidth < 0 || vwidth == dwidth) &&
                       (dheight < 0 || vheight == dheight);

        if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
            /* If the drawable has no intrinsic size, or we're told to
                scaletofit, then we just fill our entire view.
            */
            mDrawable.setBounds(0, 0, vwidth, vheight);
            mDrawMatrix = null;
        } else {
            // We need to do the scaling ourself, so have the drawable
            // use its native size.
            mDrawable.setBounds(0, 0, dwidth, dheight);

            if (ScaleType.MATRIX == mScaleType) {
                // Use the specified matrix as-is.
                if (mMatrix.isIdentity()) {
                    mDrawMatrix = null;
                } else {
                    mDrawMatrix = mMatrix;
                }
            } else if (fits) {
                // The bitmap fits exactly, no transform needed.
                mDrawMatrix = null;
            } else if (ScaleType.CENTER == mScaleType) {
                // Center bitmap in view, no scaling.
                mDrawMatrix = mMatrix;
                mDrawMatrix.setTranslate((int) ((vwidth - dwidth) * 0.5f + 0.5f),
                                         (int) ((vheight - dheight) * 0.5f + 0.5f));
            } else if (ScaleType.CENTER_CROP == mScaleType) {
                mDrawMatrix = mMatrix;

                float scale;
                float dx = 0, dy = 0;

                if (dwidth * vheight > vwidth * dheight) {
                    scale = (float) vheight / (float) dheight; 
                    dx = (vwidth - dwidth * scale) * 0.5f;
                } else {
                    scale = (float) vwidth / (float) dwidth;
                    dy = (vheight - dheight * scale) * 0.5f;
                }

                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate((int) (dx + 0.5f), (int) (dy + 0.5f));
            } else if (ScaleType.CENTER_INSIDE == mScaleType) {
                mDrawMatrix = mMatrix;
                float scale;
                float dx;
                float dy;
                
                if (dwidth <= vwidth && dheight <= vheight) {
                    scale = 1.0f;
                } else {
                    scale = Math.min((float) vwidth / (float) dwidth,
                            (float) vheight / (float) dheight);
                }
                
                dx = (int) ((vwidth - dwidth * scale) * 0.5f + 0.5f);
                dy = (int) ((vheight - dheight * scale) * 0.5f + 0.5f);

                mDrawMatrix.setScale(scale, scale);
                mDrawMatrix.postTranslate(dx, dy);
            } else {
                // Generate the required transform.
                mTempSrc.set(0, 0, dwidth, dheight);
                mTempDst.set(0, 0, vwidth, vheight);
                
                mDrawMatrix = mMatrix;
                mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));
            }
        }
    }

很显然,这个mDrawMatrix会根据mScaleType的值来操作,因为我的程序内mScaleType是ScaleType.FIT_XY,所以执行了这段:

if (dwidth <= 0 || dheight <= 0 || ScaleType.FIT_XY == mScaleType) {
  mDrawable.setBounds(0, 0, vwidth, vheight);   mDrawMatrix = null;
}

这个mDrawable被设置成控件本身的宽高了。

然后我试图查找下当控件被执行动画之后,这个bounds会不会也被设置了,我找到了一个方法:

    /**
     * Return the visible drawing bounds of your view. Fills in the output
     * rectangle with the values from getScrollX(), getScrollY(),
     * getWidth(), and getHeight(). These bounds do not account for any
     * transformation properties currently set on the view, such as
     * {@link #setScaleX(float)} or {@link #setRotation(float)}.
     *
     * @param outRect The (scrolled) drawing bounds of the view.
     */
    public void getDrawingRect(Rect outRect) {
        outRect.left = mScrollX;
        outRect.top = mScrollY;
        outRect.right = mScrollX + (mRight - mLeft);
        outRect.bottom = mScrollY + (mBottom - mTop);
    }

这个方法返回你的视图的绘制区域的边距信息,其中有一句话(These bounds do not account for any * transformation properties currently set on the view, such as * {@link #setScaleX(float)} or {@link #setRotation(float)}.) ,意思应该是这个bounds是不会被动画所影响的。好吧。。。感觉没有头绪了啊。。。

接着我去Google了一下:android ImageView after ScaleAnimation draw not complete

在倒数几条搜索结果中,

我找到了:ImageView scale animation cropping(link:https://code.google.com/p/android/issues/detail?id=20333),内容如下:

Reported by halfs...@gmail.com, Sep 26, 2011

I have a project that requires a fairly simple animation.
There is a ImageView created this way:
        <ImageView android:src="@drawable/letter3" android:clipChildren="false" android:clipToPadding="false" android:cropToPadding="false" android:id="@+id/startmessage" android:adjustViewBounds="true" android:layout_width="wrap_content" android:scaleType="fitCenter" android:layout_centerVertical="true" android:layout_centerHorizontal="true"></ImageView>

As I understand it wrap_content should keep the size of the view big enough to contain the image within.
Then in code I set an image and start an animation that fades out and scales up the image:

final ImageView v = (ImageView)findViewById(R.id.startmessage);
                fadeout.setAnimationListener(null);
                v.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.letter3));
                v.startAnimation(fadeout);

Here is the animation code:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/accelerate_interpolator"
     android:shareInterpolator="true">
    <alpha  android:fromAlpha = "1.0"
            android:toAlpha = "0.0"
            android:duration="1000" 
    />
</set>


The problem is that when the scaling animation begins the drawing rect does not seem to change so the image is cropped when it becomes larger. The problem occurs only on android 2.3.3 I believe, on device and on emulator as well. I played around with all the scaleType settings for the imageview but it didn't fix the problem.

Sep 26, 2011
#1 halfs...@gmail.com

Correction - this is how the animation is defined:


<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
     android:interpolator="@android:anim/accelerate_interpolator"
     android:shareInterpolator="true">
    <alpha  android:fromAlpha = "1.0"
            android:toAlpha = "0.0"
            android:duration="1000" 
    />
    <scale android:fromXScale="1.0"
           android:toXScale="6.0"
           android:fromYScale="1.0"
           android:toYScale="6.0"
           android:pivotX="50%"
           android:pivotY="50%"
           android:duration="1000"
    />
</set>

Sep 26, 2011
Project Member #2 romain...@android.com

The ImageView will be cropped by its parent. You need to disable children clipping on the parent and make the parent big enough.

Status: Declined
Sep 27, 2011
#3 halfs...@gmail.com

The parent is fullscreen and it also is set to not crop the children, also this behavior is observer only on 2.3.3, on  2.2 and lower everything is working fine.

    <RelativeLayout android:layout_width="fill_parent" android:layout_height="fill_parent" android:clipChildren="false" android:clipToPadding="false" android:id="@+id/startlayout" android:visibility="invisible">
        <ImageView android:layout_height="wrap_content" android:src="@drawable/letter3" android:layout_width="wrap_content" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:scaleType="fitCenter" android:id="@+id/startmessage"></ImageView>
    </RelativeLayout>

 

意思是你的父容器的clipChildren属性默认为true,导致了这个裁剪现象的发生。现在你只需要将ScrollView和LinearLayout的clipChildren属性设置为true即可。

就目前来看,我的三星i9250是没问题,另外我用Genymotion模拟器模拟4.3的Nexus4也没问题,其他版本的未知。

 

 

 

 
posted @ 2014-12-18 18:02  雪佛兰,热爱我的热爱  阅读(4392)  评论(0编辑  收藏  举报