转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48052709
如今,Android开发已经成为移动互联开发领域中一支不可或缺的力量,那么Android中要实现3D的效果那也就是合情合理的事情了。那么,如何在Android中实现像IOS中那样的3D图片浏览效果呢?下面,鄙人将重磅推出今天的重点博文,和大家一起在Android中实现酷炫的3D图片浏览效果。
老规矩,还是要来啰嗦下原理的东西。
整体实现是以手机屏幕的正中间位置为对称轴,位于正中间的图片显示最大,也最亮,同时左右两边的图片以最中间位置为对称轴,分别旋转对应的角度,同时亮度调整为适当的比例,已达到对称的效果。具体的3D浏览图片效果,我是通过自定义Gallery来实现的,创建一个类GalleryFlow,继承Gallery,在这个类中进行图像的旋转、缩放,亮度设置等操作。同时,在这个类中,我创建了一个相机对象camera来设置图像的变化效果;同时自定义一个图像的适配器类ImageAdapter,这个类继承BaseAdapter,主要是实现界面图片的显示,以及创建带有倒影的图片。
原理啰嗦完了,那就让我们一起来实现这个酷炫的3D效果吧。
显示图片的适配器,这个类继承BaseAdapter,完成图像的基本显示,同时将原有图片生成带有倒影的图片进行显示。具体的实现为,在构造方法中将界面中的上下文信息,和存放图片id的数组传递过来,通过传递的图片id数组生成对应的ImageView数组,用来存放带有图片信息的ImageView对象。在createRefectedBitmap()方法中将原有图片处理成带有倒影效果的图片存放在ImageView对象中,然后将ImageView对象存放在ImageView数组中。
具体实现的代码如下:
package com.lyz.gallery.activity; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Bitmap.Config; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.LinearGradient; import android.graphics.Matrix; import android.graphics.Paint; import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuffXfermode; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; /** * 自定义图片显示的适配器ImageAdapter * @author liuyazhuang * */ public class ImageAdapter extends BaseAdapter { private Context context; private int[] imageIds; private ImageView[] images; //构造方法 public ImageAdapter(Context context, int[] imageIds) { this.context = context; this.imageIds = imageIds; //存放图片的数组 images = new ImageView[imageIds.length]; } @Override public int getCount() { return images.length; } @Override public Object getItem(int position) { return images[position]; } @Override public long getItemId(int position) { return position; } @Override public View getView(int position, View convertView, ViewGroup parent) { return images[position]; } /** * 创建带有倒影的图片 */ public void createRefectedBitmap() { //原图片与倒影图片之间的距离 int refectionGap = 4; //向图片数组中加入图片 for(int i = 0; i < imageIds.length; i++){ int imageId = imageIds[i]; //原图片 Bitmap resourceBitmap = BitmapFactory.decodeResource(context.getResources(), imageId); int width = resourceBitmap.getWidth(); int height = resourceBitmap.getHeight(); //倒影图片 //reource:原图片 //x,y:生成倒影图片的起始位置 //width,heiht:生成倒影图片宽和高 //Matrix m:用来设置图片的样式(倒影) Matrix m = new Matrix(); //x:水平翻转;y:垂直翻转 1支持; -1翻转 m.setScale(1, -1); Bitmap refrectionBitmap = Bitmap.createBitmap(resourceBitmap, 0, height / 2, width, height / 2,m, false); //合成的带有倒影的图片 Bitmap bitmap = Bitmap.createBitmap(width, height + height/2, Config.ARGB_8888); //创建画布 Canvas canvas = new Canvas(bitmap); //绘制原图片 canvas.drawBitmap(resourceBitmap, 0, 0, null); //绘制原图片与倒影之间的间隔 Paint defaultPaint = new Paint(); canvas.drawRect(0, height, width, height + refectionGap, defaultPaint); //绘制倒影图片 canvas.drawBitmap(refrectionBitmap, 0, height + refectionGap, null); //ps中的渐变和遮罩效果 Paint paint = new Paint(); //设置遮罩效果 paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); //设置渐变效果 //设置着色器为遮罩着色 LinearGradient shader = new LinearGradient(0, height, 0, bitmap.getHeight(), 0x70ffffff, 0x00ffffff, TileMode.CLAMP); paint.setShader(shader); canvas.drawRect(0, height, width, bitmap.getHeight(), paint); //创建BitmapDrawable图片 BitmapDrawable bd = new BitmapDrawable(bitmap); //消除图片锯齿效果,使图片平滑 bd.setAntiAlias(true); ImageView imageView = new ImageView(context); imageView.setImageDrawable(bd); //设置图片大小 imageView.setLayoutParams(new GalleryFlow.LayoutParams(160, 240)); //将图片放置在images数组中 images[i] = imageView; } } }
这个类中我们主要实现的是图片的缩放、旋转、亮度调整等操作,下面我们分解来看这个类。
这里的成员变量主要是:最大的旋转角度、最大缩放值、记录中间点的位置、相机对象
具体实现代码如下:
//最大的旋转角度 private int maxRotateAngle = 50; //最大缩放值 private int maxZoom = -250; //记录中间点的位置 private int currentOfGallery; //创建相机对象 private Camera camera = new Camera();
我在这里复写了Gallery的三个构造方法,并在每个构造方法中分别调用了setStaticTransformationsEnabled(true);方法,这个方法的作用是:指定图形是否变化 false:否 true:是。当我们调用了setStaticTransformationsEnabled方法时,Android就会回调下面的getChildStaticTransformation方法
具体实现代码如下:
public GalleryFlow(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setStaticTransformationsEnabled(true); } public GalleryFlow(Context context, AttributeSet attrs) { super(context, attrs); //指定图形是否变化 false:否 true:是 setStaticTransformationsEnabled(true); } public GalleryFlow(Context context) { super(context); setStaticTransformationsEnabled(true); }
这个方法是调用了setStaticTransformationsEnabled时,Android会回调的一个方法,我在这个方法中完成的操作主要是:计算图像的旋转角度,设置图片的变形样式,同时调用图片变形的放方法transformationBitmap来达到图片变形的效果。
具体实现的代码如下:
@Override protected boolean getChildStaticTransformation(View child, Transformation t) { //得到图片的中心点 int currentOfChild = getCurrentOfView(child); int width = child.getLayoutParams().width; int height = child.getLayoutParams().height; //旋转的角度 int rotateAngle = 0; t.clear(); //设置图片变形样式 t.setTransformationType(Transformation.TYPE_MATRIX); //位置中心点位置 if(currentOfChild == currentOfGallery){ transformationBitmap((ImageView)child, t, 0); }else{ //不是中心位置 rotateAngle = (int) ((float)(currentOfGallery - currentOfChild) / width * maxRotateAngle); if(Math.abs(rotateAngle) > maxRotateAngle){ rotateAngle = rotateAngle < 0 ? -maxRotateAngle : maxRotateAngle; } //图片变形 transformationBitmap((ImageView)child, t, rotateAngle); } return true; }
这个方法主要完成的操作就是通过相机对象来完成对图像的变形操作。
具体实现代码如下:
/** * 图片变形 * @param child * @param t * @param i */ private void transformationBitmap(ImageView child, Transformation t, int rotateAngle) { //保存图像变化的效果 camera.save(); Matrix imageMatrix = t.getMatrix(); int rotate = Math.abs(rotateAngle); int imageWidth = child.getWidth(); int imageHeight = child.getHeight(); //z:正数:图片变大 //x:水平移动 //y:垂直移动 camera.translate(0.0f, 0.0f, 100.0f); //当前旋转角度小于最大旋转角度 if(rotate < maxRotateAngle){ float zoom = (float) ((rotate * 1.5) + maxZoom); camera.translate(0.0f, 0.0f, zoom); //设置图片渐变效果 child.setAlpha((int) (255 - rotate * 2.5)); } //图片向展示中心进行垂直角度旋转 camera.rotateY(rotateAngle); camera.getMatrix(imageMatrix); imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2)); imageMatrix.postTranslate(imageWidth / 2, imageHeight / 2); //还原图像变化的效果 camera.restore(); }
具体实现代码如下:
/** * 获取Gallery展示图片的中心点 * @return */ public int getCurrentOfGallery(){ return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft(); }
具体实现代码如下:
/** * 获取图片中心点 * @param view * @return */ public int getCurrentOfView(View view){ return view.getLeft() + view.getWidth() / 2; }
这个方法是当屏幕大小变化的时候,Android自动回调的方法,我在这个方法中的操作就是将获取到的Gallery展示图片的中心点赋值给成员变量currentOfGallery。
具体代码如下:
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { currentOfGallery = getCurrentOfGallery(); super.onSizeChanged(w, h, oldw, oldh); }
package com.lyz.gallery.activity; import android.content.Context; import android.graphics.Camera; import android.graphics.Matrix; import android.util.AttributeSet; import android.view.View; import android.view.animation.Transformation; import android.widget.Gallery; import android.widget.ImageView; /** * 自定义Gallery * @author liuyazhuang * */ public class GalleryFlow extends Gallery { //最大的旋转角度 private int maxRotateAngle = 50; //最大缩放值 private int maxZoom = -250; //记录中间点的位置 private int currentOfGallery; //创建相机对象 private Camera camera = new Camera(); public GalleryFlow(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); setStaticTransformationsEnabled(true); } public GalleryFlow(Context context, AttributeSet attrs) { super(context, attrs); //指定图形是否变化 false:否 true:是 setStaticTransformationsEnabled(true); } public GalleryFlow(Context context) { super(context); setStaticTransformationsEnabled(true); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { currentOfGallery = getCurrentOfGallery(); super.onSizeChanged(w, h, oldw, oldh); } @Override protected boolean getChildStaticTransformation(View child, Transformation t) { //得到图片的中心点 int currentOfChild = getCurrentOfView(child); int width = child.getLayoutParams().width; int height = child.getLayoutParams().height; //旋转的角度 int rotateAngle = 0; t.clear(); //设置图片变形样式 t.setTransformationType(Transformation.TYPE_MATRIX); //位置中心点位置 if(currentOfChild == currentOfGallery){ transformationBitmap((ImageView)child, t, 0); }else{ //不是中心位置 rotateAngle = (int) ((float)(currentOfGallery - currentOfChild) / width * maxRotateAngle); if(Math.abs(rotateAngle) > maxRotateAngle){ rotateAngle = rotateAngle < 0 ? -maxRotateAngle : maxRotateAngle; } //图片变形 transformationBitmap((ImageView)child, t, rotateAngle); } return true; } /** * 图片变形 * @param child * @param t * @param i */ private void transformationBitmap(ImageView child, Transformation t, int rotateAngle) { //保存图像变化的效果 camera.save(); Matrix imageMatrix = t.getMatrix(); int rotate = Math.abs(rotateAngle); int imageWidth = child.getWidth(); int imageHeight = child.getHeight(); //z:正数:图片变大 //x:水平移动 //y:垂直移动 camera.translate(0.0f, 0.0f, 100.0f); //当前旋转角度小于最大旋转角度 if(rotate < maxRotateAngle){ float zoom = (float) ((rotate * 1.5) + maxZoom); camera.translate(0.0f, 0.0f, zoom); //设置图片渐变效果 child.setAlpha((int) (255 - rotate * 2.5)); } //图片向展示中心进行垂直角度旋转 camera.rotateY(rotateAngle); camera.getMatrix(imageMatrix); imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2)); imageMatrix.postTranslate(imageWidth / 2, imageHeight / 2); //还原图像变化的效果 camera.restore(); } /** * 获取Gallery展示图片的中心点 * @return */ public int getCurrentOfGallery(){ return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft(); } /** * 获取图片中心点 * @param view * @return */ public int getCurrentOfView(View view){ return view.getLeft() + view.getWidth() / 2; } }
这个类主要实现的功能是加载布局文件,构造要显示的图片的id数组,调用ImageAdapter方法实现图片的显示操作。
具体实现的代码如下:
package com.lyz.gallery.activity; import android.os.Bundle; import android.app.Activity; import android.view.Menu; /** * 程序主入口 * @author liuyazhuang * */ public class MainActivity extends Activity { private GalleryFlow gallery_flow; //存放图片id的数组 private int[] imageIds; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //构造存放图片id的数组 imageIds = new int[]{ R.drawable.photo1, R.drawable.photo2, R.drawable.photo3, R.drawable.photo4, R.drawable.photo5, R.drawable.photo6, R.drawable.photo7, R.drawable.photo8 }; gallery_flow = (GalleryFlow) findViewById(R.id.gallery_flow); //实例化ImageAdapter对象 ImageAdapter adapter = new ImageAdapter(this, imageIds); //向图片数组中加载图片 adapter.createRefectedBitmap(); gallery_flow.setAdapter(adapter); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } }
这个布局文件很简单,就是放置了一个我们自己定义的GalleryFlow控件。
具体实现代码如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@android:color/black"> <com.lyz.gallery.activity.GalleryFlow android:id="@+id/gallery_flow" android:layout_width="match_parent" android:layout_height="match_parent"/> </RelativeLayout>
这个文件中没有做任何操作,都是Android自动生成的内容。
具体实现代码如下:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.lyz.gallery.activity" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.lyz.gallery.activity.MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
大家可以到链接http://download.csdn.net/detail/l1028386804/9058447下载Android实现3D图片浏览效果示例完整源代码
本实例中,为了方面,我把一些文字直接写在了布局文件中和相关的类中,大家在真实的项目中要把这些文字写在string.xml文件中,在外部引用这些资源,切记,这是作为一个Android程序员最基本的开发常识和规范,我在这里只是为了方便直接写在了类和布局文件中。