Android can basically achieve coverflow through pseudo 3D Transformation

Source: Internet
Author: User

Anyone who has used Android's own gallery component knows that the effect of gallery is to drag and browse a group of images, which is obviously inferior to coverflow used to drag and browse images on the iPhone. In fact, you can extend gallery and basically implement coverflow through pseudo 3D transformation. This article passesSource codeParse the implementation of this function. DetailsCodeFor more information, see annotations.

To use gallery, we must first specify an adapter for it. Here, we implement a custom imageadapter to create a reflection effect for the image.
The input parameters are context andProgramThe image ID array in the drawable. Then, call the createreflectedimages () method to create the reflection effect of each image, generate the corresponding imageview array, and finally return it in getview.

/** Copyright (c) 2010 Neil Davies ** licensed under the Apache license, version 2.0 (the "License "); * You may not use this file before t in compliance with the license. * You may obtain a copy of the license at ** specified unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * Without warranties or conditions of any kind, either express or implied. * See the license for the specific language governing permissions and * limitations under the license. ** this code is base on the android gallery widget and was created * by Neil Davies neild001 'At 'gmail dot com To Be A coverflow widget ** @ author Neil Davies */public class imageadapter extends baseadapter {int mgalleryitembackground; private context mcontext; private integer [] mimageids; private imageview [] mimages; Public imageadapter (context c, int [] imageids) {mcontext = C; mimageids = imageids; mimages = new imageview [mimageids. length];} public Boolean createreflectedimages () {// The gap we want between the reflection and the original imagefinal int reflectiongap = 4; int Index = 0; For (INT imageid: mimageids) {bitmap originalimage = bitmapfactory. decoderesource (mcontext. getresources (), imageid); int width = originalimage. getwidth (); int Height = originalimage. getheight (); // This will not scale but will flip on the Y axismatrix matrix = new matrix (); matrix. prescale (1,-1); // create a bitmap with the flip matrix applied to it. // We only want the bottom half of the imagebitmap reflectionimage = bitmap. createbitmap (originalimage, 0, height/2, width, height/2, matrix, false ); // create a new bitmap with same width but taller to fit // reflectionbitmap bitmapwithreflection = bitmap. createbitmap (width, (height + height/2), config. argb_8888); // create a new canvas with the bitmap that's big enough for // The image plus gap plus reflectioncanvas canvas = new canvas (bitmapwithreflection ); // draw in the original imagecanvas. drawbitmap (originalimage, 0, 0, null); // draw in the gap paint deafaultpaint = new paint (); canvas. drawrect (0, height, width, height + reflectiongap, deafaultpaint); // draw in the reflectioncanvas. drawbitmap (reflectionimage, 0, height + reflectiongap, null); // create a shader that is a linear gradient that covers the // reflectionpaint paint = new paint (); lineargradient shader = new lineargradient (0, originalimage. getheight (), 0, bitmapwithreflection. getheight () + reflectiongap, 0x70ffffff, 0x00ffffff, tilemode. clamp); // set the paint to use this shader (linear gradient) paint. setshader (shader); // set the transfer mode to be Porter Duff and destination inpaint. setxfermode (New porterduduxfermode (mode. dst_in); // draw a rectangle using the paint with our linear gradientcanvas. drawrect (0, height, width, bitmapwithreflection. getheight () + reflectiongap, paint); imageview = new imageview (mcontext); imageview. setimagebitmap (bitmapwithreflection); imageview. setlayoutparams (New galleryflow. layoutparams (160,240); // imageview. setscaletype (scaletype. matrix); mimages [index ++] = imageview;} return true;} public int getcount () {return mimageids. length;} public object getitem (INT position) {return position;} public long getitemid (INT position) {return position;} public view getview (INT position, view convertview, viewgroup parent) {// use this code if you want to load from resources/** imageview I = new imageview (mcontext); * I. setimageresource (mimageids [position]); I. setlayoutparams (New * coverflow. layoutparams (350,350); * I. setscaletype (imageview. scaletype. center_inside); ** // make sure we set anti-aliasing otherwise we get jargies * bitmapdrawable drawable = (bitmapdrawable) I. getdrawable (); * drawable. setantialias (true); return I; */return mimages [position];}/*** returns the size (0.0f to 1.0f) of the views depending on the * 'offset' to the center. */public float getscale (Boolean focused, int offset) {/* formula: 1/(2 ^ offset) */return math. max (0, 1.0f/(float) math. pow (2, math. ABS (offset )));}}}

it is not enough to achieve only the image reflection effect, because in coverflow, image switching has the rotation and scaling effects, but not in the built-in Gallery. Therefore, we can extend our own gallery to implement our own galleryflow. In the original gallery class, a method getchildstatictransformation () is provided to transform the image. By overwriting this method and calling the custom transformimagebitmap ("the distance between each image and the gallery center") method, we can rotate and scale each image accordingly. Camera and matrix are used for view transformation. For more information, see

Public class galleryflow extends gallery {/*** graphics camera used for transforming the matrix of imageviews */private camera mcamera = new camera (); /*** the maximum angle the child imageview will be rotated by */private int mmaxrotationangle = 60; /*** the maximum zoom on the center child */private int mmaxzoom =-120;/*** the centre of the coverflow */private int mcoveflowcenter; Publ IC galleryflow (context) {super (context); this. setstatictransformationsenabled (true);} public galleryflow (context, attributeset attrs) {super (context, attrs); this. setstatictransformationsenabled (true);} public galleryflow (context, attributeset attrs, int defstyle) {super (context, attrs, defstyle); this. setstatictransformationsenabled (true);}/*** get the max Rotation Al angle of the image ** @ return the mmaxrotationangle */Public int getmaxrotationangle () {return mmaxrotationangle ;} /*** set the max rotational angle of each image *** @ Param maxrotationangle * The mmaxrotationangle to set */Public void setmaxrotationangle (INT maxrotationangle) {mmaxrotationangle = maxrotationangle ;} /*** get the max zoom of the center image *** @ return the mmaxzoom */ Public int getmaxzoom () {return mmaxzoom ;} /*** set the max zoom of the center image ** @ Param maxzoom * The mmaxzoom to set */Public void setmaxzoom (INT maxzoom) {mmaxzoom = maxzoom ;} /*** get the centre of the coverflow *** @ return the center of this coverflow. */private int getcenterofcoverflow () {return (getwidth ()-getpaddingleft ()-getpaddingright ()/2 + getpaddingleft ();}/** * Get the centre of the view ** @ return the center of the given view. */Private Static int getcenterofview (view) {return view. getleft () + view. getwidth ()/2;}/*** {@ inheritdoc} ** @ see # setstatictransformationsenabled (Boolean) */protected Boolean getchildstatictransformation (view child, transformation T) {final int childcenter = getcenterofview (child); Final int childwidth = child. Getwidth (); int rotationangle = 0; T. clear (); T. settransformationtype (transformation. type_matrix); If (childcenter = mcoveflowcenter) {transformimagebitmap (imageview) Child, T, 0);} else {rotationangle = (INT) (float) (mcoveflowcenter-childcenter)/childwidth) * mmaxrotationangle); If (math. ABS (rotationangle)> mmaxrotationangle) {rotationangle = (rotationangle <0 )? -Mmaxrotationangle: mmaxrotationangle;} transformimagebitmap (imageview) Child, T, rotationangle);} return true ;} /*** this is called during layout when the size of this view has changed. if * You were just added to the view hierarchy, you're called with the old * values of 0. ** @ Param w * current width of this view. * @ Param H * current height of this view. * @ Param oldw * Old width of thi S view. * @ Param oldh * Old height of this view. */protected void onsizechanged (int w, int H, int oldw, int oldh) {mcoveflowcenter = getcenterofcoverflow (); super. onsizechanged (W, H, oldw, oldh );} /*** transform the image bitmap by the angle passed ** @ Param imageview * imageview the imageview whose bitmap we want to rotate * @ Param T * transformation * @ Param rotationangle * The Angle by whi Ch to rotate the bitmap */private void transformimagebitmap (imageview child, transformation T, int rotationangle) {mcamera. save (); Final matrix imagematrix = T. getmatrix (); Final int imageheight = child. getlayoutparams (). height; Final int imagewidth = child. getlayoutparams (). width; Final int rotation = math. ABS (rotationangle); // forward the camera angle on the Z axis. The actual effect is to enlarge the image. // If the image is moved on the Y axis, the image is moved up and down; on the X axis, the image is moved left and right. Mcamera. translate (0.0f, 0.0f, 1001_f); // as the angle of the view gets less, zoom in if (rotation <mmaxrotationangle) {float zoomamount = (float) (mmaxzoom + (rotation * 1.5); mcamera. translate (0.0f, 0.0f, zoomamount);} // rotate on the Y axis, and flip the corresponding image vertically to the inside. // If the image is rotated on the X axis, the image is reversed horizontally. Mcamera. rotatey (rotationangle); mcamera. getmatrix (imagematrix); imagematrix. pretranslate (-(imagewidth/2),-(imageheight/2); imagematrix. posttranslate (imagewidth/2), (imageheight/2); mcamera. restore ();}}

The Code ends here. If you are interested, you can adjust the parameters to achieve more and more dazzling results.

The following is an example of a call:

Public void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. layout_gallery); integer [] images = {R. drawable. img0001, R. drawable. img0030, R. drawable. im0000100, R. drawable. img0130, R. drawable. im0000200, R. drawable. img0230, R. drawable. im0000300, R. drawable. img0330, R. drawable. img0354}; imageadapter adapter = new imageadapter (this, images); adapter. createreflectedimages (); galleryflow = (galleryflow) findviewbyid (R. id. gallery_flow); galleryflow. setadapter (adapter );}

