Xamarin Android 仿制个性动画菜单


Xamarin Android 仿制个性动画菜单

xamarin.android


唱衰 .NET / Xamarin 的, 看完图示就可以关掉了.

不为证明什么, 仅为示例, 不做实际用途, 所以然并卵.

照例,上图

062204037686442.gif-414.4kB

上代码: https://github.com/gruan01/Xamarin-Example/tree/master/DiscMenu

此示例 防照: http://blog.csdn.net/lmj623565791/article/details/43131133

不过没有按照他样那做, 因为那也是然并卵. 谁会把 App 做的如此花梢?

本示例主要 实践 SurfaceView , 及 Animator 的用法.

如图所示, 中心那个滚动的圆形头像, 是用 Surface 做的, 虽然用 Animation 一样可以做.

弹出的子项是用 Animator 做的.

SurfaceView

SurfaceView 不受 UI 线程干扰, 所以它的 OnDraw 方法实现了也是白实现.

它需要自带一个独立的线程去更新视图内容.

要绘制, 当然需要一个 Canvas , 它可以这样获取:

  1. using (var canvas = this.Holder.LockCanvas()) {
  2. 2 if (canvas != null) {
  3. 3 this.DrawBitmap(canvas, this.Center);
  4. 4 this.DrawRing(canvas);
  5. 5 }
  6. 6 this.Holder.UnlockCanvasAndPost(canvas);
  7. 7 }

不过, 你最好加上 try..catch..

当 Resume 的时候, 这里会出点小异常.

LockCanvas() 方法返回绘制所需的 Canvas,

UnlockCanvasAndPost() 是把绘制的内容提交, 并释放 canvas.

另外, 必须实现 ISurfaceHolderCallback 接口, 并注册

  1. public class CenterView : SurfaceView, ISurfaceHolderCallback, IRunnable {
  2. 2 ...
  3. 3 public CenterView(Context ctx, Bitmap center, int radius) : base(ctx) {
  4. 4 ...
  5. 5 this.Holder.AddCallback(this);

在 ISurfaceHolderCallback 的方法 SurfaceCreated 中开启自己的线程, 无限循环的进行绘制

  1. public void SurfaceCreated(ISurfaceHolder holder) {
  2. 2 this.IsRunning = true;
  3. 3 this.Thread = new Java.Lang.Thread(this);
  4. 4 this.Thread.Start();
  5. 5 }

在 VS 的 Android 模拟器下, 如果不设置:

  1. this.Holder.SetFormat(Format.Transparent);

这个 SurfaceView 的内容就不会显示, 但是一切都是正常执行的, 在 Android SDK 自带的模拟器下就没有问题.

网易新闻客户端 中的 视频, 在 VS 的模拟器上也不显示画面, 我猜也是因为这个问题.

Surface 就这么简单, 余下的, 就靠自己脑洞大开了.

Canvas 相关技巧

1, 旋转

如上图所示的那个不断旋转的头像, 其实是先旋转 canvas , 然后在绘制

  1. private void DrawBitmap(Canvas c, Bitmap bmp) {
  2. var sc = c.Save();
  3. this.Degree = this.Degree % 360 + DEGREE_STEP;
  4. //要先旋转,后画图才有效果
  5. c.Rotate(this.Degree, c.Width / 2, c.Height / 2);
  6. ...
  7. c.DrawBitmap(bmp, null, rect, paint);

顺序反了是不会有效果的.

2, 镂空

如图所示的和头像反着转的那个绿色线条, 其实是对大的圆形做角度渐变, 然后用小一点的圆形进行镂空, 当然画布也是旋转了的.

  1. //用两个圆 Path 构造一个 圆环显示区域, 即挖空内圆
  2. 2 var pInner = new Path();
  3. 3 pInner.AddCircle(cx, cy, this.BitmapRadius + SPACE / 2, Path.Direction.Cw);
  4. 4 var pOut = new Path();
  5. 5 pOut.AddCircle(cx, cy, this.Radius, Path.Direction.Cw);
  6. 6
  7. 7 c.ClipPath(pOut);
  8. 8 c.ClipPath(pInner, Region.Op.Difference);
  9. 9
  10. 10 //var color = new Color((int)(DateTime.Now.Ticks % 0xFFFFFFFF));
  11. 11 //c.DrawColor(color);
  12. 12
  13. 13 //用角度渐变填充外圆的范围
  14. 14 var g = new SweepGradient(cx, cy, Color.Green, Color.Transparent);
  15. 15 var paint = new Paint();
  16. 16 paint.SetShader(g);
  17. 17 c.DrawCircle(cx, cy, this.Radius, paint);

c 是 canvas,

c.ClipPath(pOut), 即限制 canvas 只绘制为 pOut 这个 path 的闭合区域.

c.ClipPath(pInner, Region.Op.Difference) 即把 pInner 的 闭合区域 从 上一句 的 绘制区域 中排除.

顺序不要搞反, 反了没有效果.

3, 补充: 正反两个方向

一个方向转动没意思, 两个方向是怎么做的呢? 很简单, 绘制头向的时候按正角度(x) , 绘制头向外面那一圈是用的 -x

Animator

SurfaceView 不好做交互控制, 所以弹出子项的动画是用 Animator 做的.

Android 的 Animation 分为好多种, 我不了解, 现学现卖而已.

这里主要是实现了 子项 位置/大小 的变化, 所以 我用了 ObjectAnimator

  1. var aniX = ObjectAnimator.OfFloat(c, "X", xs);
  2. var aniY = ObjectAnimator.OfFloat(c, "Y", ys);
  3. var aniSX = ObjectAnimator.OfFloat(c, "ScaleX", ss);
  4. var aniSY = ObjectAnimator.OfFloat(c, "ScaleY", ss);

其中的 c 即是子项 view,

X, Y, ScaleX, ScaleY 你可以理解为 .NET 的反射, 它要用反射去更新这些属性的值.

展开与收回

是两个向反的过程, 即把起始值与结束值互换一下就可以了

  1. float[] xs = new float[] { cx - hw, l };
  2. float[] ys = new float[] { cy - hy, t };
  3. float[] ss = new float[] { 0.1f, 1 };
  4. if (!expand) {
  5. Array.Reverse(xs);
  6. Array.Reverse(ys);
  7. Array.Reverse(ss);
  8. }

一开始, 我想把这些 Animator 保存起来, 当收起的时候, 就把各个 animator.Reverse() 一下, 这样就可以得到向反的动画. 但是总是收回不到中心点, 所以, 我放弃了这种想法.

跳蛋模式

我自己给它取的名字, 其实是 Interpolator

  1. set.SetInterpolator(new Android.Views.Animations.BounceInterpolator());

试了几个 Interpolator , 就这个跳蛋模式能在展开的时候, 把最右边的的那个子项展开到指定的点上.

圆形排列

我数学早还给老师了, 这里用的到三角函数, 勾股定理 都是百度来的, 所以表问我.


完, 谢谢观赏, 请点..........

作者:xling
原文地址:http://www.cnblogs.com/xling/p/4709477.html

分享到