1. 利用Gallery组件实现 3D图片浏览器的功能,如下:
2. 下面是详细的实现过程如下:
(1)这里我是测试性代码,我的图片是自己添加到res/drawable/目录下的,如下:
但是开发中不能是这样,往往是浏览手机中的图片,比如浏览手机中SD卡中的图片,这里我们需要写一个方法 getImagesFromSD()获取SD卡中的图片信息(图片的SD路径),代码如下:
1 private ListgetImagesFromSD() { 2 List imageList = new ArrayList (); 3 File f = Environment.getExternalStorageDirectory(); 4 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 5 f = new File(Environment.getExternalStorageDirectory().toString()); 6 } else { 7 Toast.makeText(MainActivity.this, R.string.sdcarderror, Toast.LENGTH_LONG).show(); 8 return imageList; 9 } 10 11 File[] files = f.listFiles(); 12 if (files == null || files.length == 0) 13 return imageList; 14 15 for (int i = 0; i < files.length; i++) { 16 File file = files[i]; 17 if (isImageFile(file.getPath())) 18 imageList.add(file.getPath()); 19 } 20 return imageList; 21 }
上面这个方法的作用,就是获取SD卡中,所有文件的后缀名满足图片后缀名的文件的路径,然后放到List容器中返回。
isImageFile方法是这样实现的:
1 private boolean isImageFile(String fName) { 2 String end = fName.substring(fName.lastIndexOf(".") + 1, fName.length()).toLowerCase(); 3 if (end.equals("jpg") || end.equals("gif") || end.equals("png") || end.equals("jpeg") || end.equals("bmp")) { 4 return true; 5 } 6 return false; 7 8 }
这里我们先实现Gallery 3D图片浏览的效果,暂时不用理会这个图片路径的问题。
(2)我们知道Android中自带的Gallery组件是不能实现3D浏览效果的,我们这里需要自定义一个GalleryView继承自Gallery,从而附加这个GalleryView
特别的功能:实现图片的 旋转 和 缩放 ( 两种功能集合起来,给用户感官上就是3D浏览器的感觉)
GalleryView如下:
1 package com.mythou.grallery3d; 2 3 import android.content.Context; 4 import android.graphics.Camera; 5 import android.graphics.Matrix; 6 import android.util.AttributeSet; 7 import android.view.View; 8 import android.view.animation.Transformation; 9 import android.widget.Gallery; 10 import android.widget.ImageView; 11 12 /** 13 * 自定义GalleryView: 14 * 主要做了对图片的旋转和缩放操作,根据图片的屏幕中的位置对其进行旋转缩放操作。 15 * @author hebao 16 * 17 */ 18 public class GalleryView extends Gallery { 19 // 相机类,用来做类3D效果处理,比如z轴方向上的平移,绕y轴的旋转等 20 private Camera mCamera = new Camera(); 21 // 最大转动角度,是图片绕y轴最大旋转角度,也就是屏幕最边上那两张图片的旋转角度 22 private int mMaxRotationAngle = 45; 23 // 最大缩放值,是图片在z轴平移的距离,视觉上看起来就是放大缩小的效果 24 private int mMaxZoom = -120; 25 // 半径值 26 private int mCoveflowCenter; 27 28 public GalleryView(Context context) { 29 super(context); 30 /** 设置为true时,允许多子类进行静态转换: 31 * 也就是说把这个属性设成true的时候每次viewGroup(看Gallery的源码就可以看到它是从ViewGroup间 32 * 接继承过来的)在重新画它的child的时候都会促发getChildStaticTransformation这个函数,所以我 33 * 们只需要在这个函数里面去加上旋转和放大的操作就可以了 34 **/ 35 this.setStaticTransformationsEnabled(true); 36 } 37 38 public GalleryView(Context context, AttributeSet attrs) { 39 super(context, attrs); 40 this.setStaticTransformationsEnabled(true); 41 } 42 43 public GalleryView(Context context, AttributeSet attrs, int defStyle) { 44 super(context, attrs, defStyle); 45 this.setStaticTransformationsEnabled(true); 46 } 47 48 public int getMaxRotationAngle() { 49 return mMaxRotationAngle; 50 } 51 52 public void setMaxRotationAngle(int maxRotationAngle) { 53 mMaxRotationAngle = maxRotationAngle; 54 } 55 56 public int getMaxZoom() { 57 return mMaxZoom; 58 } 59 60 public void setMaxZoom(int maxZoom) { 61 mMaxZoom = maxZoom; 62 } 63 64 65 //获取GalleryView的中心位置center 66 private int getCenterOfCoverflow() { 67 return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft(); 68 } 69 //获取子View的位置 70 private static int getCenterOfView(View view) { 71 return view.getLeft() + view.getWidth() / 2; 72 } 73 74 75 76 /** 77 * 在当前GalleryView的大小发生改变是被系统调用 78 */ 79 @Override 80 protected void onSizeChanged(int w, int h, int oldw, int oldh) { 81 mCoveflowCenter = getCenterOfCoverflow(); 82 super.onSizeChanged(w, h, oldw, oldh); 83 } 84 85 /** 86 * 控制gallery中每个图片的旋转(重写的gallery中方法) 87 */ 88 @Override 89 protected boolean getChildStaticTransformation(View child, Transformation trans) { 90 //取得当前子view的半径值 91 final int childCenter = getCenterOfView(child); 92 System.out.println("childCenter:"+childCenter); 93 94 final int childWidth = child.getWidth(); 95 96 //旋转角度 97 int rotationAngle = 0; 98 //重置转换状态 99 trans.clear(); 100 /**设置转换类型 101 * TYPE_IDENTITY 102 * TYPE_ALPHA 103 * TYPE_MATRIX 104 * TYPE_BOTH 105 */ 106 trans.setTransformationType(Transformation.TYPE_BOTH); 107 108 //如果图片位于中心位置不需要进行旋转 109 if (childCenter == mCoveflowCenter) { 110 transformImageBitmap((ImageView) child, trans, 0); 111 } else { 112 //根据图片在gallery中的位置来计算图片的旋转角度 113 rotationAngle = (int) (((float) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle); 114 System.out.println("rotationAngle:" +rotationAngle); 115 //如果旋转角度绝对值大于最大旋转角度返回 (-mMaxRotationAngle 或 mMaxRotationAngle ) 116 if (Math.abs(rotationAngle) > mMaxRotationAngle) { 117 rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle; 118 } 119 transformImageBitmap((ImageView) child, trans, rotationAngle); 120 } 121 122 return true; 123 } 124 125 /** 126 * 127 * 128 * 旋转、缩放图片 129 * 130 * @param child 子View 131 * @param trans 变换,Transformation 中包含一个矩阵和 alpha 值,矩阵是用来做平移、旋转和缩放动画的, 132 * 通过Transformation来设置子控件的canvas。 133 * @param rotationAngle 旋转角度 134 * 135 * 备注:Transformation 136 * Transformation记录了仿射矩阵Matrix,动画每触发一次,会对原来的矩阵做一次运算, 137 * View的Bitmap与这个矩阵相乘就可以实现相应的操作(旋转、平移、缩放等)。 138 * Transformation类封装了矩阵和alpha值,它有两个重要的成员,一是mMatrix,二是mAlpha(控制透明度)。 139 * 140 */ 141 private void transformImageBitmap(ImageView child, Transformation trans, int rotationAngle) { 142 143 //对效果进行保存 144 mCamera.save(); 145 146 final Matrix imageMatrix = trans.getMatrix(); 147 //图片高度 148 final int imageHeight = child.getLayoutParams().height; 149 //图片宽度 150 final int imageWidth = child.getLayoutParams().width; 151 //返回旋转角度的绝对值 152 final int rotation = Math.abs(rotationAngle); 153 154 /** 155 * translate(float x, float y, float z) 156 * 三个参数x,y,z分别为在手机屏幕坐标系x,y,z三个轴上的位移,这三个参数为正:表示沿着相应坐标系正方向移动 157 * 158 * 在Z轴上负向移动camera的视角,实际效果为放大图片。 159 * 在Y轴上移动,则图片上下移动; X轴上对应图片左右移动。 160 */ 161 mCamera.translate(0.0f, 0.0f, -20.0f); 162 163 // As the angle of the view gets less, zoom in 164 if (rotation < mMaxRotationAngle) { 165 float zoomAmount = (float) (mMaxZoom + (rotation * 1.0)); 166 mCamera.translate(0.0f, 0.0f, zoomAmount); 167 } 168 169 // rotateY:在Y轴上旋转,沿着Y轴图片往里翻转,对应图片竖向向里翻转。 170 // rotateX:在X轴上旋转,沿着X轴图片往里翻转,则对应图片横向向里翻转。 171 mCamera.rotateY(rotationAngle); 172 173 mCamera.getMatrix(imageMatrix); 174 /** 175 * preTranslate(float dx, float dy) 176 * postTranslate(float dx, float dy) 177 * 注意他们参数是平移的距离,而不是平移目的地的坐标 178 */ 179 imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2)); 180 imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2)); 181 //恢复相机状态 182 mCamera.restore(); 183 } 184 }
通过自定义gallery控件,现在我们已经实现了当滑动Gallery里面的图片时,两侧的图片会发生一定角度的旋转,也就是完成了3D效果的第一部,下一步,就需要我们在Gallery的Adapter里面,对getView方法,进行改造,从而完成预览小图片的倒影效果。
(3)实现自定义适配器ImageAdapter,完成倒影效果
要完成倒映效果,我们需要在getView方法中,对进来的图片进行处理,具体代码如下:
1 package com.mythou.grallery3d; 2 3 import java.util.ArrayList; 4 import java.util.HashMap; 5 import java.util.List; 6 import java.util.Map; 7 import android.content.Context; 8 import android.graphics.Bitmap; 9 import android.graphics.Bitmap.Config; 10 import android.graphics.BitmapFactory; 11 import android.graphics.Canvas; 12 import android.graphics.LinearGradient; 13 import android.graphics.Matrix; 14 import android.graphics.Paint; 15 import android.graphics.PorterDuff.Mode; 16 import android.graphics.PorterDuffXfermode; 17 import android.graphics.Shader.TileMode; 18 import android.view.View; 19 import android.view.ViewGroup; 20 import android.widget.BaseAdapter; 21 import android.widget.ImageView; 22 import android.widget.ImageView.ScaleType; 23 24 /** 25 * 自定义适配器ImageAdapter 26 * 在ImageAdapter中主要做了图片的倒影效果 以及 创建了对原始图片和倒影的显示区域 27 * 28 * @author hebao 29 * 30 */ 31 public class ImageAdapter extends BaseAdapter { 32 private ImageView[] mImages;// 图片数组 33 private Context mContext;// 上下文,提供给外界(Activity)使用 34 public List
(4)使用自定义的Gallery控件实现最终的图片浏览器
首先我们来到grallery_layout.xml布局文件之中,如下:
1 xml version="1.0" encoding="utf-8"?> 2 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <TextView 8 android:id="@+id/tvTitle" 9 android:layout_width="wrap_content" 10 android:layout_height="wrap_content" 11 android:layout_centerHorizontal="true" 12 android:textSize="16sp" /> 13 14 <com.mythou.grallery3d.GalleryView 15 android:id="@+id/mygallery" 16 android:spacing="20dp" 17 android:layout_width="fill_parent" 18 android:layout_height="fill_parent" 19 android:unselectedAlpha="128" 20 android:layout_below="@id/tvTitle" 21 android:layout_marginTop="10dip" /> 22 23 RelativeLayout>
接下来我们来到Activity之中,如下:
1 package com.mythou.grallery3d; 2 import android.app.Activity; 3 import android.os.Bundle; 4 import android.view.View; 5 import android.view.Window; 6 import android.widget.AdapterView; 7 import android.widget.AdapterView.OnItemClickListener; 8 import android.widget.AdapterView.OnItemSelectedListener; 9 import android.widget.TextView; 10 import android.widget.Toast; 11 12 public class Grallery3DActivity extends Activity { 13 14 private TextView tvTitle; 15 private GalleryView gallery; 16 private ImageAdapter adapter; 17 18 @Override 19 public void onCreate(Bundle savedInstanceState) { 20 requestWindowFeature(Window.FEATURE_NO_TITLE); 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.grallery_layout); 23 24 initRes(); 25 } 26 27 private void initRes(){ 28 tvTitle = (TextView) findViewById(R.id.tvTitle); 29 gallery = (GalleryView) findViewById(R.id.mygallery); 30 31 adapter = new ImageAdapter(this); 32 //创建倒影效果 33 adapter.createReflectedImages(); 34 gallery.setAdapter(adapter); 35 36 gallery.setOnItemSelectedListener(new OnItemSelectedListener() { 37 @Override 38 public void onItemSelected(AdapterView> parent, View view, int position, long id) { 39 tvTitle.setText(adapter.titles[position]); 40 } 41 42 @Override 43 public void onNothingSelected(AdapterView> parent) { 44 } 45 }); 46 47 gallery.setOnItemClickListener(new OnItemClickListener() { 48 @Override 49 public void onItemClick(AdapterView> parent, View view, int position, long id) { 50 Toast.makeText(Grallery3DActivity.this, "img " + (position+1) + " selected", Toast.LENGTH_SHORT).show(); 51 } 52 }); 53 } 54 }
(5)布署程序到手机上,效果如下: