Android之——史上最简单最酷炫的3D图片浏览效果的实现

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48052709

如今,Android开发已经成为移动互联开发领域中一支不可或缺的力量,那么Android中要实现3D的效果那也就是合情合理的事情了。那么,如何在Android中实现像IOS中那样的3D图片浏览效果呢?下面,鄙人将重磅推出今天的重点博文,和大家一起在Android中实现酷炫的3D图片浏览效果。

一、原理

老规矩,还是要来啰嗦下原理的东西。

整体实现是以手机屏幕的正中间位置为对称轴,位于正中间的图片显示最大,也最亮,同时左右两边的图片以最中间位置为对称轴,分别旋转对应的角度,同时亮度调整为适当的比例,已达到对称的效果。具体的3D浏览图片效果,我是通过自定义Gallery来实现的,创建一个类GalleryFlow,继承Gallery,在这个类中进行图像的旋转、缩放,亮度设置等操作。同时,在这个类中,我创建了一个相机对象camera来设置图像的变化效果;同时自定义一个图像的适配器类ImageAdapter,这个类继承BaseAdapter,主要是实现界面图片的显示,以及创建带有倒影的图片。

原理啰嗦完了,那就让我们一起来实现这个酷炫的3D效果吧。

二、实现

1、自定义适配器ImageAdapter

显示图片的适配器,这个类继承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;
		}
	}
}

2、自定义Gallery类GalleryFlow

这个类中我们主要实现的是图片的缩放、旋转、亮度调整等操作,下面我们分解来看这个类。

1)成员变量

这里的成员变量主要是:最大的旋转角度、最大缩放值、记录中间点的位置、相机对象

具体实现代码如下:

//最大的旋转角度
private int maxRotateAngle = 50;
//最大缩放值
private int maxZoom = -250;
//记录中间点的位置
private int currentOfGallery;
//创建相机对象
private Camera camera = new Camera();
2)构造方法

我在这里复写了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);
}
3)getChildStaticTransformation方法

这个方法是调用了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;
}
4)图片变形方法transformationBitmap

这个方法主要完成的操作就是通过相机对象来完成对图像的变形操作。

具体实现代码如下:

/**
 * 图片变形
 * @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();
}
5)获取Gallery展示图片的中心点方法

具体实现代码如下:

/**
 * 获取Gallery展示图片的中心点
 * @return
 */
public int getCurrentOfGallery(){
	return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();
}
6)获取图片中心点方法

具体实现代码如下:

/**
 * 获取图片中心点
 * @param view
 * @return
 */
public int getCurrentOfView(View view){
	return view.getLeft() + view.getWidth() / 2;
}
7)onSizeChanged方法

这个方法是当屏幕大小变化的时候,Android自动回调的方法,我在这个方法中的操作就是将获取到的Gallery展示图片的中心点赋值给成员变量currentOfGallery。

具体代码如下:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
	currentOfGallery = getCurrentOfGallery();
	super.onSizeChanged(w, h, oldw, oldh);
}
8)完整代码如下:
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;
	}
}

3、MainActivity

这个类主要实现的功能是加载布局文件,构造要显示的图片的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;
	}

}

4、布局文件activity_main.xml

这个布局文件很简单,就是放置了一个我们自己定义的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>

5、AndroidManifest.xml

这个文件中没有做任何操作,都是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程序员最基本的开发常识和规范,我在这里只是为了方便直接写在了类和布局文件中。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-24 01:39:56

Android之——史上最简单最酷炫的3D图片浏览效果的实现的相关文章

【Android】史上最简单,一步集成侧滑(删除)菜单,高仿QQ、IOS

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 转载请标明出处: http://blog.csdn.net/zxt0601/article/details/53157090 本文出自:[张旭童的博客](http://blog.csdn.net/zxt0601) 代码传送门:喜欢的话,随手点个star.多谢 https://github.com/mcxtzhang/SwipeDelMenuLayout 重要的话 开头说,not for the RecyclerView or L

