目录
4.22 加载手机磁盘里的图片文件
4.23 动态放大缩小ImageView里的图片
| |
|
4.24 动态旋转图片 | |
|
4.22 加载手机磁盘里的图片文件 | |
|
4.23 动态放大缩小ImageView里的图片 | |
|
本范例将运用Martix(android.graphics.Matrix)对象的postScale()方法来实现在手机上缩放图片的功能,在实现本范例前,需先在/res/drawable/中放入一张图片。以本范例而言,图片文件路径为/res/drawable/ex04_23.png,程序会预先将图片加载到ImageView Widget中,并设计两个Button,一个有放大的功能、一个有缩小的功能,以显示图片放大缩小的效果。为了避免ImageView在放大的过程中,无止境地超过屏幕的尺寸,程序将在放大阶段控制图片的最大宽高。
主程序设计
程序中的small()方法会将图片缩小为原尺寸的4/5,big()方法会将图片放大为原尺寸的1.25倍,当放大按钮的onClick事件被触发时,会运行big()方法;缩小按钮的onClick事件被触发时,则运行small()方法。
程序中以Matrix对象搭配Bitmap的createBitmap()方法来对图片进行缩放,并利用DisplayMetrics对象来取得屏幕显示大小,用以控制图片放大后的尺寸不会超过屏幕显示的区域。
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.AbsoluteLayout;
import android.widget.Button;
import android.widget.ImageView;
public class ex4_23 extends Activity {
/* 相关变量声明 */
private ImageView mImageView;
private Button mButton01;
private Button mButton02;
private AbsoluteLayout layout1;
private Bitmap bmp;
private int id=0;
private int displayWidth;
private int displayHeight;
private float scaleWidth=1;
private float scaleHeight=1;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
/*加载main.xml Layout */
setContentView(R.layout.main);
/* 取得屏幕分辨率大小 */
DisplayMetrics dm=new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(dm);
displayWidth=dm.widthPixels;
/* 屏幕高度须扣除下方Button高度 */
displayHeight=dm.heightPixels-80;
/* 初始化相关变量 */
bmp=BitmapFactory.decodeResource(getResources(),R.drawable.ex04_23);
mImageView = (ImageView)findViewById(R.id.myImageView);
layout1 = (AbsoluteLayout)findViewById(R.id.layout1);
mButton01 = (Button)findViewById(R.id.myButton1);
mButton02 = (Button)findViewById(R.id.myButton2);
/* 缩小按钮onClickListener */
mButton01.setOnClickListener(new Button.OnClickListener()
{
@Override
public void onClick(View v)
{
small();
}
});
/* 放大按钮onClickListener */
mButton02.setOnClickListener(new Button.OnClickListener()
{
@Override
public void onClick(View v)
{
big();
}
});
}
/* 图片缩小的method */
private void small()
{
int bmpWidth=bmp.getWidth();
int bmpHeight=bmp.getHeight();
/* 设置图片缩小的比例 */
double scale=0.8;
/* 计算出这次要缩小的比例 */
scaleWidth=(float) (scaleWidth*scale);
scaleHeight=(float) (scaleHeight*scale);
/* 产生reSize后的Bitmap对象 */
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizeBmp = Bitmap.createBitmap(bmp,0,0,bmpWidth,bmpHeight,matrix,true);
if(id==0)
{
/* 如果是第一次按,就删除原来默认的ImageView */
layout1.removeView(mImageView);
}
else
{
/* 如果不是第一次按,就删除上次放大缩小所产生的ImageView */
layout1.removeView((ImageView)findViewById(id));
}
/* 产生新的ImageView,放入reSize的Bitmap对象,再放入Layout中 */
id++;
ImageView imageView = new ImageView(ex4_23.this);
imageView.setId(id);
imageView.setImageBitmap(resizeBmp);
layout1.addView(imageView);
setContentView(layout1);
/* 因为图片放到最大时放大按钮会disable,所以在缩小时把它重设为enable */
mButton02.setEnabled(true);
}
/* 图片放大的method */
private void big()
{
int bmpWidth=bmp.getWidth();
int bmpHeight=bmp.getHeight();
/* 设置图片放大的比例 */
double scale=1.25;
/* 计算这次要放大的比例 */
scaleWidth=(float)(scaleWidth*scale);
scaleHeight=(float)(scaleHeight*scale);
/* 产生reSize后的Bitmap对象 */
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleHeight);
Bitmap resizeBmp = Bitmap.createBitmap(bmp,0,0,bmpWidth,bmpHeight,matrix,true);
if(id==0)
{
/* 如果是第一次按,就删除原来设置的ImageView */
layout1.removeView(mImageView);
}
else
{
/* 如果不是第一次按,就删除上次放大缩小所产生的ImageView */
layout1.removeView((ImageView)findViewById(id));
}
/* 产生新的ImageView,放入reSize的Bitmap对象,再放入Layout中 */
id++;
ImageView imageView = new ImageView(ex4_23.this);
imageView.setId(id);
imageView.setImageBitmap(resizeBmp);
layout1.addView(imageView);
setContentView(layout1);
/* 如果再放大会超过屏幕大小,就把Button disable */
if(scaleWidth*scale*bmpWidth>displayWidth||scaleHeight*scale*bmpHeight>displayHeight)
{
mButton02.setEnabled(false);
}
}
}
因为在Android中不允许ImageView在产生后,动态修改其长度与宽度,所以为了实现图片放大、缩小的功能,使用的方式是当用户在单击放大或缩小的按钮时,除了将图片作放大或缩小的动作外,并将原来Layout中的ImageView删除,重新产生一个新的ImageView,指定图片给它后,再放入Layout里。用户看起来,就好像是同一张图片在放大或缩小。
在放大、缩小图片文件时使用的Martix对象,除了可以用来作图片的缩放外,还有非常实用的旋转效果,我们将会在后面的范例中示范。
4.24 动态旋转图片 | |
|
要旋转ImageView其实很简单,先将前一次ImageView里的图片放入暂存Bitmap,接着再利用Bitmap.createBitmap来创建新的Bitmap对象,在创建新的Bitmap对象的同时,搭配Matrix对象里的setRotate()方法动态旋转新创建的Bitmap。然后,再将旋转好的Bitmap对象,以新构造的方式创建新的Bitmap对象,最后再“放入”ImageView中显示,就可以达到旋转ImageView的效果,这个范例的学习重点则在于如何操作BitmapFactory.decodeResource()方法来使用Matrix对图像的控制,为了让旋转能够通过事件来进行,所以在Layout中配置了两个Button Widget,以及一个ImageView Widget。当单击“向左旋转”按钮时,画面的小河马就会向左倾斜;当单击“向右旋转”按钮时,画面的小河马就会向右倾斜。
范例程序
程序设置了两个按钮,分别处理向左及向右旋转的语句,而为了防止旋转角度超过数值范围,所以默认参考值(ScaleAngle)向左最多为−5、向右则最多为5,再利用Matrix对象里的matrix.setRotate()方法,将5*ScaleAngle的计算结果传入作为参数,使其产生每次至少5°的变化,在−20°和20°之间。
Bitmap.createBitmap()方法所扮演的角色为产生暂存的Bitmap,使之每一次(单击按钮时)都“复制”来自原始图“mySourceBmp”的数据,自坐标(0,0)-(原图宽,原图高)复制成为新图,而由于createBitmap()方法的第六个参数可指定Matrix对象,这也就是本范例程序旋转画面的唯一关键。
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class ex4_24 extends Activity {
private Button mButton1;
private Button mButton2;
private TextView mTextView1;
private ImageView mImageView1;
private int ScaleTimes;
private int ScaleAngle;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mButton1 =(Button) findViewById(R.id.myButton1);
mButton2 =(Button) findViewById(R.id.myButton2);
mTextView1 = (TextView) findViewById(R.id.myTextView1);
mImageView1 = (ImageView) findViewById(R.id.myImageView1);
ScaleTimes = 1;
ScaleAngle = 1;
final Bitmap mySourceBmp = BitmapFactory.decodeResource(getResources(), R.drawable.hippo);
final int widthOrig = mySourceBmp.getWidth();
final int heightOrig = mySourceBmp.getHeight();
/* 程序刚运行,加载默认的Drawable */
mImageView1.setImageBitmap(mySourceBmp);
/* 向左旋转按钮 */
mButton1.setOnClickListener(new Button.OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
ScaleAngle--;
if(ScaleAngle<-5)
{
ScaleAngle = -5;
}
/* ScaleTimes=1,维持1:1的宽高比例*/
int newWidth = widthOrig * ScaleTimes;
int newHeight = heightOrig * ScaleTimes;
float scaleWidth = ((float) newWidth) / widthOrig;
float scaleHeight = ((float) newHeight) / heightOrig;
Matrix matrix = new Matrix();
/* 使用Matrix.postScale设置维度 */
matrix.postScale(scaleWidth, scaleHeight);
/* 使用Matrix.postRotate方法旋转Bitmap*/
//matrix.postRotate(5*ScaleAngle);
matrix.setRotate(5*ScaleAngle);
/* 创建新的Bitmap对象 */
Bitmap resizedBitmap = Bitmap.createBitmap(mySourceBmp, 0, 0, widthOrig, heightOrig, matrix, true);
/**/
BitmapDrawable myNewBitmapDrawable = new BitmapDrawable(resizedBitmap);
mImageView1.setImageDrawable(myNewBitmapDrawable);
mTextView1.setText(Integer.toString(5*ScaleAngle));
}
});
/* 向右旋转按钮 */
mButton2.setOnClickListener(new Button.OnClickListener()
{
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub
ScaleAngle++;
if(ScaleAngle>5)
{
ScaleAngle = 5;
}
/* ScaleTimes=1,维持1:1的宽高比例*/
int newWidth = widthOrig * ScaleTimes;
int newHeight = heightOrig * ScaleTimes;
/* 计算旋转的Matrix比例 */
float scaleWidth = ((float) newWidth) / widthOrig;
float scaleHeight = ((float) newHeight) / heightOrig;
Matrix matrix = new Matrix();
/* 使用Matrix.postScale设置维度 */
matrix.postScale(scaleWidth, scaleHeight);
/* 使用Matrix.postRotate方法旋转Bitmap*/
//matrix.postRotate(5*ScaleAngle);
matrix.setRotate(5*ScaleAngle);
/* 创建新的Bitmap对象 */
Bitmap resizedBitmap = Bitmap.createBitmap(mySourceBmp, 0, 0, widthOrig, heightOrig, matrix, true);
/**/
BitmapDrawable myNewBitmapDrawable = new BitmapDrawable(resizedBitmap);
mImageView1.setImageDrawable(myNewBitmapDrawable);
mTextView1.setText(Integer.toString(5*ScaleAngle));
}
});
}
}
扩展学习
Matrix类的设计,不仅是二维空间的结构,事实上,它原始的设计是3´3的矩阵,由于Matrix类并不需要构造器,因此在声明Matrix对象之后,可以调用reset()方法或set()方法产生新的矩阵,如同本范例的setRotate一样。
回来看看,我们在这个范例程序中,也放上了添加注释的语句matrix.postRotate(5*ScaleAngle),经测试后,无论是使用postRotate()方法或setRotate()方法,效果都是相同的,但较困惑的是Google的官方SDK文件中,所描述的postRotate()方法的原型如下:
boolean postRotate(float degrees, float px, float py)
经测试postRotate()方法并没有第二、第三个参数需要传递,这是在Android SDK 1.0_r2才发现的,未来可能会因为改版而有所改变。
另外,Matrix还有一个常用的postScale()方法,可通过矩阵的方式用以指定其缩放大小,其原型如下:
public boolean postScale(float sx, float sy, float px, float py)
使用的公式为:M' = S(sx, sy, px, py)* M,示意的片段程序如下。
final Bitmap mySourceBmp =
BitmapFactory.decodeResource(getResources(), R.drawable.hippo)
Matrix mx = new Matrix();
mx.postScale
(
(float)20/mySourceBmp.getWidth(),
(float)20/mySourceBmp.getHeight()
);
mySourceBmp = Bitmap.createBitmap
(bm, 0, 0, bm.getWidth(), bm.getHeight(), mx, true);
mImageView1.setImageBitmap(mySourceBmp);