Android仿新版微信浮窗效果

阅读公众号或其他文章,经常需要暂时退出文章.

在新版微信中,可以把浏览的文章缩小为浮窗.点击浮窗继续阅读.对于经常在微信里阅读的人来说,这简直就是人类之光.

微信效果如下

微信效果

对于这功能我进行了仿写.

效果如下

仿写效果

微信的大佬一定用了了不起的技术,我这里只是实现效果.

简单写了一个库,一句代码即可实现效果

github.com/SherlockQi/…

//在AppDelegate中将类名传入即可
[HKFloatManager addFloatVcs:@[@"HKSecondViewController"]];

使用到的技术点

监听侧滑返回

//设置边缘侧滑代理
self.navigationController.interactivePopGestureRecognizer.delegate = self;

//当开始侧滑pop时调用此方法
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer{
 /* 判断是否开启边缘侧滑返回 **/
 if (self.navigationController.viewControllers.count > 1) {
   [self beginScreenEdgePanBack:gestureRecognizer];
  return YES;
 }
 return NO;
}
/* UIScreenEdgePanGestureRecognizer
@property(nullable, nonatomic, readonly) UIGestureRecognizer *interactivePopGestureRecognizer NS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
/*! This subclass of UIPanGestureRecognizer only recognizes if the user slides their finger
 in from the bezel on the specified edge. */
