有两种,一种是直接继承Gallery的,通过override Gallery的getChildStaticTransformation方法实现,这个网上有,速度还不错,最开始采用的这个方案。
后来有时间,又觉得opengl可能会更快一些,所以照着iOS上很著名的FlowCover写了一个。两个的代码都贴出来吧,仅供参考。
第一种:
继承Gallery,重载getChildStaticTransformation函数,通过Camera来模拟变换,加了一些平移和旋转。
这个我当时碰到的主要问题在于怎么才能让距离比较合适,因为需要左右两边的几个最后能够叠在一起,所以需要一个公式来进行平移,这个公式应该满足f(max) = max, f(0) = 0, f(x) > x,其中x是距中心的距离,max是最后一个需要平移的函数,这儿我选择了左右的第二个,最后使用的是和开方有关的函数。
因为继承的是Gallery,所以只能通过spacing来控制需要显示多少个内容,不太方便,这个也是我后来写了opengl的原因。
- import android.content.Context;
- import android.graphics.Camera;
- import android.graphics.Matrix;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.animation.Transformation;
- import android.widget.Gallery;
- import android.widget.ImageView;
- public class CoverFlow extends Gallery {
- private static final String TAG = "CoverFlow";
- private static float DELTAX = 0;
- private Camera mCamera = new Camera();
- private int mMaxRotationAngle = 60;
- private int mMaxZoom = -120;
- private int mCoveflowCenter;
- private float mSpeed = 2;
- public CoverFlow(Context context, float imagesize, float spacing) {
- super(context);
- DELTAX = (float) Math.sqrt(imagesize - spacing);
- this.setStaticTransformationsEnabled(true);
- }
- public int getMaxRotationAngle() {
- return mMaxRotationAngle;
- }
- public void setMaxRotationAngle(int maxRotationAngle) {
- mMaxRotationAngle = maxRotationAngle;
- }
- public int getMaxZoom() {
- return mMaxZoom;
- }
- public void setMaxZoom(int maxZoom) {
- mMaxZoom = maxZoom;
- }
- private int getCenterOfCoverflow() {
- return (getWidth() - getPaddingLeft() - getPaddingRight()) / 2 + getPaddingLeft();
- }
- private static int getCenterOfView(View view) {
- return view.getLeft() + view.getWidth() / 2;
- }
- @Override
- protected int getChildDrawingOrder(int childCount, int i) {
- // TODO Auto-generated method stub
- //return super.getChildDrawingOrder(childCount, i);
- int select = getSelectedItemPosition() - getFirstVisiblePosition();
- if (i < select) {
- return i;
- } else if (i == select) {
- return childCount - 1;
- } else {
- return select + (childCount - i - 1);
- }
- }
- @Override
- 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, 0);
- } else {
- android.util.Log.i(TAG, "x: " + (mCoveflowCenter - childCenter));
- rotationAngle = (int) (((float) (mCoveflowCenter - childCenter) / childWidth) * mMaxRotationAngle);
- if (Math.abs(rotationAngle) > mMaxRotationAngle) {
- rotationAngle = (rotationAngle < 0) ? -mMaxRotationAngle : mMaxRotationAngle;
- }
- transformImageBitmap((ImageView) child, t, rotationAngle, mCoveflowCenter - childCenter);
- }
- return true;
- }
- protected void onSizeChanged(int w, int h, int oldw, int oldh) {
- mCoveflowCenter = getCenterOfCoverflow();
- super.onSizeChanged(w, h, oldw, oldh);
- }
- private void transformImageBitmap(ImageView child, Transformation t, int rotationAngle, int delta) {
- mCamera.save();
- final Matrix imageMatrix = t.getMatrix();;
- final int imageHeight = child.getHeight();
- final int imageWidth = child.getWidth();
- final int rotation = Math.abs(rotationAngle);
- 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);
- }
- mCamera.rotateY(rotationAngle);
- float absx = Math.abs(delta);
- float x = (float) (DELTAX * Math.sqrt(absx) - absx);
- if (delta > 0) {
- mCamera.translate(-x, 0, 0);
- } else if (delta < 0){
- mCamera.translate(x, 0, 0);
- }
- mCamera.getMatrix(imageMatrix);
- imageMatrix.preTranslate(-(imageWidth/2), -(imageHeight/2));
- imageMatrix.postTranslate((imageWidth/2), (imageHeight/2));
- mCamera.restore();
- }
- public void setSpeed(float speed) {
- mSpeed = speed;
- }
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
- float velocityY) {
- return super.onFling(e1, e2, velocityX / mSpeed, velocityY);
- }
- }
- private static final float IMAGE_SIZE_MDPI = 170.6667f;
- private static final float GALLERY_SPACING_MDPI = -23.333f;
- IMAGE_SIZE = IMAGE_SIZE_MDPI * dpi / 160;
- GALLERY_SPACING = GALLERY_SPACING_MDPI * dpi / 160;
- mGalleryLandscape = new CoverFlow(this, IMAGE_SIZE, GALLERY_SPACING);
- mGalleryLandscape.setCallbackDuringFling(false);
- mGalleryLandscape.setAdapter(new GalleryLandscapeAdapter(this, mLocation));
- mGalleryLandscape.setSpacing((int) GALLERY_SPACING);
因为需要放缩一下图片,所以Adpater的getView函数是这个样子的:
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- ImageView view = (ImageView) convertView;
- if (view == null) {
- view = new ImageView(parent.getContext());
- }
- Bitmap bitmap = BitmapFactory.decodeFile(mLocation.get(position).getLocalImageUrl().getAbsolutePath());
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- Bitmap bm;
- if (width > IMAGE_SIZE || height > IMAGE_SIZE) {
- float scale = 1.0f;
- if (width > height) {
- scale = ((float) IMAGE_SIZE) / width;
- } else {
- scale = ((float) IMAGE_SIZE) / height;
- }
- width = (int) (width * scale);
- height = (int) (height * scale);
- android.util.Log.i(TAG, IMAGE_SIZE + " " + width + " " + height);
- bm = Bitmap.createScaledBitmap(bitmap, width, height, false);
- bitmap.recycle();
- } else {
- bm = bitmap;
- }
- view.setImageBitmap(bm);
- return view;
- }
第二种OpenGL
这个的基本思想是抄iOS的,但是进行了一些更改,例如利用velocitytracker进行速度的计算等。
- package com.locatify.fengji.coverflow;
- import java.nio.ByteBuffer;
- import java.nio.ByteOrder;
- import java.nio.FloatBuffer;
- import javax.microedition.khronos.egl.EGLConfig;
- import javax.microedition.khronos.opengles.GL10;
- import android.content.Context;
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.graphics.Canvas;
- import android.graphics.Paint;
- import android.graphics.PixelFormat;
- import android.graphics.Rect;
- import android.graphics.RectF;
- import android.opengl.GLSurfaceView;
- import android.opengl.GLU;
- import android.opengl.GLUtils;
- import android.view.MotionEvent;
- import android.view.VelocityTracker;
- import android.view.animation.AnimationUtils;
- public class CoverFlowOpenGL extends GLSurfaceView implements GLSurfaceView.Renderer {
- private static final String TAG = "AnotherCoverFlow";
- private static final int TOUCH_MINIMUM_MOVE = 5;
- private static final int IMAGE_SIZE = 256; // the bitmap size we use for the texture
- private static final int MAX_TILES = 48; // the maximum tiles in the cache
- private static final int VISIBLE_TILES = 3; // the visble tiles left and right
- private static final float SCALE = 0.7f; // the scale of surface view
- private static final float SPREAD_IMAGE = 0.14f;
- private static final float FLANK_SPREAD = 0.4f;
- private static final float FRICTION = 10.0f;
- private static final float MAX_SPEED = 6.0f;
- private static final float[] GVertices = new float[]{
- -1.0f, -1.0f, 0.0f,
- 1.0f, -1.0f, 0.0f,
- -1.0f, 1.0f, 0.0f,
- 1.0f, 1.0f, 0.0f,
- };
- private static final float[] GTextures = new float[]{
- 0.0f, 1.0f,
- 1.0f, 1.0f,
- 0.0f, 0.0f,
- 1.0f, 0.0f,
- };
- private GL10 mGLContext;
- private FloatBuffer mVerticesBuffer;
- private FloatBuffer mTexturesBuffer;
- private float[] mMatrix;
- private int mBgTexture;
- private FloatBuffer mBgVerticesBuffer;
- private FloatBuffer mBgTexturesBuffer;
- private Bitmap mBgBitmap;
- private boolean mInitBackground;
- private float mOffset;
- private RectF mTouchRect;
- private int mWidth;
- private boolean mTouchMoved;
- private float mTouchStartPos;
- private float mTouchStartX;
- private float mTouchStartY;
- private float mStartOffset;
- private long mStartTime;
- private float mStartSpeed;
- private float mDuration;
- private Runnable mAnimationRunnable;
- private VelocityTracker mVelocity;
- private boolean mStopBackgroundThread;
- private CoverFlowListener mListener;
- private DataCache<Integer, CoverFlowRecord> mCache;
- public CoverFlowOpenGL(Context context) {
- super(context);
- setEGLConfigChooser(8, 8, 8, 8, 16, 0);
- setRenderer(this);
- setRenderMode(RENDERMODE_WHEN_DIRTY);
- getHolder().setFormat(PixelFormat.TRANSLUCENT);
- //setZOrderMediaOverlay(true);
- //setZOrderOnTop(true);
- mCache = new DataCache<Integer, CoverFlowRecord>(MAX_TILES);
- mOffset = 0;
- mInitBackground = false;
- }
- public void setCoverFlowListener(CoverFlowListener listener) {
- mListener = listener;
- }
- private float checkValid(float off) {
- int max = mListener.getCount(this) - 1;
- if (off < 0)
- return 0;
- else if (off > max)
- return max;
- return off;
- }
- public void setSelection(int position) {
- endAnimation();
- mOffset = position;
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- int action = event.getAction();
- switch(action) {
- case MotionEvent.ACTION_DOWN:
- touchBegan(event);
- return true;
- case MotionEvent.ACTION_MOVE:
- touchMoved(event);
- return true;
- case MotionEvent.ACTION_UP:
- touchEnded(event);
- return true;
- }
- return false;
- }
- private void touchBegan(MotionEvent event) {
- endAnimation();
- float x = event.getX();
- mTouchStartX = x;
- mTouchStartY = event.getY();
- mStartTime = System.currentTimeMillis();
- mStartOffset = mOffset;
- mTouchMoved = false;
- mTouchStartPos = (x / mWidth) * 10 - 5;
- mTouchStartPos /= 2;
- mVelocity = VelocityTracker.obtain();
- mVelocity.addMovement(event);
- }
- private void touchMoved(MotionEvent event) {
- float pos = (event.getX() / mWidth) * 10 - 5;
- pos /= 2;
- if (!mTouchMoved) {
- float dx = Math.abs(event.getX() - mTouchStartX);
- float dy = Math.abs(event.getY() - mTouchStartY);
- if (dx < TOUCH_MINIMUM_MOVE && dy < TOUCH_MINIMUM_MOVE)
- return ;
- mTouchMoved = true;
- }
- mOffset = checkValid(mStartOffset + mTouchStartPos - pos);
- requestRender();
- mVelocity.addMovement(event);
- }
- private void touchEnded(MotionEvent event) {
- float pos = (event.getX() / mWidth) * 10 - 5;
- pos /= 2;
- if (mTouchMoved) {
- mStartOffset += mTouchStartPos - pos;
- mStartOffset = checkValid(mStartOffset);
- mOffset = mStartOffset;
- mVelocity.addMovement(event);
- mVelocity.computeCurrentVelocity(1000);
- double speed = mVelocity.getXVelocity();
- speed = (speed / mWidth) * 10;
- android.util.Log.i(TAG, "SPEED: " + speed);
- if (speed > MAX_SPEED)
- speed = MAX_SPEED;
- else if (speed < -MAX_SPEED)
- speed = -MAX_SPEED;
- startAnimation(-speed);
- } else {
- if (mTouchRect.contains(event.getX(), event.getY())) {
- mListener.topTileClicked(this, (int) (mOffset + 0.01));
- }
- }
- }
- private void startAnimation(double speed) {
- if (mAnimationRunnable != null)
- return ;
- double delta = speed * speed / (FRICTION * 2);
- if (speed < 0)
- delta = -delta;
- double nearest = mStartOffset + delta;
- nearest = Math.floor(nearest + 0.5);
- nearest = checkValid((float) nearest);
- mStartSpeed = (float) Math.sqrt(Math.abs(nearest - mStartOffset) * FRICTION * 2);
- if (nearest < mStartOffset)
- mStartSpeed = -mStartSpeed;
- mDuration = Math.abs(mStartSpeed / FRICTION);
- mStartTime = AnimationUtils.currentAnimationTimeMillis();
- mAnimationRunnable = new Runnable() {
- @Override
- public void run() {
- driveAnimation();
- }
- };
- post(mAnimationRunnable);
- }
- private void driveAnimation() {
- float elapsed = (AnimationUtils.currentAnimationTimeMillis() - mStartTime) / 1000.0f;
- if (elapsed >= mDuration)
- endAnimation();
- else {
- updateAnimationAtElapsed(elapsed);
- post(mAnimationRunnable);
- }
- }
- private void endAnimation() {
- if (mAnimationRunnable != null) {
- mOffset = (float) Math.floor(mOffset + 0.5);
- mOffset = checkValid(mOffset);
- requestRender();
- removeCallbacks(mAnimationRunnable);
- mAnimationRunnable = null;
- }
- }
- private void updateAnimationAtElapsed(float elapsed) {
- if (elapsed > mDuration)
- elapsed = mDuration;
- float delta = Math.abs(mStartSpeed) * elapsed - FRICTION * elapsed * elapsed / 2;
- if (mStartSpeed < 0)
- delta = -delta;
- mOffset = checkValid(mStartOffset + delta);
- requestRender();
- }
- @Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- mCache.clear();
- android.util.Log.i(TAG, "CREATE");
- mGLContext = gl;
- mVerticesBuffer = makeFloatBuffer(GVertices);
- mTexturesBuffer = makeFloatBuffer(GTextures);
- }
- @Override
- public void onSurfaceChanged(GL10 gl, int w, int h) {
- if (getAnimation() != null)
- return;
- android.util.Log.i(TAG, "CHANGED");
- mWidth = w;
- float imagew = w * 0.45f / SCALE / 2.0f;
- float imageh = h * 0.45f / SCALE / 2.0f;
- mTouchRect = new RectF(w / 2 - imagew, h / 2 - imageh, w / 2 + imagew, h / 2 + imageh);
- gl.glViewport(0, 0, w, h);
- float ratio = ((float) w) / h;
- gl.glMatrixMode(GL10.GL_PROJECTION);
- gl.glLoadIdentity();
- gl.glOrthof(-ratio * SCALE, ratio * SCALE, -1 * SCALE, 1 * SCALE, 1, 3);
- float[] vertices = new float[] {
- -ratio * SCALE, -SCALE, 0,
- ratio * SCALE, -SCALE, 0,
- -ratio * SCALE, SCALE, 0,
- ratio * SCALE, SCALE, 0
- };
- mBgVerticesBuffer = makeFloatBuffer(vertices);
- }
- public void setBackgroundTexture(int res) {
- mBgBitmap = BitmapFactory.decodeResource(getResources(), res);
- mInitBackground = true;
- }
- private void initBg() {
- mInitBackground = false;
- if (mBgBitmap != null) {
- int tmp = 1;
- int w = mBgBitmap.getWidth();
- int h = mBgBitmap.getHeight();
- while (w > tmp || h > tmp) {
- tmp <<= 1;
- }
- int width = tmp;
- int height = tmp;
- Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
- Canvas cv = new Canvas(bm);
- int left = (width - w) / 2;
- int top = (height - h) / 2;
- cv.drawBitmap(mBgBitmap, left, top, new Paint());
- GL10 gl = mGLContext;
- int[] tex = new int[1];
- gl.glGenTextures(1, tex, 0);
- mBgTexture = tex[0];
- gl.glBindTexture(GL10.GL_TEXTURE_2D, mBgTexture);
- GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bm, 0);
- bm.recycle();
- gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
- gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
- gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
- gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
- gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE);
- float[] textcoor = new float[] {
- (tmp - w) / 2.0f / tmp, (tmp - h) / 2.0f / tmp,
- (tmp + w) / 2.0f / tmp, (tmp - h) / 2.0f / tmp,
- (tmp - w) / 2.0f / tmp, (tmp + h) / 2.0f / tmp,
- (tmp + w) / 2.0f / tmp, (tmp + h) / 2.0f / tmp
- };
- mBgTexturesBuffer = makeFloatBuffer(textcoor);
- }
- }
- @Override
- public void onDrawFrame(GL10 gl) {
- //android.util.Log.i(TAG, "DRAW");
- gl.glMatrixMode(GL10.GL_MODELVIEW);
- gl.glLoadIdentity();
- GLU.gluLookAt(gl, 0, 0, 2, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
- gl.glDisable(GL10.GL_DEPTH_TEST);
- gl.glClearColor(0, 0, 0, 0);
- gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
- gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
- gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
- drawBg(gl);
- draw(gl);
- }
- public void drawBg(GL10 gl) {
- if (mBgBitmap != null) {
- if (mInitBackground)
- initBg();
- gl.glPushMatrix();
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mBgVerticesBuffer);
- gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mBgTexturesBuffer);
- gl.glEnable(GL10.GL_TEXTURE_2D);
- gl.glBindTexture(GL10.GL_TEXTURE_2D, mBgTexture); // bind texture
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
- gl.glPopMatrix();
- }
- }
- private void draw(GL10 gl) {
- mStopBackgroundThread = true;
- gl.glPushMatrix();
- gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVerticesBuffer); // vertices of square
- gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTexturesBuffer); // texture vertices
- gl.glEnable(GL10.GL_TEXTURE_2D);
- gl.glEnable(GL10.GL_BLEND);
- gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
- final float offset = mOffset;
- int i = 0;
- int max = mListener.getCount(this) - 1;
- int mid = (int) Math.floor(offset + 0.5);
- int iStartPos = mid - VISIBLE_TILES;
- if (iStartPos < 0)
- iStartPos = 0;
- // draw the left tiles
- for (i = iStartPos; i < mid; ++i) {
- drawTile(i, i - offset, gl);
- }
- // draw the right tiles
- int iEndPos = mid + VISIBLE_TILES;
- if (iEndPos > max)
- iEndPos = max;
- for (i = iEndPos; i >= mid; --i) {
- drawTile(i, i - offset, gl);
- }
- mListener.tileOnTop(this, (int) offset);
- gl.glPopMatrix();
- mStopBackgroundThread = false;
- preLoadCache(iStartPos - 3, iEndPos + 3);
- }
- private void drawTile(int position, float off, GL10 gl) {
- CoverFlowRecord fcr = getTileAtIndex(position, gl);
- if (mMatrix == null) {
- mMatrix = new float[16];
- mMatrix[15] = 1;
- mMatrix[10] = 1;
- mMatrix[5] = 1;
- mMatrix[0] = 1;
- }
- float trans = off * SPREAD_IMAGE;
- float f = off * FLANK_SPREAD;
- if (f > FLANK_SPREAD)
- f = FLANK_SPREAD;
- else if (f < -FLANK_SPREAD)
- f = -FLANK_SPREAD;
- mMatrix[3] = -f;
- mMatrix[0] = 1 - Math.abs(f);
- float sc = 0.45f * mMatrix[0];
- trans += f * 1;
- gl.glPushMatrix();
- gl.glBindTexture(GL10.GL_TEXTURE_2D, fcr.mTexture[0]); // bind texture
- // draw bitmap
- gl.glTranslatef(trans, 0, 0); // translate the picture to the right position
- gl.glScalef(sc, sc, 1.0f); // scale the picture
- gl.glMultMatrixf(mMatrix, 0); // rotate the picture
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
- // draw the reflection
- gl.glTranslatef(0, -2, 0);
- gl.glScalef(1, -1, 1);
- gl.glColor4f(0.5f, 0.5f, 0.5f, 0);
- gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
- gl.glColor4f(1, 1, 1, 1);
- gl.glPopMatrix();
- }
- private CoverFlowRecord getTileAtIndex(int position, GL10 gl) {
- synchronized(this) {
- CoverFlowRecord fcr = mCache.objectForKey(position);
- if (fcr == null) {
- Bitmap bm = mListener.getImage(this, position);
- if (bm == null)
- return null;
- int[] texture = imageToTexture(bm, gl);
- fcr = new CoverFlowRecord(texture, gl);
- mCache.putObjectForKey(position, fcr);
- }
- return fcr;
- }
- }
- private int[] imageToTexture(Bitmap bitmap, GL10 gl) {
- // generate texture
- int[] texture = new int[1];
- gl.glGenTextures(1, texture, 0);
- gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]);
- int width = bitmap.getWidth();
- int height = bitmap.getHeight();
- Bitmap bm = Bitmap.createBitmap(IMAGE_SIZE, IMAGE_SIZE, Bitmap.Config.ARGB_8888);
- Canvas cv = new Canvas(bm);
- if (width > IMAGE_SIZE || height > IMAGE_SIZE) {
- // scale the bitmap, make the width or height to the IMAGE_SIZE
- Rect src = new Rect(0, 0, width, height);
- float scale = 1.0f;
- if (width > height)
- scale = ((float) IMAGE_SIZE) / width;
- else
- scale = ((float) IMAGE_SIZE) / height;
- width = (int) (width * scale);
- height = (int) (height * scale);
- float left = (IMAGE_SIZE - width) / 2.0f;
- float top = (IMAGE_SIZE - height) / 2.0f;
- RectF dst = new RectF(left, top, left + width, top + height);
- cv.drawBitmap(bitmap, src, dst, new Paint());
- } else {
- float left = (IMAGE_SIZE - width) / 2.0f;
- float top = (IMAGE_SIZE - height) / 2.0f;
- cv.drawBitmap(bitmap, left, top, new Paint());
- }
- GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bm, 0); // draw the bitmap in the texture
- bm.recycle();
- bitmap.recycle();
- // some texture settings
- gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
- gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
- gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
- gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
- gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE);
- return texture;
- }
- // preload the cache from startindex(including) to endIndex(exclusive)
- // you just can preload the cache after the view has been attached to the window
- public void preLoadCache(final int startIndex, final int endIndex) {
- mStopBackgroundThread = false;
- if (mGLContext != null) {
- new Thread(new Runnable() {
- public void run() {
- int start = startIndex;
- if (start < 0)
- start = 0;
- int max = mListener.getCount(CoverFlowOpenGL.this);
- int end = endIndex > max ? max : endIndex;
- for (int i = start; i < end && !mStopBackgroundThread; ++i) {
- getTileAtIndex(i, mGLContext);
- }
- }
- }).run();
- }
- }
- private static FloatBuffer makeFloatBuffer(final float[] arr) {
- ByteBuffer bb = ByteBuffer.allocateDirect(arr.length * 4);
- bb.order(ByteOrder.nativeOrder());
- FloatBuffer fb = bb.asFloatBuffer();
- fb.put(arr);
- fb.position(0);
- return fb;
- }
- public static class CoverFlowRecord {
- private int[] mTexture;
- private GL10 gl;
- public CoverFlowRecord(int[] texture, GL10 gl) {
- mTexture = texture;
- this.gl = gl;
- }
- @Override
- protected void finalize() throws Throwable {
- if (mTexture != null) {
- gl.glDeleteTextures(1, mTexture, 0);
- }
- super.finalize();
- }
- }
- public static interface CoverFlowListener {
- public int getCount(CoverFlowOpenGL view); // Number of images to display
- public Bitmap getImage(CoverFlowOpenGL anotherCoverFlow, int position); // Image at position
- public void tileOnTop(CoverFlowOpenGL view, int position); // Notify what tile is on top after scroll or start
- public void topTileClicked(CoverFlowOpenGL view, int position);
- }
- }
当中用了一个类进行数据的cache,挺简单的。
- package com.locatify.fengji.coverflow;
- import java.util.LinkedHashMap;
- import java.util.concurrent.locks.ReadWriteLock;
- import java.util.concurrent.locks.ReentrantReadWriteLock;
- public class DataCache<K, E> {
- private int mCapacity;
- private LinkedHashMap<K, E> mCache;
- private final ReadWriteLock mReadWriteLock = new ReentrantReadWriteLock();
- public DataCache(int capacity) {
- mCapacity = capacity;
- mCache = new LinkedHashMap<K, E>(mCapacity) {
- private static final long serialVersionUID = -9165777183357349715L;
- @Override
- protected boolean removeEldestEntry(java.util.Map.Entry<K, E> eldest) {
- return size() > mCapacity;
- }
- };
- }
- public E objectForKey(K key) {
- mReadWriteLock.readLock().lock();
- final E result = mCache.get(key);
- mReadWriteLock.readLock().unlock();
- return result;
- }
- public void putObjectForKey(final K key, final E value) {
- if (key != null && value != null) {
- mReadWriteLock.writeLock().lock();
- mCache.put(key, value);
- mReadWriteLock.writeLock().unlock();
- }
- }
- public boolean containsKey(final K key) {
- mReadWriteLock.readLock().lock();
- final boolean result = mCache.containsKey(key);
- mReadWriteLock.readLock().unlock();
- return result;
- }
- public void clear() {
- mReadWriteLock.writeLock().lock();
- mCache.clear();
- mReadWriteLock.writeLock().unlock();
- }
- }
恩,大概就是这样子,第一次写日志,代码里有一些注释,如果还有什么问题可以留言。