1.3D画廊的实现
我们知道Android系统已经为我们提供好了一个展示图片的“容器”——Gallery,但是这个Gallery显示的效果是平面化的,动态效果不强。这里,我们动手做一个自定义的Gallery组件,实现图片的3D效果展示,想想应该不错吧,先看看效果图:
实现这个3D效果的Gallery该怎么做呢?首先,分析一下,
1,展示图片,系统自带Gallery组件,可以基于这个Gallery组件扩展我们所需要的效果。
2,展示效果需要进行3D成像。
3,展示的图片下方需要显示图片的倒影。
4,展示图片的倒影需要加上“遮罩”效果。
好了,问题列好了,我们一个个来解决吧!代码量不多,直接上代码好了。
- package com.example.gallery.view;
-
- 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;
-
- @SuppressWarnings("deprecation")
- public class CustomGallery extends Gallery {
-
-
- private int galleryCenterPoint = 0;
-
- private Camera camera;
-
- public CustomGallery(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- setStaticTransformationsEnabled(true);
- camera = new Camera();
- }
-
-
-
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
-
- super.onSizeChanged(w, h, oldw, oldh);
-
- galleryCenterPoint = getGalleryCenterPoint();
-
- }
-
-
-
-
-
-
-
- @Override
- protected boolean getChildStaticTransformation(View child, Transformation t) {
- int viewCenterPoint = getViewCenterPoint(child);
- int rotateAngle = 0;
-
-
- if (viewCenterPoint != galleryCenterPoint) {
-
- int diff = galleryCenterPoint - viewCenterPoint;
-
- float scale = (float) diff / (float) child.getWidth();
-
- rotateAngle = (int) (scale * 50);
-
- if (Math.abs(rotateAngle) > 50) {
- rotateAngle = rotateAngle > 0 ? 50 : -50;
- }
- }
-
-
- t.clear();
- t.setTransformationType(Transformation.TYPE_MATRIX);
- startTransformationItem((ImageView) child, rotateAngle, t);
- return true;
- }
-
-
-
-
-
-
-
-
-
-
-
- private void startTransformationItem(ImageView iv, int rotateAngle,
- Transformation t) {
- camera.save();
- int absRotateAngle = Math.abs(rotateAngle);
-
-
- camera.translate(0, 0, 100f);
- int zoom = -250 + (absRotateAngle * 2);
- camera.translate(0, 0, zoom);
-
-
- int alpha = (int) (255 - (absRotateAngle * 2.5));
- iv.setAlpha(alpha);
-
-
- camera.rotateY(rotateAngle);
-
- Matrix matrix = t.getMatrix();
- camera.getMatrix(matrix);
- matrix.preTranslate(-iv.getWidth() / 2, -iv.getHeight() / 2);
- matrix.postTranslate(iv.getWidth() / 2, iv.getHeight() / 2);
-
- camera.restore();
- }
-
-
-
-
-
-
- private int getGalleryCenterPoint() {
- return this.getWidth() / 2;
- }
-
-
-
-
-
-
-
- private int getViewCenterPoint(View v) {
- return v.getWidth() / 2 + v.getLeft();
- }
-
- }
代码中有注释,大家可以看着注释理解代码,我在这里要是说怎么考虑的,显得特别麻烦!这里还有一个很重要的概念——矩阵,这个我留在下面去讲解,往下看吧。获取图片的工具类:
- package com.example.gallery.view;
-
- import java.lang.ref.SoftReference;
- import java.util.Hashtable;
-
- import android.content.res.Resources;
- import android.graphics.Bitmap;
- import android.graphics.Bitmap.Config;
- import android.graphics.PorterDuff.Mode;
- import android.graphics.PorterDuffXfermode;
- import android.graphics.Shader.TileMode;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.LinearGradient;
- import android.graphics.Matrix;
- import android.graphics.Paint;
- import android.util.Log;
-
- public class ImageUtil {
-
- private static final String TAG = "ImageUtil";
-
- private static Hashtable<Integer, SoftReference<Bitmap>> mImageCache
- = new Hashtable<Integer, SoftReference<Bitmap>>();
-
-
-
-
-
-
-
-
- public static Bitmap getImageBitmap(Resources res, int resID) {
-
- SoftReference<Bitmap> reference = mImageCache.get(resID);
- if (reference != null) {
- Bitmap bitmap = reference.get();
- if (bitmap != null) {
- Log.i(TAG, "从内存中取");
- return bitmap;
- }
- }
-
- Log.i(TAG, "重新加载");
- Bitmap invertBitmap = getInvertBitmap(res, resID);
-
- mImageCache.put(resID, new SoftReference<Bitmap>(invertBitmap));
- return invertBitmap;
- }
-
-
-
-
-
-
-
- public static Bitmap getInvertBitmap(Resources res, int resID) {
-
- Bitmap sourceBitmap = BitmapFactory.decodeResource(res, resID);
-
-
- Matrix m = new Matrix();
- m.setScale(1.0f, -1.0f);
- Bitmap invertBitmap = Bitmap.createBitmap(sourceBitmap, 0,
- sourceBitmap.getHeight() / 2, sourceBitmap.getWidth(),
- sourceBitmap.getHeight() / 2, m, false);
-
-
- Bitmap resultBitmap = Bitmap.createBitmap(sourceBitmap.getWidth(),
- (int) (sourceBitmap.getHeight() * 1.5 + 5), Config.ARGB_8888);
- Canvas canvas = new Canvas(resultBitmap);
- canvas.drawBitmap(sourceBitmap, 0f, 0f, null);
- canvas.drawBitmap(invertBitmap, 0f, sourceBitmap.getHeight() + 5, null);
-
-
- Paint paint = new Paint();
-
- LinearGradient shader = new LinearGradient(0,
- sourceBitmap.getHeight() + 5, 0, resultBitmap.getHeight(),
- 0x70ffffff, 0x00ffffff, TileMode.CLAMP);
- paint.setShader(shader);
-
- paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
- canvas.drawRect(0, sourceBitmap.getHeight() + 5,
- sourceBitmap.getWidth(), resultBitmap.getHeight(), paint);
-
- return resultBitmap;
- }
- }
这个工具类就是获取整个图片的,包括实现图片的倒影和遮罩效果,看注释!这里需要讲解的是,如果避免OOM,这是一个较复杂的概念,不是一两句话就能讲清楚的,android下加载图片很容易就处理OOM,当然了,避免OOM的方式有很多,我在这是使用了内存缓存机制来避免了,即使用Java给我们提供好的“软引用”来解决。接下来,就是怎么引用这个画廊组件了。- <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.example.gallery.view.CustomGallery
- android:id="@+id/customgallery"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- </com.example.gallery.view.CustomGallery>
-
- </RelativeLayout>
- package com.example.gallery;
-
- import com.example.gallery.view.CustomGallery;
- import com.example.gallery.view.ImageUtil;
-
- import android.app.Activity;
- import android.graphics.Bitmap;
- import android.graphics.drawable.BitmapDrawable;
- import android.os.Bundle;
- import android.view.View;
- import android.view.ViewGroup;
- import android.widget.BaseAdapter;
- import android.widget.Gallery.LayoutParams;
- import android.widget.ImageView;
-
- public class MainActivity extends Activity {
-
-
- private int[] imageResIDs;
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- imageResIDs = new int[]{
- R.drawable.imgres_01,
- R.drawable.imgres_02,
- R.drawable.imgres_03,
- R.drawable.imgres_04,
- R.drawable.imgres_05,
- R.drawable.imgres_06,
- R.drawable.imgres_07,
- R.drawable.imgres_08,
- R.drawable.imgres_01,
- R.drawable.imgres_02,
- R.drawable.imgres_03,
- R.drawable.imgres_04,
- R.drawable.imgres_05,
- R.drawable.imgres_06,
- R.drawable.imgres_07,
- R.drawable.imgres_08
- };
- CustomGallery customGallery = (CustomGallery) findViewById(R.id.customgallery);
- ImageAdapter adapter = new ImageAdapter();
- customGallery.setAdapter(adapter);
- }
-
- public class ImageAdapter extends BaseAdapter {
-
- @Override
- public int getCount() {
-
- return imageResIDs.length;
- }
-
- @Override
- public Object getItem(int position) {
-
- return imageResIDs[position];
- }
-
- @Override
- public long getItemId(int position) {
-
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
-
- ImageView imageView;
- if (convertView != null) {
- imageView = (ImageView) convertView;
- } else {
- imageView = new ImageView(MainActivity.this);
- }
- Bitmap bitmap = ImageUtil.getImageBitmap(getResources(),
- imageResIDs[position]);
- BitmapDrawable drawable = new BitmapDrawable(bitmap);
- drawable.setAntiAlias(true);
- imageView.setImageDrawable(drawable);
- LayoutParams params = new LayoutParams(240, 320);
- imageView.setLayoutParams(params);
- return imageView;
- }
- }
- }
===========================================华丽丽的分割线=============================================
2.Android的矩阵基础
UI开发过程中,我们经常需要对图片进行处理,常见的如贴图,复杂一些的还有位置变换、旋转、滤镜特效等,下面简单介绍一下关于图片处理的一些基本知识和原理。
1 基本概念
对于图片的处理,最常使用到的数据结构是Bitmap,它包含了一张图片所有的数据,这些数据数据包括那些内容呢?简单说来就是由点阵和颜色值组成的,所谓点阵就是一个在概念上是Width * Height的矩阵,每一个元素对应着图片的一个像素,也就是说,点阵保存着图片的空间位置信息;而颜色值即ARGB,分别对应透明度、红、绿、蓝这四个通道分量,每个通道用8比特定义,所以一个颜色值就是一个int整型,可以表示256*256*256种颜色值。
Android中我们常用到这么几个常量:ARGB_8888、ARGB_4444、RGB_565。这几个常量其实就是告诉系统如何对图片的颜色值进行处理,例如ARGB_8888是告诉系统透明度、R、G、B在颜色值中分别用8bit表示,这时颜色值为32bit,这样的定义能够表示最多的颜色值,图片质量也是最好的;ARGB_4444则是每个通道用4bit表示,这样颜色值只用16bit,节省了空间,但是却只能表示16*16*16种颜色,也就是说图片很失去很多彩色信息;RGB_565类型的颜色值同样是16bit,但是它丢弃了透明度信息,可以表示32*64*32种颜色值。
2 颜色矩阵
颜色矩阵是一个5*4的矩阵,用来对图片颜色值进行处理。定义颜色矩阵和颜色值如下如下:
进行如下矩阵运算:
结果R为4*1的矩阵,这个矩阵就是新的颜色值,R中每个通道的值分别如下:
R’ = a*R + b*G + c*B + d*A + e;
G’ = f*R + g*G + h*B + i*A + j;
B’ = k*R + l*G + m*B + n*A + o;
A’ = p*R + q*G + r*B + s*A + t;
这样看起来或许很抽象,很难理解颜色矩阵和结果R直接的关系,我们假设颜色矩阵值如下所示:
那么结果为:
R’ = R;
G’ = G;
B’ = B;
A’ = A;
也就是说,新的颜色值跟原先的一样!再看一个例子,颜色矩阵取值为:
结果为:
R’ = R + 100;
G’ = G + 100;
B’ = B;
A’ = A;
新的颜色值中,红色通道值和绿色通道值分别增加了100,此时图片会泛黄(因为R + G = Yellow)。
从上面的几个例子我们很容易就能明白颜色矩阵中的每个分量(每一列)的意义:
第一行决定红色,
第二行决定绿色,
第三行决定蓝色,
第四行决定了透明度,
第五列是颜色的偏移量。
至此我们应该能理解如何通过颜色矩阵来改变颜色值的各个分量了。
下面是用于Android的一段代码,用于将图片处理成泛黄的效果:
- public static Bitmap testBitmap(Bitmap bitmap){
- Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
- bitmap.getHeight(), Config.RGB_565);
-
- Canvas canvas = new Canvas(output);
-
- Paint paint = new Paint();
- ColorMatrix cm = new ColorMatrix();
- float[] array = {1,0,0,0,100,
- 0,1,0,0,100,
- 0,0,1,0,0,
- 0,0,0,1,0};
- cm.set(array);
- paint.setColorFilter(new ColorMatrixColorFilter(cm));
-
- canvas.drawBitmap(bitmap, 0, 0, paint);
- return output;
- }
3 坐标变换矩阵对图片的操作除了颜色值的处理外,最常用的就是空间坐标的变换了,常见的效果有平移、旋转、拉伸等,这其实也是通过一个矩阵来完成的。坐标变换矩阵是一个3*3的矩阵,通过与一个类似(X,Y,1)的坐标值的矩阵乘法运算,能够将这个坐标值转换成一个新的坐标值,计算过程如下:结果为:
x’=a*x+b*y+c
y’=d*x+e*y+f
同颜色矩阵一样,如果坐标变换矩阵如下,则新的坐标值X、Y增加50,也就是说图片的每一点都平移了(50,50)的距离,即图片整体平移到了(50,50)坐标处。
如果坐标变换矩阵如下,则所有的X、Y坐标都增大两倍,也就是说图片被放大了两倍,其他缩放效果原理类似。
更复杂一点的还有旋转效果,一个旋转变换矩阵如下:
结果为x’ = xcosθ – ysinθ 与 y’ = xsinθ + ycosθ,这个结果的效果是绕原点逆时针旋转θ度角。
下面是用于Android的一段示例代码,用于将图片平移,也就是裁剪的效果,其他效果可以参照对应坐标变换矩阵修改即可:
- public static Bitmap test1Bitmap(Bitmap bitmap){
- Bitmap output = Bitmap.createBitmap(bitmap.getWidth(),
- bitmap.getHeight(), Config.RGB_565);
-
- Canvas canvas = new Canvas(output);
-
- Paint paint = new Paint();
- Matrix cm = new Matrix();
-
- float[] array = {1,0,50,
- 0,1,50,
- 0,0,1};
- cm.setValues(array);
- canvas.drawBitmap(bitmap, cm, paint);
- return output;
- }
下面将介绍几种常用的变换矩阵:
1.旋转
绕原点逆时针旋转θ度角的变换公式是 x' = xcosθ − ysinθ 与 y' = xsinθ + ycosθ
2. 缩放
变换后长宽分别放大x'=scale*x;y'=scale*y.
3.切变
4.反射
5.正投影
Android的图像矩阵绝对不止这些,这是一个很复杂的知识,涉及到大学相关数学的课程,能了解大学线性代数里的矩阵知识,对学习Android下的图像矩阵有很好的帮助,在这里限于篇幅,我只做了简单的基础讲解,基本可以理解,可以使用即可,如果想深入学习一下的话,请查看下方的资料链接,去下载我今天上传到CSDN资源库里面的资料。
Android图像矩阵基础与详解资料
源码请在这里下载
0 0