//NS_CLASS_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED @interface UIScreenEdgePanGestureRecognizer : UIPanGestureRecognizer
**/
//利用CADisplayLink 来实现监听返回手势
- (void)beginScreenEdgePanBack:(UIGestureRecognizer *)gestureRecognizer{
   /*
   * 引用 gestureRecognizer
   * 开启 CADisplayLink
   * 显示右下视图
   **/
 self.edgePan = (UIScreenEdgePanGestureRecognizer *)gestureRecognizer;
 _link = [CADisplayLink displayLinkWithTarget:self selector:@selector(panBack:)];
 [self.link addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
 [[UIApplication sharedApplication].keyWindow addSubview:self.floatArea];
}
//此方法中进行操作
- (void)panBack:(CADisplayLink *)link {
 //判断手势状态
 if (self.edgePan.state == UIGestureRecognizerStateChanged) {//移动过程
  /*
  * 改变右下视图 frame
  * 判断手指是否进入右下视图中
  **/
 //手指在屏幕上的位置
 CGPoint tPoint = [self.edgePan translationInView:kWindow];
  ...根据tPoint设置右下视图 frame...
 //手指在右下视图上的位置(若 x>0 && y>0 说明此时手指在右下视图上)
 CGPoint touchPoint = [kWindow convertPoint:[self.edgePan locationInView:kWindow] toView:self.floatArea];
 if (touchPoint.x > 0 && touchPoint.y > 0) {
    ...
    //由于右下视图是1/4圆 所以需要这步判断
    if (pow((kFloatAreaR - touchPoint.x), 2) + pow((kFloatAreaR - touchPoint.y), 2) <= pow((kFloatAreaR), 2)) {
     self.showFloatBall = YES;
    }
    ...
 }else if (self.edgePan.state == UIGestureRecognizerStatePossible) {
  /*
  * 停止CADisplayLink
  * 隐藏右下视图
  * 显示/隐藏浮窗
  **/
  [self.link invalidate];
  if (self.showFloatBall) {
    self.floatBall.iconImageView.image= [self.floatViewController valueForKey:@"hk_iconImage"];
    [kWindow addSubview:self.floatBall];
   }
 }
}

监听浮窗移动/点击

#import "HKFloatBall.h" 类为浮窗视图类
//点击浮窗后让代理push之前保留起来的控制器
- (void)tap:(UIGestureRecognizer *)tap{
 if ([self.delegate respondsToSelector:@selector(floatBallDidClick:)]) {
  [self.delegate floatBallDidClick:self];
  }
}
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event{
 ...结束监听...
}
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event{
 ...结束监听...
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
 /*
 * 改变浮窗 frame
 * 改变右下视图 frame
 * 判断浮窗center 是否在右下视图之上
 **/
 CGPoint center_ball = [kWindow convertPoint:self.floatBall.center toView:self.cancelFloatArea];
 if (pow((kFloatAreaR - center_ball.x), 2) + pow((kFloatAreaR - center_ball.y), 2) <= pow((kFloatAreaR), 2)) {
  if (!self.cancelFloatArea.highlight) {
   self.cancelFloatArea.highlight = YES;
  }
 }
}
}

自定义push/pop动画

//设置navigationController代理
 self.navigationController.delegate = self;
#pragma UINavigationControllerDelegate
//push/pop 时会调用此代理方法
- (nullable id )navigationController:(UINavigationController *)navigationController
           animationControllerForOperation:(UINavigationControllerOperation)operation
               fromViewController:(UIViewController *)fromVC
               toViewController:(UIViewController *)toVC{
 ... 判断是否执行动画 若 return nil 则执行原始 push/pop 动画...
 //HKTransitionPush HKTransitionPop 是自己写的两个动画类,需要实现 if(operation==UINavigationControllerOperationPush) {
  return [[HKTransitionPush alloc]init];
 } else if(operation==UINavigationControllerOperationPop){
  return [[HKTransitionPop alloc]init];
 }
}
HKTransitionPush HKTransitionPop 代码类似已HKTransitionPush为例
#import "HKTransitionPush.h"
-(NSTimeInterval)transitionDuration:(id)transitionContext{
 return kAuration;//动画时间
}
- (void)animateTransition:(id)transitionContext {
 //获取上下文
 self.transitionContext = transitionContext;
 UIViewController * fromVC = [transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey];
 UIViewController *toVC = [transitionContext viewControllerForKey:UITransitionContextToViewControllerKey];
 UIView *contView = [transitionContext containerView];
 [contView addSubview:fromVC.view];
 [contView addSubview:toVC.view];
 //添加遮罩视图
 [fromVC.view addSubview:self.coverView];
 //浮窗的 frame push时这个是起始 frame ,pop时是结束时的 frame
 CGRect floatBallRect = [HKFloatManager shared].floatBall.frame;
 //开始/结束时的曲线
 UIBezierPath *maskStartBP = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(floatBallRect.origin.x, floatBallRect.origin.y,floatBallRect.size.width , floatBallRect.size.height) cornerRadius:floatBallRect.size.height/2];
 UIBezierPath *maskFinalBP = [UIBezierPath bezierPathWithRoundedRect:CGRectMake(0, 0,SCREEN_WIDTH, SCREEN_HEIGHT) cornerRadius:floatBallRect.size.width/2];
 //.layer.mask 是部分显示的原因
 CAShapeLayer *maskLayer = [CAShapeLayer layer];
 maskLayer.path = maskFinalBP.CGPath;
 toVC.view.layer.mask = maskLayer;
 //动画类
 CABasicAnimation *maskLayerAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
 maskLayerAnimation.fromValue = (__bridge id)(maskStartBP.CGPath);
 maskLayerAnimation.toValue = (__bridge id)((maskFinalBP.CGPath));
 maskLayerAnimation.duration = kAuration;
 maskLayerAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
 maskLayerAnimation.delegate = self;
 [maskLayer addAnimation:maskLayerAnimation forKey:@"path"];
 //隐藏浮窗
 [UIView animateWithDuration:kAuration animations:^{
  [HKFloatManager shared].floatBall.alpha = 0;
 }];
}
#pragma mark - CABasicAnimation的Delegate
//动画完成后代理
- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag{
 [self.transitionContext completeTransition:YES];
 [self.transitionContext viewControllerForKey:UITransitionContextFromViewControllerKey].view.layer.mask = nil;
 [self.transitionContext viewControllerForKey:UITransitionContextToViewControllerKey].view.layer.mask = nil;
 [self.coverView removeFromSuperview];

}
-(UIView *)coverView{
 if (!_coverView) {
  _coverView = [[UIView alloc]initWithFrame:[UIScreen mainScreen].bounds];
  _coverView.backgroundColor = [UIColor blackColor];
  _coverView.alpha = 0.5;
 };
 return _coverView;
}

解耦

将所有代码集中在 #import "HKFloatManager.h" 中

//在AppDelegate中将类名传入即可,在该类控制器侧滑返回时启动浮窗功能(需要在实例化导航控制器之后)
[HKFloatManager addFloatVcs:@[@"HKSecondViewController"]];

若需要设置浮窗头像,设置该控制器的"hk_iconImage"

@property (nonatomic, strong) UIImage *hk_iconImage;
Tips

震动反馈

