最近做倒影特效,网上找了很多资料,都是通过View的getDrawingCache或去Bitmap然后再画上去,我用的时候问题多多啊。UI初始化未完成时,怎么改写获取DrawingCache都报空异常啊。哎哟我的天,网上罗列了一大堆的传说保证能用的神秘解决方案,我都试了,一样的问题。我同时做了一个延时处理,也就是初始化等个几秒再获取DrawingCache去画倒影。思来想去,还是觉得不爽。潜心研究,搞了如下一个ReflectionLayout控件处理倒影,代码如下,有需要的可以试试,包你爽到底。
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Shader;
import android.graphics.PorterDuff.Mode;
import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
/**
* Generic layout which creates a glass reflection look. Measuring has many
* side-effects and works best with a single ImageView child but should be
* flexible enough to display any other type of view.
*
* @author <a href="mailto:Qian.Keane@gmail.com">keane</a>
* @version 1.0
*/
public class ReflectionLayout extends FrameLayout {
private static final String TAG = "ReflectionLayout";
private static final boolean DEBUG_DRAWING_TIME = false;
/**
* Desired reflection layout size (including the child). This should be
* configurable at runtime somehow. It may be smaller than this depending on
* layout constraints.
*/
private static final float REFLECTION_SIZE = 2.20f;
/* Drawing tools used to create the reflection pool effect. */
private final Paint mDarkPaint = new Paint();
private final Paint mReflectionPaint = new Paint();
private final Matrix mMatrix = new Matrix();
private final Shader mShader;
public ReflectionLayout(Context context) {
this(context, null);
}
public ReflectionLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ReflectionLayout(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setWillNotDraw(false);
mDarkPaint.setColor(0x98000000);
mShader = new LinearGradient(0, 0, 0, 1, 0x70ffffff, 0x00ffffff, TileMode.MIRROR);
// mShader = new LinearGradient(0, 0, 0, 1, 0x70ffffff, 0x00ffffff, TileMode.MIRROR);
mReflectionPaint.setShader(mShader);
// mReflectionPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
mReflectionPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
}
@Override
protected void onMeasure(int wspec, int hspec) {
super.onMeasure(wspec, hspec);
if (getChildCount() > 0) {
View child = getChildAt(0);
int childw = child.getMeasuredWidth();
int childh = child.getMeasuredHeight();
/* Enlarge the child's height by 33% for the reflection. */
setMeasuredDimension(resolveSize(childw, wspec), resolveSize((int) (childh * REFLECTION_SIZE), hspec));
}
}
@Override
protected void onDraw(Canvas canvas) {
long now;
if (DEBUG_DRAWING_TIME) {
now = System.currentTimeMillis();
}
/* Magic magic magic... */
if (getChildCount() > 0) {
drawReflection(canvas);
}
if (DEBUG_DRAWING_TIME) {
long elapsed = System.currentTimeMillis() - now;
Log.d(TAG, "Drawing took " + elapsed + " ms");
}
}
private void drawReflection(Canvas canvas) {
View child = getChildAt(0);
int childw = child.getWidth();
int childh = child.getHeight();
int selfh = getHeight();
int poolh = selfh - childh;
/*
* Save a layer so that we can render off screen initially in order to
* achieve the DST_OUT xfer mode. This allows us to have a non-solid
* background.
*/
canvas.saveLayer(child.getLeft(), child.getBottom(), child.getRight(), child.getBottom() + getBottom(), null, Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);
/* Draw the flipped child. */
canvas.save();
canvas.scale(1, -1);
canvas.translate(0, -(childh * 2));
child.draw(canvas);
canvas.restore();
/* Saturate the flipped image with a dark color. */
canvas.drawRect(0, childh, childw, selfh, mDarkPaint);
/* Carve out the reflection area's alpha channel. */
mMatrix.setScale(1, poolh);
mMatrix.postTranslate(0, childh);
mShader.setLocalMatrix(mMatrix);
canvas.drawRect(0, childh, childw, selfh, mReflectionPaint);
/* Apply the canvas layer. */
canvas.restore();
}
}