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 uses source code parsing to implement this function. For specific code functions, see annotations.
The final implementation result is as follows:
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 parameter is the image ID array in context and drawable in the program. 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 except in compliance with the License.* You may obtain a copy of the License at** [url]http://www.apache.org/licenses/LICENSE-2.0[/url]** 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 gapPaint 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 PorterDuffXfermode(Mode.DST_IN));// Draw a rectangle using the paint with our linear gradientcanvas.drawRect(0, height, width,bitmapWithReflection.getHeight() + reflectionGap, paint); ImageView 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 jaggies* 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) */returnMath.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 code comments.
Public class galleryflow extendsgallery {/*** graphics camera used for transforming the matrix of imageviews */privatecamera mcamera = newcamera (); /*** the maximum angle the child imageview will be rotated by */privateint mmaxrotationangle = 60;/*** the maximum zoom on the center child */privateint mmaxzoom =-120; /*** the centre of the coverflow */privateint mcoveflowcenter; publicgalle Ryflow (context) {super (context); this. setstatictransformationsenabled (true);} publicgalleryflow (context, attributeset attrs) {super (context, attrs); this. setstatictransformationsenabled (true);} publicgalleryflow (context, attributeset attrs, int defstyle) {super (context, attrs, defstyle); this. setstatictransformationsenabled (true);}/*** get the max rotational angle o F The image ** @ return the mmaxrotationangle */publicint getmaxrotationangle () {returnmmaxrotationangle ;} /*** set the max rotational angle of each image *** @ Param maxrotationangle * The mmaxrotationangle to set */publicvoid setmaxrotationangle (intmaxrotationangle) {mmaxrotationangle = maxrotationangle ;} /*** get the max zoom of the center image *** @ return the mmaxzoom */publicint getm Axzoom () {returnmmaxzoom;}/*** set the max zoom of the center image ** @ Param maxzoom * The mmaxzoom to set */publicvoid setmaxzoom (intmaxzoom) {mmaxzoom = maxzoom;}/*** get the centre of the coverflow ** @ return the center of this coverflow. */privateint getcenterofcoverflow () {return (getwidth ()-getpaddingleft ()-getpaddingright ()/2 + getpaddingleft ();}/*** get the centre The view ** @ return the center of the given view. */privatestatic int getcenterofview (view) {returnview. getleft () + view. getwidth ()/2;}/*** {@ inheritdoc} ** @ see # setstatictransformationsenabled (Boolean) */protectedboolean getchildstatictransformation (view child, transformation T) {finalint childcenter = getcenterofview (child); finalint childwidth = child. getwidth (); introtationan GLE = 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);} returntrue;}/*** 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 this View. * @ Param oldh * Old height of this view. */protectedvoid onsizechanged (intw, 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 which To rotate the bitmap */privatevoid transformimagebitmap (imageview child, transformation T, introtationangle) {mcamera. save (); finalmatrix imagematrix = T. getmatrix (); finalint imageheight = child. getlayoutparams (). height; finalint imagewidth = child. getlayoutparams (). width; finalint 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, 100366f); // as the angle of the view gets less, zoom in if (rotation <mmaxrotationangle) {floatzoomamount = (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.img0100, R.drawable.img0130, R.drawable.img0200, R.drawable.img0230, R.drawable.img0300, R.drawable.img0330, R.drawable.img0354 }; ImageAdapter adapter =new ImageAdapter(this, images); adapter.createReflectedImages(); GalleryFlow 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 an algorithm that I know now, that is, to calculate the correlation between pixels and insert intermediate pixels 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.
From: http://www.eoeandroid.com/thread-39709-1-1.html
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.