Android之——史上最简单旋转菜单实现效果

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48048323 由于身体原因,前几天没有给大家更新博客,那么,今天我们就来一起实现一个非常酷炫的旋转菜单效果吧.在很多APP中,不难发现,人家把菜单效果设计的那叫一个酷炫啊,其中一种设计就是将菜单设计成旋转的效果.好了,那么这么酷炫的菜单效果是如何实现的呢?下面,就让我们一起来实现这个酷炫的菜单效果吧. 一.原理 老规矩,还是先唠叨下原理级别的东西. 这个示例的实现原理很简单,利用

Android之——史上最简单图片轮播广告效果实现

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48049913 如今的Android开发需求越来越来多,实现效果越来越酷炫,很多Android APP都要实现PC网站上那样的图片轮播效果,那么,这些图片的轮播效果是如何实现的呢?下面,我就跟大家一起来实现这些酷炫的功能. 一.原理 首先,将这些要轮播的图片和一些文本分别放置在不同的数据集合中,程序启动的时候默认显示一组图片和文本数据,然后启动一个定时器,每隔一段时间便替换掉显示的

css3实现酷炫的3D盒子翻转效果

简介 运用css3先在平面空间组成立方体盒子,再让整个盒子翻转起来,先来张效果图: 步骤 1.先用css将6张图片摆成下图的样子: 下面就是通过css3的3D变换将每个面进行翻转,使之成为一个立体的盒子,代码如下: //左面 .pic2{ transform:rotateY(90deg); //沿y轴翻转90度 transform-origin:right; //以右边为轴 } //前面 .pic6{ transform:translateZ(100%); //垂直屏幕向外移动立方体的长度 }

Android之——史上最简单自定义开关按钮的实现

转载请注明出处:http://blog.csdn.net/l1028386804/article/details/48102871 很多时候,我们在很多无论是Android还是IOS的APP中都会遇到这样的一种效果,有一个按钮,我们点击一下,便会滑动一下,一会显示"开",一会显示"关",这便是开关按钮了,比如:很多Android手机的设置功能里,就有很多功能是用开关按钮实现的,那么这些开关按钮时如何实现的呢?下面,就让我们一起来实现这个功能吧. 一.原理 我们在界面

仿智能社官网:原生JS实现简单又酷炫的3D立方体时钟

先放一下我做的效果:https://linrunzheng.github.io/3Dclock/3Dclock/new.html 至于3D立方体怎么做这里就不在阐述了,可以看一下我之前的博客. 这里默认你已经做好了6个立方体,直接上JS代码: 页面进来的时候,先给6个立方体赋值上现在的时间,由于立方体比较小,左右2个面看不清且影响效果,这里左右2个面就不赋time了: //获取元素 var oul = document.querySelectorAll("ul"); var back

开源框架】Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发

[原][开源框架]Android之史上最全最简单最有用的第三方开源库收集整理,有助于快速开发,欢迎各位... 时间 2015-01-05 10:08:18 我是程序猿,我为自己代言 原文  http://blog.csdn.net/caoyouxing/article/details/42418591 主题 开源 安卓开发 http://www.tuicool.com/articles/jyA3MrU Android开源库 自己一直很喜欢Android开发,就如博客签名一样, 我是程序猿,我为自

Android 自定义控件打造史上最简单的侧滑菜单

转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/39185641 ,本文出自[张鸿洋的博客] 侧滑菜单在很多应用中都会见到,最近QQ5.0侧滑还玩了点花样~~对于侧滑菜单,一般大家都会自定义ViewGroup,然后隐藏菜单栏,当手指滑动时,通过Scroller或者不断的改变leftMargin等实现:多少都有点复杂,完成以后还需要对滑动冲突等进行处理~~今天给大家带来一个简单的实现,史上最简单有点夸张,但是的确是我目前遇到过的最

Android滑动菜单特效实现,仿人人客户端侧滑效果,史上最简单的侧滑实现

本文首发于CSDN博客,转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/8714621 人人客户端有一个特效还是挺吸引人的,在主界面手指向右滑动,就可以将菜单展示出来,而主界面会被隐藏大部分,但是仍有左侧的一小部分同菜单一起展示. 据说人人客户端的这个特效是从facebook客户端模仿来的,至于facebook是不是又从其它地方模仿来的就不得而知了.好,今天我们就一起来实现这个效果,总之我第一次看到这个特效是在人人客户端看到的,我