PS1:

It can be seen that this implementation of the gallery sawtooth problem is more serious. You can use the following code in createreflectedimages:

 
Bitmapdrawable BD = new bitmapdrawable (bitmapwithreflection); Bd. setantialias (true );...

Then use IV. setimagedrawable (BD );

Replace IV. setimagebitmap (bitmapwithreflection );
You can basically eliminate the Sawtooth.

PS2:
The memoryleak problem to be determined by the imageadapter. The decode method of Bitmap may cause ml. OOM may appear after multiple screen rotation when imageadapter is used. Currently, you can call the recycle () method and set null and call system. GC () in time by calling the completed bimap, but the problem is not obvious.

Celebrate the essence and recommendations, increase 3 Ps ~

PS3 on PS1:
Why is it not obvious after anti-tooth protection is enabled on the 256 floor. The answer is: it is impossible to completely eliminate the sawtooth, but it will be greatly improved after anti-sawtooth is enabled.
In addition, I also talked about why Android does not enable sawtooth by default. The following is my idea:
Interpolation is what I know now Algorithm That is, to calculate the correlation between pixels and insert the middle pixel to smooth the image edge. However, this will undoubtedly consume a lot of operations.
Although I have not passed the test, I guess the image performance will be reduced by at least 30% after using antialias.
Of course, there is no complicated graphic operation involved here, so enabling anti-aliasing will not have a significant performance impact, but if you test the simulator or low-end models, you will find a problem.


