Android gallery 3D效果

来源:互联网 发布:淘宝备注怎么备注 编辑:程序博客网 时间:2024/05/16 15:34

转:http://www.open-open.com/lib/view/open1350309480336.html   

在看了iOS上面的CoverFlow后,感觉效果真的不错,就想在android上面实现一个,这个程序在网上参考了一此核心的代码,当然我添加了一些其他的东西,废话不多话,先看效果,不然就是无图无真相了。

 

Android gallery 3D效果

 

其实实现这个效果很简单,下面作一个简单的介绍

 

一,创建倒影效果

这个基本思路是:

1,创建一个源图一样的图,利用martrix将图片旋转180度。这个倒影图的高是源图的一半。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Matrix matrix = newMatrix();
 
// 1表示放大比例,不放大也不缩小。
// -1表示在y轴上相反,即旋转180度。
matrix.preScale(1, -1);
 
Bitmap reflectionBitmap = Bitmap.createBitmap(
    srcBitmap,
    0,
    srcBitmap.getHeight() / 2,  // top为源图的一半
    srcBitmap.getWidth(),       // 宽度与源图一样
    srcBitmap.getHeight() / 2,  // 高度与源图的一半
    matrix,
    false);

2,创建一个最终效果的图,即源图 + 间隙 + 倒影。

?
1
2
3
4
5
6
finalint REFLECTION_GAP = 5;
 
Bitmap bitmapWithReflection = Bitmap.createBitmap(
       reflectionWidth,
       srcHeight + reflectionHeight + REFLECTION_GAP,
       Config.ARGB_8888);

3,依次将源图、倒影图绘制在最终的bitmap上面。

?
1
2
3
4
5
6
7
8
// Prepare the canvas to draw stuff.
Canvas canvas = newCanvas(bitmapWithReflection);
             
// Draw the original bitmap.
canvas.drawBitmap(srcBitmap, 00null);
             
// Draw the reflection bitmap.
canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);
4,创建LinearGradient,从而给定一个由上到下的渐变色。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Paint paint = newPaint();
paint.setAntiAlias(true);
LinearGradient shader = newLinearGradient(
        0,
        srcHeight,
        0,
        bitmapWithReflection.getHeight() + REFLECTION_GAP,
        0x70FFFFFF,
        0x00FFFFFF,
        TileMode.MIRROR);
paint.setShader(shader);
paint.setXfermode(newPorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));
 
// Draw the linear shader.
canvas.drawRect(
        0,
        srcHeight,
        srcWidth,
        bitmapWithReflection.getHeight() + REFLECTION_GAP,
        paint);

回到顶部

二,扩展Gallery

扩展系统的gallery,我们需要重写两个方法,getChildStaticTransformation()和getChildDrawingOrder(),同时,要使这两个方法能被调用,必须执行如下两行代码,文档上面是有说明的。

?
1
2
3
4
// Enable set transformation.
this.setStaticTransformationsEnabled(true);
// Enable set the children drawing order.
this.setChildrenDrawingOrderEnabled(true);

  • getChildDrawingOrder的实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Override
protectedint getChildDrawingOrder(intchildCount, inti)
{
    // Current selected index.
    intselectedIndex = getSelectedItemPosition() - getFirstVisiblePosition();
    if(selectedIndex < 0)
    {
        returni;
    }
     
    if(i < selectedIndex)
    {
        returni;
    }
    elseif (i >= selectedIndex)
    {
        returnchildCount - 1- i + selectedIndex;
    }
    else
    {
        returni;
    }
}

这里为什么要计算drawing order,因为从上图中看到,我们的效果是:中间左边的顺序是 0, 1, 2,右边的child覆盖左边的child,而在中间右边的顺序正好相反,左边的覆盖右边的,所以我们要重写这个方法,而gallery自身的实现,不是这种效果。

  • getChildStaticTransformation的实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