UIImpactFeedbackGenerator*impactLight = [[UIImpactFeedbackGenerator alloc]initWithStyle:UIImpactFeedbackStyleMedium];
[impactLight impactOccurred];
 // UIImpactFeedbackStyleLight,
 // UIImpactFeedbackStyleMedium,
 // UIImpactFeedbackStyleHeavy

分类获取当前控制器

#import "NSObject+hkvc.h"
@implementation NSObject (hkvc)
- (UIViewController *)hk_currentViewController
{
 UIWindow *keyWindow = [UIApplication sharedApplication].keyWindow;
 UIViewController *vc = keyWindow.rootViewController;
  if ([vc isKindOfClass:[UINavigationController class]])
  {
   vc = [(UINavigationController *)vc visibleViewController];
  }
  else if ([vc isKindOfClass:[UITabBarController class]])
  {
   vc = [(UITabBarController *)vc selectedViewController];
  }
 return vc;
}
- (UINavigationController *)hk_currentNavigationController
{
 return [self hk_currentViewController].navigationController;
}
- (UITabBarController *)hk_currentTabBarController
{
 return [self hk_currentViewController].tabBarController;
}
@end

判断控制器是否有"hk_iconImage"属性

- (BOOL)haveIconImage{
 BOOL have = NO;
 unsigned int outCount = 0;
 Ivar *ivars = class_copyIvarList([self.floatViewController class], &outCount);
 for (unsigned int i = 0; i < outCount; i ++) {
  Ivar ivar = ivars[i];
  const char * nameChar = ivar_getName(ivar);
  NSString *nameStr =[NSString stringWithFormat:@"%s",nameChar];
  if([nameStr isEqualToString:@"_hk_iconImage"]) {
   have = YES;
  }
 }
 free(ivars);
 return have;
}

以上便是实现该效果的全部实现.上方含有部分伪代码.全部代码已上传至---Github---欢迎(跪求) Star.

