/** * Custom layout to display "full-screen" cover artwork with a * reflection effect. This layout should contain only one view, which * will be placed over the bottom refection. */ public class ReflectionLayout extends ViewGroup { private static final String TAG = "ReflectionLayout"; private Paint mReflect = new Paint(Paint.FILTER_BITMAP_FLAG); private Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG); private Bitmap mBitmap; private Rect mImageDim = new Rect(); private Rect mReflectionClip = new Rect(); private Rect mReflectionDim = new Rect(); public ReflectionLayout(Context context) { super(context); init(); } public ReflectionLayout(Context context, AttributeSet attrs, Map inflateParams) { super(context, attrs, inflateParams); init(); } public ReflectionLayout(Context context, AttributeSet attrs, Map inflateParams, int defStyle) { super(context, attrs, inflateParams, defStyle); init(); } private void init() { // mReflect.setXfermode(new PorterDuff(PorterDuff.Mode.DST_OUT)); } public void setImageURI(Uri uri) { try { mBitmap = BitmapFactory.decodeStream (mContext.getContentResolver().openInputStream(uri)); } catch (Exception e) { Log.d(TAG, "Unable to read content: " + uri); } // configureBounds(); } public void setImageResource(int id) { mBitmap = BitmapFactory.decodeResource(getResources(), id); // configureBounds(); } private void configureBounds() { Log.d(TAG, "configureBounds()"); /* Not ready yet... */ if (getChildCount() == 0) return; int w = getMeasuredWidth(); int h = getMeasuredHeight(); if (w == 0 || h == 0) { Log.d(TAG, "...abort!"); return; } View v = getChildAt(0); int vh = v.getMeasuredHeight(); int bw = mBitmap.getWidth(); int bh = mBitmap.getHeight(); /* TODO: Keep aspect ratio. */ assert bw == bh; mImageDim.set(0, 0, w, h - vh); float portion = (float)vh / (float)h; Log.d(TAG, "bh="+ bh + ", portion=" + portion); mReflectionClip.set(0, bh - (int)((float)bh * portion), bw, bh); mReflectionDim.set(0, 0, w, vh); LinearGradient shader = new LinearGradient(0, 0, 0, 10, 0xffff0000, 0xff00ff00, TileMode.MIRROR); mReflect.setShader(shader); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { Log.d(TAG, "onLayout"); assert getChildCount() == 1; configureBounds(); int selfw = getMeasuredWidth(); int selfh = getMeasuredHeight(); View v = getChildAt(0); int vh = v.getMeasuredHeight(); Log.d(TAG, "selfw=" + selfw + ", selfh=" + selfh); Log.d(TAG, "vh=" + vh); v.layout(0, selfh - vh, selfw, selfh); } @Override protected void onMeasure(int widthSpec, int heightSpec) { Log.d(TAG, "onMeasure..."); assert getChildCount() == 1; View v = getChildAt(0); v.measure (getChildMeasureSpec(widthSpec, 0, v.getLayoutParams().width), getChildMeasureSpec(heightSpec, 0, v.getLayoutParams().height)); /* XXX: Our layout doesn't play nice with any other usage than * the one we have in playlist.xml :) */ assert View.MeasureSpec.getMode(widthSpec) == View.MeasureSpec.EXACTLY; assert View.MeasureSpec.getMode(heightSpec) == View.MeasureSpec.EXACTLY; setMeasuredDimension (View.MeasureSpec.getSize(widthSpec), View.MeasureSpec.getSize(heightSpec)); } @Override protected void dispatchDraw(Canvas canvas) { assert getChildCount() == 1; Rect r = new Rect(); getDrawingRect(r); Log.d(TAG, "r=" + r); Log.d(TAG, "clip=" + mReflectionClip); Log.d(TAG, "dim=" + mReflectionDim); canvas.drawBitmap(mBitmap, null, mImageDim, mPaint); canvas.save(); canvas.translate(0, mImageDim.height() + mReflectionDim.height()); canvas.scale(1.0f, -1.0f); canvas.drawBitmap(mBitmap, mReflectionClip, mReflectionDim, mReflect); canvas.restore(); super.dispatchDraw(canvas); } }