protectedboolean getChildStaticTransformation(View child, Transformation t)
{
    super.getChildStaticTransformation(child, t);
     
    finalint childCenter = getCenterOfView(child);
    finalint childWidth  = child.getWidth();
     
    introtationAngle = 0;
    t.clear();
    t.setTransformationType(Transformation.TYPE_MATRIX);
     
    // If the child is in the center, we do not rotate it.
    if(childCenter == mCoveflowCenter)
    {
        transformImageBitmap(child, t, 0);
    }
    else
    {
        // Calculate the rotation angle.
        rotationAngle = (int)(((float)(mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
         
        // Make the angle is not bigger than maximum.
        if(Math.abs(rotationAngle) > mMaxRotationAngle)
        {
            rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;
        }
         
        transformImageBitmap(child, t, rotationAngle);
    }
     
    returntrue;
}

这个方法就是根据child来计算它的transformation(变换),我们需要去修改它里面的matrix,从而达到旋转的效果。根据位置和角度来计算的matrix的方法写在另外一个方法transformImageBitmap中实现。

  • transformImageBitmap()的实现

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
privatevoid transformImageBitmap(View child, Transformation t, introtationAngle)
{
    mCamera.save();
     
    finalMatrix imageMatrix = t.getMatrix();
    finalint imageHeight = child.getHeight();
    finalint imageWidth  = child.getWidth();
    finalint rotation    = Math.abs(rotationAngle);
     
    // Zoom on Z axis.
    mCamera.translate(00, mMaxZoom);
     
    if(rotation < mMaxRotationAngle)
    {
        floatzoomAmount = (float)(mMaxZoom + rotation * 1.5f);
        mCamera.translate(00, zoomAmount);
    }
     
    // Rotate the camera on Y axis.
    mCamera.rotateY(rotationAngle);
    // Get the matrix from the camera, in fact, the matrix is S (scale) transformation.
    mCamera.getMatrix(imageMatrix);
     
    // The matrix final is T2 * S * T1, first translate the center point to (0, 0),
    // then scale, and then translate the center point to its original point.
    // T * S * T
     
    // S * T1
    imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));
    // (T2 * S) * T1
    imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
     
    mCamera.restore();
}

这里,简单说明一个,

        第一,先在Z轴上平称,其实就是得到一个缩放矩阵变换,我这里简写为 S。

        第二,是利用camera这个类来生成matrix,其实mCamera.rotateY就是围绕Y轴旋转。这里生成了一个旋转矩阵,记为 R 。经过这两步,此时调用mCamera.getMatrix(imageMatrix); 从Camera中得到matrix,此时这个矩阵中包含了S * R。

        第三,最关键是下面两句       

?
1
2
3
4
// S * T1
 imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));
 // (T2 * S) * T1
 imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
        由于这里涉及到旋转与缩放,缩放操作其实应该是针对Child中点进行了,这里就是作一个平衡操作,我们必须是先平移,再缩放,再平移回原来位置,所以,我们最终的矩阵变换应该是这样的:

        M = T * (S * R) * T1   (这里在T1表示与T相反)。

回到顶部

三,完整代码

GalleryFlow.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
importandroid.content.Context;
importandroid.graphics.Camera;
importandroid.graphics.Matrix;
importandroid.util.AttributeSet;
importandroid.view.View;
importandroid.view.animation.Transformation;
importandroid.widget.Gallery;
 
