Android OpenGL Gallery Cover Flow

1 篇文章 0 订阅
1 篇文章 0 订阅

因为要写一个和iPhone上的CoverFlow差不多的东西,但是在网上找了半天都没有找到,所以只能自己写拉。

 

有两种,一种是直接继承Gallery的,通过override Gallery的getChildStaticTransformation方法实现,这个网上有,速度还不错,最开始采用的这个方案。

后来有时间,又觉得opengl可能会更快一些,所以照着iOS上很著名的FlowCover写了一个。两个的代码都贴出来吧,仅供参考。

第二个例子在https://github.com/jackfengji/glcoverflow上面有整个的完整工程。

 

第一种:

继承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进行速度的计算等。

import android.content.Context;
import android.graphics.*;
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;
import DataCache;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

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;
        requestRender();
	}
	
	@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;
			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();

	    mGLContext = gl;
	    mVerticesBuffer = makeFloatBuffer(GVertices);
	    mTexturesBuffer = makeFloatBuffer(GTextures);
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int w, int h) {
		if (getAnimation() != null)
			return;
		
		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) {
		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.38f * 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(1f, 1f, 1f, 0.5f);
		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);
        gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
		
		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);
	}
}

给一个使用的例子

    	mGalleryLandscape = new CoverFlowOpenGL(this);
        // set the cover flow listener
    	mGalleryLandscape.setCoverFlowListener(new MyCoverFlowListener());
        // set the selected image location which starts from 0
    	mGalleryLandscape.setSelection(mActiveLocation);
        // set the background drawable
    	mGalleryLandscape.setBackgroundTexture(R.drawable.bg_tour);


其中MyCoverFlowListener是实现了CoverFlowListener的一个类

    public static class MyCoverFlowListener implements CoverFlowOpenGL.CoverFlowListener {
        private static int[] IMAGES = new int[] {R.drawable.image1, R.drawable.image2, R.drawable.image3};
        private Context mContext;
        
        public MyCoverFlowListener(Context context) {
            mContext = context;
        }
        
        @Override
        public void tileOnTop(CoverFlowOpenGL view, int position) {
        }
        
        @Override
        public Bitmap getImage(CoverFlowOpenGL view, int position) {
            return BitmapFactory.decodeResource(mContext.getResources(), IMAGES[position]);
        }

        @Override
        public int getCount(CoverFlowOpenGL view) {
            return IMAGES.length;
        }
        
        @Override
        public void topTileClicked(CoverFlowOpenGL view, int position) {
        }
    }



 

当中用了一个类进行数据的cache,挺简单的。

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

 

恩,大概就是这样子,第一次写日志,代码里有一些注释,如果还有什么问题可以留言。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 26
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 26
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值