Android PhotoView 在方向改变后保持缩放
Android PhotoView Keep Zoom After Orientation Change
我正在使用 Chris Banes 的 PhotoView class 来放大图像并查看它,但我想做到这一点,以便当我改变方向时,照片更改后仍会放大。
我了解如何执行此操作的基础知识,当检测到方向更改时,将调用 onSaveInstanceState,因此我尝试将实例保存在那里,然后在 onCreate 时将其放回 PhotoView被调用。
public class MainActivity extends ActionBarActivity
{
PhotoView mPhotoView;
@Override
protected void onCreate( Bundle aSavedInstanceState )
{
super.onCreate( aSavedInstanceState );
mPhotoView = new PhotoView(this);
mPhotoView.setMaximumScale( 12 );
setContentView( mPhotoView );
mPhotoView.setImageResource( R.drawable.vm_app_icon);
if (aSavedInstanceState != null)
{
RectF theRect = aSavedInstanceState.getParcelable( "Rect" );
if ( theRect != null)
{
Matrix theMatrix = new Matrix();
theMatrix.setScale( theRect.bottom, theRect.left, theRect.right, theRect.top );
mPhotoView.setDisplayMatrix( theMatrix );
}
}
}
@Override
protected void onSaveInstanceState( final Bundle outState )
{
super.onSaveInstanceState( outState );
RectF theRect = mPhotoView.getDisplayRect();
if (theRect != null)
{
outState.putParcelable( "Rect", theRect );
}
}
}
但这行不通。我应该在包中存储什么才能应用回 PhotoView 以保持缩放级别?
好的,经过大约 10 个小时的尝试,我已经弄明白了。
为了保存缩放级别,我需要在 Bundle 中保存两个东西,Scale(缩放级别)和 DisplayRect(RectF 类型)。
缩放级别 - MinScale 和 MaxScale 之间的数字,在我的实例中介于 1 和 16
RectF 包含四个值,出于某种原因,它们是当前视图的左上角相对于当前屏幕方向的坐标。尽管它包含左上角的坐标,但我不想围绕它旋转,我想围绕中心旋转,所以我需要找到矩形的中心,然后将该值除以 "ScreenBase"是一个将值标准化并使其能够转换为差异平面的值。这是我的保存方式:
@Override
protected void onSaveInstanceState( final Bundle outState )
{
super.onSaveInstanceState( outState );
Matrix theMatrix = mPhotoView.getDisplayMatrix();
float[] theFloat = new float[9];
theMatrix.getValues( theFloat );
RectF theRect = mPhotoView.getDisplayRect();
if (theRect != null)
{
if( theRect.left > ( mViewWidth / 2 ) || ( theRect.left >= 0 ) )
{
theRect.left = 0;
}
else
{
theRect.left = ( theRect.left - ( mViewWidth / 2 ) ) / mScreenBase;
}
if( theRect.top > ( mViewHeight / 2 ) || ( theRect.top >= 0 ) )
{
theRect.top = 0;
}
else
{
theRect.top = ( theRect.top - ( mViewHeight / 2 ) ) / mScreenBase;
}
outState.putParcelable( "RectF", theRect );
outState.putFloat( "ZoomLevel", mPhotoView.getScale() );
}
}
然后当我们在另一边拿起它时,我们必须对数字进行大量操作才能使新屏幕的左上角 space 以同一位置为中心(并操作如果出现边界问题,我是这样做的:
@Override
protected void onCreate( final Bundle aSavedInstanceState )
{
super.onCreate( aSavedInstanceState );
mPhotoView = new PhotoView( this );
mPhotoView.setMaximumScale( 16 );
setContentView( mPhotoView );
mPhotoView.setImageResource( R.drawable.vm_app_icon );
mPhotoView.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener()
{
public boolean onPreDraw()
{
mPhotoView.getViewTreeObserver().removeOnPreDrawListener( this );
mViewHeight = mPhotoView.getMeasuredHeight();
mViewWidth = mPhotoView.getMeasuredWidth();
Matrix theMatrix = mPhotoView.getDisplayMatrix();
theMatrix.getValues( mBaseMatrixValues );
mScreenBase = mBaseMatrixValues[ 0 ];
int theWidth = mPhotoView.getWidth();
Log.e(TAG, theWidth + "");
if( aSavedInstanceState != null )
{
float[] theFloats = new float[ 9 ];
float theZoom = aSavedInstanceState.getFloat( "ZoomLevel" );
RectF theRect = aSavedInstanceState.getParcelable( "RectF" );
theFloats[ 0 ] = theZoom;
theFloats[ 4 ] = theZoom;
theFloats[ 2 ] = ( theRect.left * mScreenBase ) - ( theZoom * mBaseMatrixValues[ 2 ] ) + ( mViewWidth / 2 ); //Left
theFloats[ 5 ] = ( theRect.top * mScreenBase ) - ( theZoom * mBaseMatrixValues[ 5 ] ) + ( mViewHeight / 2 ); //Top
theFloats[ 8 ] = (float) 1.0;
theFloats = CheckBoundaries( theZoom, theFloats, theRect );
theMatrix.setValues( theFloats );
mPhotoView.setDisplayMatrix( theMatrix ); //Sets the mSuppMatrix in the PhotoViewAttacher
Matrix theImageViewMatrix = mPhotoView.getDisplayMatrix(); //Gets the new mDrawMatrix
mPhotoView.setImageMatrix( theImageViewMatrix ); //And applies it to the PhotoView (catches out of boundaries problems)
}
return true;
}
} );
}
private float[] CheckBoundaries(final float aZoom, float[] aFloats, final RectF aRect )
{
if( aZoom == 1.0 ) //If the zoom is all the way out
{
aFloats[ 2 ] = 0;
aFloats[ 5 ] = 0;
return aFloats;
}
theMaxLeftValue = ( ( mViewHeight * aZoom ) - mViewWidth + ( aZoom * mBaseMatrixValues[ 2 ] ) );
theMaxTopValue = ( ( mViewWidth * aZoom ) - mViewHeight + ( aZoom * mBaseMatrixValues[ 5 ] ) );
if( Math.abs( aFloats[ 2 ] ) > ( theMaxLeftValue ) )
{
aFloats[ 2 ] = -Math.abs( theMaxLeftValue ) + 10;
}
else if( Math.abs( aFloats[ 2 ] ) < ( aZoom * mBaseMatrixValues[ 2 ] ) )
{
aFloats[ 2 ] = -( aZoom * mBaseMatrixValues[ 2 ] );
}
if( Math.abs( aFloats[ 5 ] ) > ( theMaxTopValue ) )
{
aFloats[ 5 ] = -Math.abs( theMaxTopValue ) + 10;
}
else if( Math.abs( aFloats[ 5 ] ) < ( aZoom * mBaseMatrixValues[ 5 ] ) )
{
aFloats[ 5 ] = -( aZoom * mBaseMatrixValues[ 5 ] );
}
if( aFloats[ 2 ] > 0 )
aFloats[ 2 ] = -( mViewWidth / 2 );
else if( aFloats[ 5 ] > 0 )
aFloats[ 5 ] = -( mViewHeight / 2 );
return aFloats;
}
我正在使用 Chris Banes 的 PhotoView class 来放大图像并查看它,但我想做到这一点,以便当我改变方向时,照片更改后仍会放大。
我了解如何执行此操作的基础知识,当检测到方向更改时,将调用 onSaveInstanceState,因此我尝试将实例保存在那里,然后在 onCreate 时将其放回 PhotoView被调用。
public class MainActivity extends ActionBarActivity
{
PhotoView mPhotoView;
@Override
protected void onCreate( Bundle aSavedInstanceState )
{
super.onCreate( aSavedInstanceState );
mPhotoView = new PhotoView(this);
mPhotoView.setMaximumScale( 12 );
setContentView( mPhotoView );
mPhotoView.setImageResource( R.drawable.vm_app_icon);
if (aSavedInstanceState != null)
{
RectF theRect = aSavedInstanceState.getParcelable( "Rect" );
if ( theRect != null)
{
Matrix theMatrix = new Matrix();
theMatrix.setScale( theRect.bottom, theRect.left, theRect.right, theRect.top );
mPhotoView.setDisplayMatrix( theMatrix );
}
}
}
@Override
protected void onSaveInstanceState( final Bundle outState )
{
super.onSaveInstanceState( outState );
RectF theRect = mPhotoView.getDisplayRect();
if (theRect != null)
{
outState.putParcelable( "Rect", theRect );
}
}
}
但这行不通。我应该在包中存储什么才能应用回 PhotoView 以保持缩放级别?
好的,经过大约 10 个小时的尝试,我已经弄明白了。
为了保存缩放级别,我需要在 Bundle 中保存两个东西,Scale(缩放级别)和 DisplayRect(RectF 类型)。
缩放级别 - MinScale 和 MaxScale 之间的数字,在我的实例中介于 1 和 16
RectF 包含四个值,出于某种原因,它们是当前视图的左上角相对于当前屏幕方向的坐标。尽管它包含左上角的坐标,但我不想围绕它旋转,我想围绕中心旋转,所以我需要找到矩形的中心,然后将该值除以 "ScreenBase"是一个将值标准化并使其能够转换为差异平面的值。这是我的保存方式:
@Override
protected void onSaveInstanceState( final Bundle outState )
{
super.onSaveInstanceState( outState );
Matrix theMatrix = mPhotoView.getDisplayMatrix();
float[] theFloat = new float[9];
theMatrix.getValues( theFloat );
RectF theRect = mPhotoView.getDisplayRect();
if (theRect != null)
{
if( theRect.left > ( mViewWidth / 2 ) || ( theRect.left >= 0 ) )
{
theRect.left = 0;
}
else
{
theRect.left = ( theRect.left - ( mViewWidth / 2 ) ) / mScreenBase;
}
if( theRect.top > ( mViewHeight / 2 ) || ( theRect.top >= 0 ) )
{
theRect.top = 0;
}
else
{
theRect.top = ( theRect.top - ( mViewHeight / 2 ) ) / mScreenBase;
}
outState.putParcelable( "RectF", theRect );
outState.putFloat( "ZoomLevel", mPhotoView.getScale() );
}
}
然后当我们在另一边拿起它时,我们必须对数字进行大量操作才能使新屏幕的左上角 space 以同一位置为中心(并操作如果出现边界问题,我是这样做的:
@Override
protected void onCreate( final Bundle aSavedInstanceState )
{
super.onCreate( aSavedInstanceState );
mPhotoView = new PhotoView( this );
mPhotoView.setMaximumScale( 16 );
setContentView( mPhotoView );
mPhotoView.setImageResource( R.drawable.vm_app_icon );
mPhotoView.getViewTreeObserver().addOnPreDrawListener( new ViewTreeObserver.OnPreDrawListener()
{
public boolean onPreDraw()
{
mPhotoView.getViewTreeObserver().removeOnPreDrawListener( this );
mViewHeight = mPhotoView.getMeasuredHeight();
mViewWidth = mPhotoView.getMeasuredWidth();
Matrix theMatrix = mPhotoView.getDisplayMatrix();
theMatrix.getValues( mBaseMatrixValues );
mScreenBase = mBaseMatrixValues[ 0 ];
int theWidth = mPhotoView.getWidth();
Log.e(TAG, theWidth + "");
if( aSavedInstanceState != null )
{
float[] theFloats = new float[ 9 ];
float theZoom = aSavedInstanceState.getFloat( "ZoomLevel" );
RectF theRect = aSavedInstanceState.getParcelable( "RectF" );
theFloats[ 0 ] = theZoom;
theFloats[ 4 ] = theZoom;
theFloats[ 2 ] = ( theRect.left * mScreenBase ) - ( theZoom * mBaseMatrixValues[ 2 ] ) + ( mViewWidth / 2 ); //Left
theFloats[ 5 ] = ( theRect.top * mScreenBase ) - ( theZoom * mBaseMatrixValues[ 5 ] ) + ( mViewHeight / 2 ); //Top
theFloats[ 8 ] = (float) 1.0;
theFloats = CheckBoundaries( theZoom, theFloats, theRect );
theMatrix.setValues( theFloats );
mPhotoView.setDisplayMatrix( theMatrix ); //Sets the mSuppMatrix in the PhotoViewAttacher
Matrix theImageViewMatrix = mPhotoView.getDisplayMatrix(); //Gets the new mDrawMatrix
mPhotoView.setImageMatrix( theImageViewMatrix ); //And applies it to the PhotoView (catches out of boundaries problems)
}
return true;
}
} );
}
private float[] CheckBoundaries(final float aZoom, float[] aFloats, final RectF aRect )
{
if( aZoom == 1.0 ) //If the zoom is all the way out
{
aFloats[ 2 ] = 0;
aFloats[ 5 ] = 0;
return aFloats;
}
theMaxLeftValue = ( ( mViewHeight * aZoom ) - mViewWidth + ( aZoom * mBaseMatrixValues[ 2 ] ) );
theMaxTopValue = ( ( mViewWidth * aZoom ) - mViewHeight + ( aZoom * mBaseMatrixValues[ 5 ] ) );
if( Math.abs( aFloats[ 2 ] ) > ( theMaxLeftValue ) )
{
aFloats[ 2 ] = -Math.abs( theMaxLeftValue ) + 10;
}
else if( Math.abs( aFloats[ 2 ] ) < ( aZoom * mBaseMatrixValues[ 2 ] ) )
{
aFloats[ 2 ] = -( aZoom * mBaseMatrixValues[ 2 ] );
}
if( Math.abs( aFloats[ 5 ] ) > ( theMaxTopValue ) )
{
aFloats[ 5 ] = -Math.abs( theMaxTopValue ) + 10;
}
else if( Math.abs( aFloats[ 5 ] ) < ( aZoom * mBaseMatrixValues[ 5 ] ) )
{
aFloats[ 5 ] = -( aZoom * mBaseMatrixValues[ 5 ] );
}
if( aFloats[ 2 ] > 0 )
aFloats[ 2 ] = -( mViewWidth / 2 );
else if( aFloats[ 5 ] > 0 )
aFloats[ 5 ] = -( mViewHeight / 2 );
return aFloats;
}