以上所述是小编给大家介绍的Android仿新版微信浮窗效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Android实现类似qq微信消息悬浮窗通知功能

    实现方法:(需要开启悬浮窗通知权限.允许应用在其他应用上显示) 一.利用headsup 悬挂式Notification,他是5.0中新增的,也就是API中的Headsup的Notification,可以在不打断用户操作的时候,给用户通知 二.使用Window创建悬浮窗 当window属性设置为FLAGE_NOT_FOCUSABLE表示不需要获取焦点,也不需要接受各种输入事件,此标记会同时启用FLAGE_NOT_TOUCH_MODEL,最终事件会直接传递给下层具有焦点的Widow FLAGE_NO

  • Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能

    如何为不同的list item呈现不同的菜单,本文实例就为大家介绍了Android仿微信或QQ滑动弹出编辑.删除菜单效果.增加下拉刷新等功能的实现,分享给大家供大家参考,具体内容如下 效果图: 1. 下载开源项目,并将其中的liberary导入到自己的项目中: 2. 使用SwipeMenuListView代替ListView,在页面中布局: <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swipeRefresh

  • 微信web端后退强制刷新功能的实现代码

    具体代码如下所示: <script> //生成uuid var uuidChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz".split(""); function uuid() { var r; var uuid = []; uuid[8] = uuid[13] = uuid[18] = uuid[23] = "-"; uuid[14] =

  • Android微信端的下拉刷新功能

    在Android和iOS上对于下拉刷新的处理方法: 在微信公众号内,在面对下拉刷新这个问题上,Android和iOS都自己的表现方式: iOS: Android: 所以我们要给内容加载监听器 function bindEvent() { document.addEventListener('touchstart', touchSatrtFunc, false); document.addEventListener('touchmove', touchMoveFunc, false); docum

  • Android仿新版微信浮窗效果

    阅读公众号或其他文章,经常需要暂时退出文章. 在新版微信中,可以把浏览的文章缩小为浮窗.点击浮窗继续阅读.对于经常在微信里阅读的人来说,这简直就是人类之光. 微信效果如下 微信效果 对于这功能我进行了仿写. 效果如下 仿写效果 微信的大佬一定用了了不起的技术,我这里只是实现效果. 简单写了一个库,一句代码即可实现效果 github.com/SherlockQi/- //在AppDelegate中将类名传入即可 [HKFloatManager addFloatVcs:@[@"HKSecondVie

  • Android仿打开微信红包动画效果实现代码

    首先看下效果: 实现原理: 准备3张不同角度的图片,通过AnimationDrawable帧动画进行播放即可 代码实现: 1.编写动画xml文件: <?xml version="1.0" encoding="utf-8"?> <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false&

  • Android仿QQ微信侧滑删除效果

    仿QQ侧滑删除效果图 1.自定义listview public class DragDelListView extends ListView { private boolean moveable=false; private boolean closed=true; private float mDownX,mDownY; private int mTouchPosition,oldPosition=-1; private DragDelItem mTouchView,oldView; priv

  • Android仿IOS系统悬浮窗效果

    在一些场合里,我们使用悬浮窗会有很大的便利,比如IOS系统的悬浮窗,360或者其他手机卫士的悬浮窗等等. 本篇博客,我们创造出两个悬浮窗,通过点击小悬浮窗打开或者关闭大悬浮窗(一个播放控制器). 代码如下: 在这之前,我们需要在manifest中申请权限: <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> 并且,悬浮窗这个权限我们需要手动在手机找到应用权限管理,允许这个权限才行

  • Android 仿余额宝数字跳动动画效果完整代码

    一:想都不用想的,有图有真相,看着爽了,在看下面源码 二:实例源码分析 ①:首先定义接口 package com.demo.tools.view; /** * 数字动画自定义 * * @author zengtao 2015年7月17日 上午11:48:27 * */ public interface RiseNumberBase { public void start(); public RiseNumberTextView withNumber(float number); public R

  • Android仿支付宝微信支付密码界面弹窗封装dialog

    一,功能效果 二,实现过程 1,先写xml文件:dialog_keyboard.xml 注意事项 (1),密码部分用的是一个线性布局中6个TextView,并设置android:inputType="numberPassword",外框是用的一个有stroke属性的shape, (2),1-9数字是用的recycleview ,每个item的底部和右边有1dp的黑线,填充后形成分割线. (3),recycleview 要设置属性  android:overScrollMode=&quo

  • Android仿QQ微信未读消息小红点BadgeHelper

    Android 小红点 未读消息功能 BadgeHelper 因为最近的项目需求,翻遍github上的未读消息红点开源库, 发现大部分 不能适配不同情况的布局, 所以我写了一个能兼容全部的 ! 网上的写法是 继承TextView然后生成一个小红点drawable,设置到背景中去, 然后把目标view外层加一层FrameLayout,然后把小红点添加进去 但这样做的问题来了, 小红点与目标View 会叠起来!, 挡住文字,!!! 看得我瞎了~~~ 而且 他们提供的setOffsetX setpad

  • Android仿斗鱼直播的弹幕效果

    记得之前有位朋友在我的公众号里问过我,像直播的那种弹幕功能该如何实现?如今直播行业确实是非常火爆啊,大大小小的公司都要涉足一下直播的领域,用斗鱼的话来讲,现在就是千播之战.而弹幕则无疑是直播功能当中最为重要的一个功能之一,那么今天,我就带着大家一起来实现一个简单的Android端弹幕效果. 分析 首先我们来看一下斗鱼上的弹幕效果,如下图所示: 这是一个Dota2游戏直播的界面,我们可以看到,在游戏界面的上方有很多的弹幕,看直播的观众们就是在这里进行讨论的. 那么这样的一个界面该如何实现呢?其实并

  • jQuery简单实现中间浮窗效果

    本文实例讲述了jQuery简单实现中间浮窗效果.分享给大家供大家参考,具体如下: basic.css: /* * -- 样式说明 -- * 最大优先实现法,全局能实现不用区域,区域能实现不用模板,模板能实现不用界面,界面能实现不用标签 * g - 全局 * t - 区域 * m - 模板 * ui - 界面 * lb - 标签 * j - 脚本 只使用在有JS操作的样式 */ /*公共样式*/ html, body, div, span, object, iframe, h1, h2, h3,

  • Android仿qq消息拖拽效果

    本文实例为大家分享了Android仿qq消息拖拽效果展示的具体代码,供大家参考,具体内容如下 这是一个仿qq消息拖拽效果,View和拖拽实现了分离,TextView.Button.Imageview等都可以实现相应的拖拽效果:在触发的地方调用 MessageBubbleView.attach(findViewById(R.id.text_view), new MessageBubbleView.BubbleDisappearListener() { @Override public void d

随机推荐