• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Joanna Qian
Stay Hungry, Stay Foolish!
博客园    首页    新随笔    联系   管理    订阅  订阅
使用Android自带Gallery组件实现CoverFlow

源代码转载自国外Neil Davies,使用Apache2.0开源协议

实现了一个自定义的ImageAdapter,为图片制作倒影效果。
传入参数为context和程序内drawable中的图片ID数组。之后调用其中的createReflectedImages()方法分别创造每一个图像的倒影效果,生成对应的ImageView数组,最后在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
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* 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 image
final 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 axis
Matrix 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 image
Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0,
height / 2, width, height / 2, matrix, false);

// Create a new bitmap with same width but taller to fit reflection
Bitmap 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 reflection
Canvas canvas = new Canvas(bitmapWithReflection);
// Draw in the original image
canvas.drawBitmap(originalImage, 0, 0, null);
// Draw in the gap
Paint deafaultPaint = new Paint();
canvas.drawRect(0, height, width, height + reflectionGap,
deafaultPaint);
// Draw in the reflection
canvas.drawBitmap(reflectionImage, 0, height + reflectionGap,
null);

// Create a shader that is a linear gradient that covers the
// reflection
Paint 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 in
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
// Draw a rectangle using the paint with our linear gradient
canvas.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) */
return Math.max(0, 1.0f / (float) Math.pow(2, Math.abs(offset)));
}
}
}

仅仅实现了图片的倒影效果还不够,因为在coverflow中图片切换是有旋转和缩放效果的,而自带的gallery中并没有实现。因此,扩展自带的 gallery,实现自己的galleryflow。在原gallery类中,提供了一个方法 getChildStaticTransformation()以实现对图片的变换。通过覆写这个方法并在其中调用自定义的 transformImageBitmap(“每个图片与gallery中心的距离”)方法,,即可实现每个图片做相应的旋转和缩放。其中使用了 camera和matrix用于视图变换。具体可参考代码注释。

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 centre Child
         private int mMaxZoom = -120;
         // The Centre of the Coverflow
         */
        private int mCoveflowCenter;

        public GalleryFlow(Context context) {
                super(context);
                this.setStaticTransformationsEnabled(true);
        }

        public GalleryFlow(Context context, AttributeSet attrs) {
                super(context, attrs);
                this.setStaticTransformationsEnabled(true);
        }

        public GalleryFlow(Context context, AttributeSet attrs, int defStyle) {
                super(context, attrs, defStyle);
                this.setStaticTransformationsEnabled(true);
        }

        /**
         * Get the max rotational 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 centre image @return the mMaxZoom
         */
        public int getMaxZoom() {
                return mMaxZoom;
        }

        /**
         * Set the max zoom of the centre image @param maxZoom* the mMaxZoom to set
         */
        public void setMaxZoom(int maxZoom) {
                mMaxZoom = maxZoom;
        }

        /**
         * Get the Centre of the Coverflow
         * 
         * @return The centre of this Coverflow.
         */
        private int getCenterOfCoverflow() {
                return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2
                                + getPaddingLeft();
        }

        /**
         * Get the Centre of the View @return The centre of the given view.
         */
        private static int getCenterOfView(View 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 this 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 which 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);

                // 在Z轴上正向移动camera的视角,实际效果为放大图片。
                // 如果在Y轴上移动,则图片上下移动;X轴上对应图片左右移动。
                mCamera.translate(0.0f, 0.0f, 100.0f);

                // 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);
                }

                // 在Y轴上旋转,对应图片竖向向里翻转。
                // 如果在X轴上旋转,则对应图片横向向里翻转。
                mCamera.rotateY(rotationAngle);
                mCamera.getMatrix(imageMatrix);
                imageMatrix.preTranslate(-(imageWidth / 2), -(imageHeight / 2));
                imageMatrix.postTranslate((imageWidth / 2), (imageHeight / 2));
                mCamera.restore();
        }
}

下面是调用的Activity示例:

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:
可以看出来这样实现的gallery锯齿问题比较严重。可以在createReflectedImages()使用以下代码:

BitmapDrawable bd = new BitmapDrawable(bitmapWithReflection);
bd.setAntiAlias(true);

然后用iv.setImageDrawable(bd);
代替iv.setImageBitmap(bitmapWithReflection);
即可基本消除锯齿。

PS2:
ImageAdapter有待确定的MemoryLeak问题,貌似的Bitmap的decode方法会造成ML,使用ImageAdapter时多次旋 转屏幕后会出现OOM。目前可以通过将使用完毕的bimap调用recycle()方法和设置null并及时调用system.gc()得到一些改善,但 是问题并不明显。

 

posted on 2013-03-07 13:31  Joanna Qian  阅读(600)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3