問題:我有一個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也沒問題,其他版本的未知。