publicclass GalleryFlow extendsGallery
{
    /**
     * The camera class is used to 3D transformation matrix.
     */
    privateCamera mCamera = newCamera();
     
    /**
     * The max rotation angle.
     */
    privateint mMaxRotationAngle = 60;
     
    /**
     * The max zoom value (Z axis).
     */
    privateint mMaxZoom = -120;
     
    /**
     * The center of the gallery.
     */
    privateint mCoveflowCenter = 0;
     
    publicGalleryFlow(Context context)
    {
        this(context, null);
    }
     
    publicGalleryFlow(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }
     
    publicGalleryFlow(Context context, AttributeSet attrs, intdefStyle)
    {
        super(context, attrs, defStyle);
         
        // Enable set transformation.
        this.setStaticTransformationsEnabled(true);
        // Enable set the children drawing order.
        this.setChildrenDrawingOrderEnabled(true);
    }
     
    publicint getMaxRotationAngle()
    {
        returnmMaxRotationAngle;
    }
     
    publicvoid setMaxRotationAngle(intmaxRotationAngle)
    {
        mMaxRotationAngle = maxRotationAngle;
    }
     
    publicint getMaxZoom()
    {
        returnmMaxZoom;
    }
     
    publicvoid setMaxZoom(intmaxZoom)
    {
        mMaxZoom = maxZoom;
    }
     
    @Override
    protectedint getChildDrawingOrder(intchildCount, inti)
    {
        // Current selected index.
        intselectedIndex = getSelectedItemPosition() - getFirstVisiblePosition();
        if(selectedIndex < 0)
        {
            returni;
        }
         
        if(i < selectedIndex)
        {
            returni;
        }
        elseif (i >= selectedIndex)
        {
            returnchildCount - 1- i + selectedIndex;
        }
        else
        {
            returni;
        }
    }
     
    @Override
    protectedvoid onSizeChanged(intw, inth, intoldw, intoldh)
    {
        mCoveflowCenter = getCenterOfCoverflow();
        super.onSizeChanged(w, h, oldw, oldh);
    }
     
    privateint getCenterOfView(View view)
    {
        returnview.getLeft() + view.getWidth() / 2;
    }
     
    @Override
    protectedboolean getChildStaticTransformation(View child, Transformation t)
    {
        super.getChildStaticTransformation(child, t);
         
        finalint childCenter = getCenterOfView(child);
        finalint childWidth  = child.getWidth();
         
        introtationAngle = 0;
        t.clear();
        t.setTransformationType(Transformation.TYPE_MATRIX);
         
        // If the child is in the center, we do not rotate it.
        if(childCenter == mCoveflowCenter)
        {
            transformImageBitmap(child, t, 0);
        }
        else
        {
            // Calculate the rotation angle.
            rotationAngle = (int)(((float)(mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
             
            // Make the angle is not bigger than maximum.
            if(Math.abs(rotationAngle) > mMaxRotationAngle)
            {
                rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;
            }
             
            transformImageBitmap(child, t, rotationAngle);
        }
         
        returntrue;
    }
     
    privateint getCenterOfCoverflow()
    {
        return(getWidth() - getPaddingLeft() - getPaddingRight()) / 2+ getPaddingLeft();
    }
     
    privatevoid transformImageBitmap(View child, Transformation t, introtationAngle)
    {
        mCamera.save();
         
        finalMatrix imageMatrix = t.getMatrix();
        finalint imageHeight = child.getHeight();
        finalint imageWidth  = child.getWidth();
        finalint rotation    = Math.abs(rotationAngle);
         
        // Zoom on Z axis.
        mCamera.translate(00, mMaxZoom);
         
        if(rotation < mMaxRotationAngle)
        {
            floatzoomAmount = (float)(mMaxZoom + rotation * 1.5f);
            mCamera.translate(00, zoomAmount);
        }
         
        // Rotate the camera on Y axis.
        mCamera.rotateY(rotationAngle);
        // Get the matrix from the camera, in fact, the matrix is S (scale) transformation.
        mCamera.getMatrix(imageMatrix);
         
        // The matrix final is T2 * S * T1, first translate the center point to (0, 0),
        // then scale, and then translate the center point to its original point.
        // T * S * T
         
        // S * T1
        imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));
        // (T2 * S) * T1
        imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
         
        mCamera.restore();
    }
}

BitmapUtil.java

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
packagecom.lee.gallery3d.utils;
 
importandroid.graphics.Bitmap;
importandroid.graphics.Bitmap.Config;
importandroid.graphics.Canvas;
importandroid.graphics.LinearGradient;
importandroid.graphics.Matrix;
importandroid.graphics.Paint;
importandroid.graphics.PixelFormat;
importandroid.graphics.PorterDuffXfermode;
importandroid.graphics.Shader.TileMode;
importandroid.graphics.drawable.Drawable;
 
publicclass BitmapUtil
{
    publicstatic Bitmap createReflectedBitmap(Bitmap srcBitmap)
    {
        if(null== srcBitmap)
        {
            returnnull;
        }
         
        // The gap between the reflection bitmap and original bitmap.
        finalint REFLECTION_GAP = 4;
         
        intsrcWidth  = srcBitmap.getWidth();
        intsrcHeight = srcBitmap.getHeight();
        intreflectionWidth  = srcBitmap.getWidth();
        intreflectionHeight = srcBitmap.getHeight() / 2;
         
        if(0== srcWidth || srcHeight == 0)
        {
            returnnull;
        }
         
        // The matrix
        Matrix matrix = newMatrix();
        matrix.preScale(1, -1);
         
        try
        {
            // The reflection bitmap, width is same with original's, height is half of original's.
            Bitmap reflectionBitmap = Bitmap.createBitmap(
                    srcBitmap,
                    0,
                    srcHeight / 2,
                    srcWidth,
                    srcHeight / 2,
                    matrix,
                    false);
             
            if(null== reflectionBitmap)
            {
                returnnull;
            }
             
            // Create the bitmap which contains original and reflection bitmap.
            Bitmap bitmapWithReflection = Bitmap.createBitmap(
                    reflectionWidth,
                    srcHeight + reflectionHeight + REFLECTION_GAP,
                    Config.ARGB_8888);
             
            if(null== bitmapWithReflection)
            {
                returnnull;
            }
             
            // Prepare the canvas to draw stuff.
            Canvas canvas = newCanvas(bitmapWithReflection);
             
            // Draw the original bitmap.
            canvas.drawBitmap(srcBitmap, 00null);
             
            // Draw the reflection bitmap.
            canvas.drawBitmap(reflectionBitmap, 0, srcHeight + REFLECTION_GAP, null);
             
            Paint paint = newPaint();
            paint.setAntiAlias(true);
            LinearGradient shader = newLinearGradient(
                    0,
                    srcHeight,
                    0,
                    bitmapWithReflection.getHeight() + REFLECTION_GAP,
                    0x70FFFFFF,
                    0x00FFFFFF,
                    TileMode.MIRROR);
            paint.setShader(shader);
            paint.setXfermode(newPorterDuffXfermode(android.graphics.PorterDuff.Mode.DST_IN));
             
            // Draw the linear shader.
            canvas.drawRect(
                    0,
                    srcHeight,
                    srcWidth,
                    bitmapWithReflection.getHeight() + REFLECTION_GAP,
                    paint);
             
            returnbitmapWithReflection;
        }
        catch(Exception e)
        {
            e.printStackTrace();
        }
         
        returnnull;
    }
}
0 0