PS4:
Someone asked what the two sentences in transformimagebitmap () mean:
Imagematrix. pretranslate (-(imagewidth/2),-(imageheight/2 ));
Imagematrix. posttranslate (imagewidth/2), (imageheight/2 ));
My personal understanding is as follows:
Pretranslate is equivalent to pretranslate before any matrix transformation of the image. posttranslate is the opposite, and then posttranlate is executed after all transformation.
The meaning of these two sentences is: before any transformation, first move the entire image from the center of the image to the origin (0, 0 ), after the transformation is completed, the image is moved from the origin point to the previous center point.
If you do not add these two sentences, any transformation will take the image origin as the center point of the transformation. After the addition, any transformation will take the image center as the center point of the transformation.
For example, to rotate an image, two parameters are required: one is the rotation angle and the other is the coordinate of the rotation center. The coordinates of the rotation center affect the rotation effect. Do you understand this? You take a stick and take one end of the stick for rotation and take it in the middle of the stick, is not the same. After pretranslate and posttranslate are executed, the image itself will not be affected, and the rotation axis during image conversion will be affected.
Having said so much is actually the knowledge of matrix transformation.


Ps5 on PS2:
This issue has been fully discussed in Google group, and it seems that it only exists in debug mode. Now I am using this code without having an OOM problem.


Download Demo: Gallery.rar



Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.