制造渐变的倒影效果

英文原文: http://andraskindler.com/blog/2014/creating-view-reflections/

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 }
posted @ 2015-05-04 22:52  perfect亮  阅读(360)  评论(0)    收藏  举报