制造渐变的倒影效果
英文原文: http://andraskindler.com/blog/2014/creating-view-reflections/

虽然拟物化的设计的时代已经过去,但你不应该忘记在那个时代学到的所有东西。其中一种效果就是倒影,一种可好可坏的东西-过度使用可能会让你的UI看起来臃肿,但有时候它又是改善用户体验的良药。这篇文章将演示如何为任意的View制造一个渐变的倒影效果。
整个过程可以分为三大步:
1.建立一个你想要将之倒影的界面(就是一个view),但是不要把它添加到根view中。
2.布局与渲染这个View,以便获得这个View的bitmap。
3.为这个bitmap添加倒影,并把它绘制在屏幕上。
构建UI
第一步非常简单。和平常一样只是记住不要把根view添加到你的布局层级中。示例中使用的是一个红底白字的TextView,但是这种效果并不是局限于一个单独的View,还可以是更复杂的布局(ViewGroup)
1 final TextView textView = new TextView(this); 2 textView.setText("test"); 3 textView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); 4 textView.setTextColor(Color.WHITE); 5 textView.setTextSize(40); 6 textView.setBackgroundColor(Color.RED); 7 textView.setPadding(100, 20, 100, 20);
布局与渲染
布局过程包括两步:测量view,布局view。测量是通过measure() 方法完成,该方法负责决定view与其子view的尺寸需求。调用measure() 之后,getMeasuredWidth() 和getMeasuredHeight() 方法便能返回正确的值,这个值将在layout()方法中使用。layout()方法可以强制所有的view根据尺寸参数放置子view的位置。
1 final int measureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); 2 textView.measure(measureSpec, measureSpec); 3 textView.layout(0, 0, textView.getMeasuredWidth(), textView.getMeasuredHeight());
下一步,使用draw()方法捕获view的bitmap ,该方法将它渲染到画布上。
1 final Bitmap b = Bitmap.createBitmap(textView.getWidth(), textView.getHeight(), Bitmap.Config.ARGB_8888); 2 final Canvas c = new Canvas(b); 3 textView.draw(c);
制造倒影
产生倒影可以分为四个步骤,下图展示了每一步可以得到的效果:

你需要决定倒影的大小(用原view的百分比来衡量也许不错),中间间隙的高度,这两个都应该以像素(px)为单位。你还需要一个新的bitmap(和原bitmap大小相同,包括了倒影和间隙),以及它的Canvas。
1 final int reflectionHeight = original.getHeight() * percentage; 2 Bitmap bitmapWithReflection = Bitmap.createBitmap(original.getWidth(), (original.getHeight() + reflectionHeight + gap), Bitmap.Config.ARGB_8888);
首先,绘制原始的bitmap:
1 canvas.drawBitmap(original, 0, 0, null);
然后添加一个间隙,这个间隙在这里用透明的画笔来代替:
1 final Paint transparentPaint = new Paint(); 2 transparentPaint.setARGB(0, 255, 255, 255); 3 canvas.drawRect(0, original.getHeight(), original.getWidth(), original.getHeight() + gap, transparentPaint);
下一步:倒影,我们将使用一个修改了的matrix 来让图像沿x轴翻转,然后将之绘制在画布中:
1 final Matrix matrix = new Matrix(); 2 matrix.preScale(1, -1); 3 canvas.drawBitmap(Bitmap.createBitmap(original, 0, original.getHeight() - reflectionHeight, 4 original.getWidth(), reflectionHeight, matrix, false), 0, original.getHeight() + gap, null);
最后一点很重要,使用合适的 LinearGradient为倒影添加一个渐变效果:
1 final Paint fadePaint = new Paint(); 2 fadePaint.setShader(new LinearGradient(0, original.getHeight(), 0, original.getHeight() + reflectionHeight + gap, 0x70ffffff, 0x00ffffff, Shader.TileMode.CLAMP)); 3 fadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 4 canvas.drawRect(0, original.getHeight(), original.getWidth(), bitmapWithReflection.getHeight() + gap, fadePaint);
好了,现在你就能得到一个显示一个view的渐变倒影效果的bitmap了,你可以将这个bitmap显示在ImageView 中,或者作为view的背景,或者在View子类的onDraw() 方法中使用。
被忘了调用original 的recycle() 避免内存管理的问题。
完整的代码在这里at this Gist。
可能打不开,我直接复制下来吧:
1 public class SixActivity extends Activity { 2 3 private Button btn; 4 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 final FrameLayout root = new FrameLayout(this); 9 setContentView(root); 10 final TextView textView = new TextView(this); 11 textView.setText("test"); 12 textView.setLayoutParams(new FrameLayout.LayoutParams( 13 ViewGroup.LayoutParams.WRAP_CONTENT, 14 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); 15 textView.setTextColor(Color.WHITE); 16 textView.setGravity(Gravity.CENTER_HORIZONTAL); 17 textView.setBackgroundColor(Color.RED); 18 textView.setPadding(100, 20, 100, 20); 19 20 final int measureSpec = View.MeasureSpec.makeMeasureSpec(0, 21 MeasureSpec.UNSPECIFIED); 22 textView.measure(measureSpec, measureSpec); 23 System.out.println("textView->" + textView.getMeasuredWidth()); 24 textView.layout(0, 0, textView.getMeasuredWidth(), 25 textView.getMeasuredHeight()); 26 final Bitmap b = Bitmap.createBitmap(textView.getWidth(), 27 textView.getHeight(), Bitmap.Config.ARGB_8888); 28 final Canvas c = new Canvas(b); 29 textView.draw(c); 30 final ImageView imageView = new ImageView(this); 31 imageView.setLayoutParams(new FrameLayout.LayoutParams( 32 ViewGroup.LayoutParams.WRAP_CONTENT, 33 ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.CENTER)); 34 imageView.setImageBitmap(creatReflection(b, 1, 10)); 35 root.addView(imageView); 36 } 37 38 private Bitmap creatReflection(Bitmap original, float percentage, int gap) { 39 final int reflectionHeight = (int) (original.getHeight() * percentage); 40 Bitmap bitmapWithReflection = Bitmap.createBitmap(original.getWidth(), 41 (original.getHeight() + reflectionHeight + gap), 42 Bitmap.Config.ARGB_8888); 43 Canvas canvas = new Canvas(bitmapWithReflection); 44 45 // original image 46 canvas.drawBitmap(original, 0, 0, null); 47 // gap drawing 48 final Paint transparentPaint = new Paint(); 49 transparentPaint.setARGB(0, 255, 255, 255); 50 canvas.drawRect(0, original.getHeight(), original.getWidth(), 51 original.getHeight() + gap, transparentPaint); 52 // reflection 53 final Matrix matrix = new Matrix(); 54 matrix.preScale(1, -1); 55 canvas.drawBitmap(Bitmap.createBitmap(original, 0, original.getHeight() 56 - reflectionHeight, original.getWidth(), reflectionHeight, 57 matrix, false), 0, original.getHeight() + gap, null); 58 // reflection shading 59 final Paint fadePaint = new Paint(); 60 fadePaint.setShader(new LinearGradient(0, original.getHeight(), 0, 61 original.getHeight() + reflectionHeight + gap, 0x70ffffff, 62 0x00ffffff, Shader.TileMode.CLAMP)); 63 fadePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); 64 canvas.drawRect(0, original.getHeight(), original.getWidth(), 65 bitmapWithReflection.getHeight() + gap, fadePaint); 66 67 original.recycle(); 68 return bitmapWithReflection; 69 70 } 71 72 }

浙公网安备 33010602011771号