单个控件比如ImageView,imageButton等产生倒影效果比较容易,但是复杂的控件比如gridview,scrollview等控件就比较麻烦。
自定义一个倒影容器类,将要生成倒影的控件放入该容器类,就可以实现复杂控件的倒影效果。下面给出倒影容器类。
package com.android.util;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.PorterDuff.Mode;
import android.graphics.Shader;
import android.graphics.Shader.TileMode;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.widget.FrameLayout;
public class ReflectContainer extends FrameLayout{
private final static String TAG = "ReflectContainer";
private static final boolean DEBUG_DRAWING_TIME = true;
private static final float REFLECTION_SIZE = 2.0f;
/* Drawing tools used to create the reflection pool effect. */
private final Paint mDarkPaint = new Paint();
private final Paint mReflectionPaint = new Paint();
private final Shader mShader;
private Matrix mMatrix = new Matrix();
public ReflectContainer(Context context) {
this(context, null);
}
public ReflectContainer(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ReflectContainer(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, 0xa0ffffff, 0x00000000, TileMode.MIRROR);
mReflectionPaint.setShader(mShader);
mReflectionPaint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
mMatrix.preScale(1, -1);
}
@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);
child.setDrawingCacheEnabled(true);
child.buildDrawingCache();
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();
// /* 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();
}
public void updateRefect(){
Log.i(TAG, "updateRefect() ");
postInvalidate();
}
}
使用方法:将需要倒影的内容放入一个容器中,在将该容器放入倒影容器ReflectContainer中,可参考下面的布局文件。
<com.android.util.ReflectContainer
android:id="@+id/shower_status_reflect_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="371dp"
android:layout_height="wrap_content"
android:background="@drawable/home_background_middle"
android:orientation="vertical" >
<LinearLayout
android:id="@+id/status_sub_panel0"
android:layout_width="match_parent"
android:layout_height="71dp"
android:gravity="center_vertical"
android:orientation="horizontal" >
<ImageView
android:layout_width="118dp"
android:layout_height="50dp"
android:layout_marginLeft="26dp"
android:layout_marginRight="26dp"
android:layout_marginTop="10dp"
android:scaleType="fitXY"
android:src="@drawable/logo_crowneplaza" />
<com.android.util.SlipButton
android:id="@+id/language_switch_cn_en_inshower"
android:layout_width="72dp"
android:layout_height="41dp"
android:layout_marginLeft="66dp"
android:layout_marginTop="15dp" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginLeft="5dp"
android:layout_marginRight="5dp"
android:layout_marginTop="10dp"
android:scaleType="centerInside"
android:src="@drawable/horizontal_divider" />
<FrameLayout
android:id="@+id/shower_module_directions"
android:layout_width="371dp"
android:layout_height="352dp"
android:layout_marginLeft="10dp" >
<ImageView
android:id="@+id/shower_module_legend"
android:layout_width="150dp"
android:layout_height="170dp"
android:layout_marginLeft="10dp"
android:layout_marginTop="40dp"
android:scaleType="fitXY" />
<TextView
android:id="@+id/shower_caption"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="165dp"
android:layout_marginTop="85dp"
android:text="@string/shower_style"
android:textColor="@android:color/white"
android:textSize="24sp" />
<ScrollView
android:id="@+id/shower_explanation"
android:layout_width="190dp"
android:layout_height="164dp"
android:layout_marginLeft="165dp"
android:layout_marginTop="122dp"
android:scrollbarStyle="outsideOverlay" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/shower_explanation_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shower_exp"
android:textColor="@android:color/white"
android:textSize="20sp" />
<ImageView
android:id="@+id/shower_headView"
android:layout_width="194dp"
android:layout_height="194dp"
android:scaleType="fitXY"
android:src="@drawable/background" />
</LinearLayout>
</ScrollView>
</FrameLayout>
</LinearLayout>
</com.android.util.ReflectContainer>
当倒影容器中的内容发生改变时,重画容器即可。
ReflectContainer mReflectContainer;
mReflectContainer = (ReflectContainer)findViewById(R.id.shower_status_reflect_container);
//内容发生变化
mReflectContainer.postInvalidate();