今天才刚考完试,终于有时间整理下之前写的一个小程序了,代码放在三维Gallery这里,有兴趣的可以下载。
实现效果如图,从系统的MediaStore数据库读取图片,组成稍带立体感的Gallery。点击图片跳到另一个页面浏览,可以用一根手指拖动或者两根手指一起来放大缩小。

程序包含5个JAVA文件。
1 ThreeDimensionalGalleryActivity是主Activity,主界面main.xml只有一个元素---自写的GalleryFlow。这类遍历MediaStore数据库读取图片的URI,将URI数组传给ImageAdapter处理照片,同时设置ImageAdapter为GalleryFlow的适配器。添加图片的单击响应事件,单击图片将跳转至ImageDetailActivity。
2 ImageAdapter继承自BaseAdapter,实现了常见的getView、getCount等接口。根据传进来的URI数组,读取图片并用Matrix倒转图像,用Canvas和Paint重新画出一个完整的Bitmap(包含原图、有阴影效果的倒影图像)。
3 GalleryFlow继承自Gallery类,重写了getChildStaticTransformation方法,使得传统Gallery两边的图片稍微旋转倾斜,中间不变,产生立体效果。
4 MultiPointTouchListener实现了OnTouchListener的接口,修改了onTouch方法,重新解析手指的触屏操作(平移、特别是缩放利用到多点触摸),并根据操作用Matrix处理传进来的ImageView。
5 ImageDetailActivity仅展示用户点击的那张图片,设置OnTouchListener为MultiPointTouchListener。
部分代码:
public class MultiPointTouchListener implements OnTouchListener {
// 利用Matrix来移动和缩放图片
Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
// 三种状态
static final int NONE = 0;
static final int DRAG = 1;
static final int ZOOM = 2;
int mode = NONE;
// 缩放用到的点,记录下来
PointF start = new PointF();
PointF mid = new PointF();
float oldDist = 1f;
@Override
public boolean onTouch(View v, MotionEvent event) {
ImageView view = (ImageView) v;
// 这里处理触屏事件
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN://第一根手指按下
matrix.set(view.getImageMatrix());
savedMatrix.set(matrix);
start.set(event.getX(), event.getY());
mode = DRAG;
break;
case MotionEvent.ACTION_POINTER_DOWN://第二根手指按下
oldDist = spacing(event);
if (oldDist > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = ZOOM;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_POINTER_UP:
mode = NONE;
break;
case MotionEvent.ACTION_MOVE:
if (mode == DRAG) {
matrix.set(savedMatrix);
//M' = T(dx, dy) * M,平移操作
matrix.postTranslate(event.getX() - start.x, event.getY()
- start.y);
} else if (mode == ZOOM) {
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float scale = newDist / oldDist;
matrix.postScale(scale, scale, mid.x, mid.y);
}
}
break;
}
view.setImageMatrix(matrix);
return true; // 表明事件已被处理
}
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
}
读取MediaStore数据库,读到的URI写进数组。
public boolean getPhotoURIs()
{
Uri uri=null;
String projection[]={MediaStore.Images.Media._ID};
try{
ContentResolver cr=this.getContentResolver();
Cursor cursor = cr.query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,projection, null, null, null);
if(null==cursor)
return false;
if(cursor.getCount()==0){
cursor.close();
return false;
}
uriArray=new Uri[cursor.getCount()];
int i=0;
while(cursor.moveToNext()&&i<cursor.getCount()){
cursor.moveToPosition(i);
uri=Uri.withAppendedPath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Images.Thumbnails._ID))+"");
uriArray[i]=uri;
i++;
}
cursor.close();
}
catch(Exception ex){
ex.printStackTrace();
}
return true;
}
ImageAdapter里读取Bitmap,创建新位图:
public boolean createReflectedImages() {
final int reflectionGap = 4;//倒影图和原图之间的距离
int index = 0;
for (Uri uri : mURIs)
{
try {
BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
bitmapOptions.inSampleSize = 4;
Bitmap originalImage = BitmapFactory.decodeStream(mContext.getContentResolver()
.openInputStream(uri),null,bitmapOptions);
if(originalImage==null)
continue;
int width = originalImage.getWidth();
int height = originalImage.getHeight();
if(height<=0||width<=0)
continue;
//利用Matrix来创建倒影图,倒影高度为原图1/2
Matrix matrix = new Matrix();
matrix.preScale(1, -1);
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0,
height/2, width, height/2, matrix, false);
//创建一个宽度不变,高度为原图+倒影图高度的位图
Bitmap bitmapWithReflection = Bitmap.createBitmap(width,
(height + height / 2), Config.ARGB_8888);
//将上面创建的位图初始化到画布
Canvas canvas = new Canvas(bitmapWithReflection);
canvas.drawBitmap(originalImage, 0, 0, null);
Paint deafaultPaint = new Paint();
deafaultPaint.setAntiAlias(false);
canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null);
Paint paint = new Paint();
paint.setAntiAlias(false);
//给倒影设置阴影
LinearGradient shader = new LinearGradient(0,originalImage.getHeight(), 0,
bitmapWithReflection.getHeight() + reflectionGap,0x70ffffff, 0x00ffffff, TileMode.MIRROR);
paint.setShader(shader);
paint.setXfermode(new PorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));
//用已经定义好的画笔构建一个矩形阴影渐变效果
canvas.drawRect(0, height, width, bitmapWithReflection.getHeight()+ reflectionGap, paint);
//创建一个ImageView用来显示已经画好的bitmapWithReflection
ImageView imageView = new ImageView(mContext);
imageView.setImageBitmap(bitmapWithReflection);
//设置imageView大小 ,不能太大
imageView.setLayoutParams(new GalleryFlow.LayoutParams(300, 400));
mImages[index++] = imageView;//有占用内存过多的风险!
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
return true;
}