2017-04-27 12:18:50 zhaojinqiang12 阅读数 2485
  • 19年录制HTML5/CSS3教程js视频教程电商项目综合实战

    * * 课程版本:这是一套综合html+css+html5+css3+js所有前端必备知识点的电商项目实战视频教程,本套课程分为6章26节。 * 核心内容 * * * * 深度讲解仿百度搜索下拉提示及防抖函数 * 深度讲解绝对定位布局实现商品二级分类 * 深度剖析轮播图实现原理 * 详解商品秒杀专区倒计时 * 本地json数据模拟前后端数据交互 * 深入了解悬浮菜单、滚动监听、返回顶部功能 * 完整学习路线推荐

    102 人正在学习 去看看 张颜源

前提:当时看到别人写过这个类似AssistiveTouch的demo,但是有问题,第一改变不了位置、第二切换页面后无法使用、第三运行时偶尔会崩溃。然后自己就去度娘、论坛中都查了一些资料,然后结合起来写了这么一个demo。
思路:实现全局 需要在 AppDelegate.m 文件中 didFinishLaunchingWithOptions 方法里面实现
1、新建一个 继承于 UIWindow 的类 AssistiveTouch

//在 AssistiveTouch.h 文件中代码

#import <UIKit/UIKit.h>

@interface AssistiveView : UIWindow

{

    UIButton *_button;

}


-(id) initWithFrame:(CGRect)frame;


@end


//在 AssistiveTouch.m 文件中代码

#import "AssistiveView.h"


@implementation AssistiveView


/*

// Only override drawRect: if you perform custom drawing.

// An empty implementation adversely affects performance during animation.

- (void)drawRect:(CGRect)rect {

    // Drawing code

}

*/

-(id)initWithFrame:(CGRect)frame

{

    self = [super initWithFrame:frame];

    

    if (self) {

        self.backgroundColor = [UIColor clearColor];

        self.windowLevel = UIWindowLevelAlert + 1;

        //这句话很重要

        [self makeKeyAndVisible];

        

        _button = [UIButton buttonWithType:UIButtonTypeCustom];

        _button.backgroundColor = [UIColor grayColor];

        _button.frame = CGRectMake(0, 0, frame.size.width, frame.size.height);

        _button.layer.cornerRadius = frame.size.width/2;

        [_button addTarget:self action:@selector(choose) forControlEvents:UIControlEventTouchUpInside];

        [self addSubview:_button];

        

        //放一个拖动手势,用来改变控件的位置

        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(changePostion:)];

        [_button addGestureRecognizer:pan];

    }

    return self;

}


//按钮事件

-(void)choose

{

    NSLog(@"悬浮窗");

}

//手势事件 -- 改变位置

-(void)changePostion:(UIPanGestureRecognizer *)pan

{

    CGPoint point = [pan translationInView:self];

    

    CGFloat width = [UIScreen mainScreen].bounds.size.width;

    CGFloat height = [UIScreen mainScreen].bounds.size.height;

    

    CGRect originalFrame = self.frame;

    if (originalFrame.origin.x >= 0 && originalFrame.origin.x+originalFrame.size.width <= width) {

        originalFrame.origin.x += point.x;

    }

    if (originalFrame.origin.y >= 0 && originalFrame.origin.y+originalFrame.size.height <= height) {

        originalFrame.origin.y += point.y;

    }

    self.frame = originalFrame;

    [pan setTranslation:CGPointZero inView:self];

    

    if (pan.state == UIGestureRecognizerStateBegan) {

        _button.enabled = NO;

    }else if (pan.state == UIGestureRecognizerStateChanged){

        

    } else {

        

        CGRect frame = self.frame;

        //记录是否越界

        BOOL isOver = NO;

        

        if (frame.origin.x < 0) {

            frame.origin.x = 0;

            isOver = YES;

        } else if (frame.origin.x+frame.size.width > width) {

            frame.origin.x = width - frame.size.width;

            isOver = YES;

        }

        

        if (frame.origin.y < 0) {

            frame.origin.y = 0;

            isOver = YES;

        } else if (frame.origin.y+frame.size.height > height) {

            frame.origin.y = height - frame.size.height;

            isOver = YES;

        }

        if (isOver) {

            [UIView animateWithDuration:0.3 animations:^{

                self.frame = frame;

            }];

        }

        _button.enabled = YES;

    }

}


//在AppDelegate中的代码

#import "AppDelegate.h"

#import "AssistiveView.h"


@interface AppDelegate ()

{

    //悬浮框

    AssistiveView * _Win;

}


@end


@implementation AppDelegate


// 设置自定义悬浮框坐标

-(void)setNew

{

    _Win = [[AssistiveView alloc] initWithFrame:CGRectMake(0, 100, 60, 60)];

}


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    // Override point for customization after application launch.

    

    // 这句话很重要,要先将rootview加载完成之后在显示悬浮框,如没有这句话,将可能造成程序崩溃

    [self performSelector:@selector(setNew) withObject:nil afterDelay:3];

    

    [self.window makeKeyAndVisible];


    

    return YES;

}







2016-05-16 21:02:17 u011993697 阅读数 5143
  • 19年录制HTML5/CSS3教程js视频教程电商项目综合实战

    * * 课程版本:这是一套综合html+css+html5+css3+js所有前端必备知识点的电商项目实战视频教程,本套课程分为6章26节。 * 核心内容 * * * * 深度讲解仿百度搜索下拉提示及防抖函数 * 深度讲解绝对定位布局实现商品二级分类 * 深度剖析轮播图实现原理 * 详解商品秒杀专区倒计时 * 本地json数据模拟前后端数据交互 * 深入了解悬浮菜单、滚动监听、返回顶部功能 * 完整学习路线推荐

    102 人正在学习 去看看 张颜源

需求

在一个app应用的最顶部添加一个悬浮窗,就像ios系统AssistiveTouch 可以左右滑动,但是最终会停在左边或右边。

实现思路

在应用的视图的最顶层添加一个UIWindow,用这个UIWindow 充当悬浮窗,给UIWindow添加移动的手势监听,让悬浮窗随着手指移动,释放的时候,让它以动画的方式靠边

代码

    //悬浮窗测试
    //创建一个悬浮窗口
    mwindow = [[AssistiveTouch alloc]initWithFrame:CGRectMake(100, 200, 40, 40) imageName:@"1.png"];
    //ios9 window要设置rootview 不然崩溃
    UIViewController *controller = [[UIViewController alloc] init];
    mwindow.rootViewController = controller;
    //展示悬浮窗。。
    [self.window makeKeyAndVisible];
        //添加移动的手势
        UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(locationChange:)];
        pan.delaysTouchesBegan = YES;
        [self addGestureRecognizer:pan];
//改变位置
-(void)locationChange:(UIPanGestureRecognizer*)p
{
    //[[UIApplication sharedApplication] keyWindow]
    CGPoint panPoint = [p locationInView:[[UIApplication sharedApplication] keyWindow]];
    if(p.state == UIGestureRecognizerStateBegan)
    {
        [NSObject cancelPreviousPerformRequestsWithTarget:self selector:@selector(changeColor) object:nil];
        _imageView.alpha = 0.8;
    }
    else if (p.state == UIGestureRecognizerStateEnded)
    {
        [self performSelector:@selector(changeColor) withObject:nil afterDelay:4.0];
    }
    if(p.state == UIGestureRecognizerStateChanged)
    {
        self.center = CGPointMake(panPoint.x, panPoint.y);
    }
    else if(p.state == UIGestureRecognizerStateEnded)
    {
        if(panPoint.x <= kScreenWidth/2)
        {
            if(panPoint.y <= 40+HEIGHT/2 && panPoint.x >= 20+WIDTH/2)
            {
                [UIView animateWithDuration:0.2 animations:^{
                    self.center = CGPointMake(panPoint.x, HEIGHT/2);
                }];
            }
            else if(panPoint.y >= kScreenHeight-HEIGHT/2-40 && panPoint.x >= 20+WIDTH/2)
            {
                [UIView animateWithDuration:0.2 animations:^{
                    self.center = CGPointMake(panPoint.x, kScreenHeight-HEIGHT/2);
                }];
            }
            else if (panPoint.x < WIDTH/2+15 && panPoint.y > kScreenHeight-HEIGHT/2)
            {
                [UIView animateWithDuration:0.2 animations:^{
                    self.center = CGPointMake(WIDTH/2, kScreenHeight-HEIGHT/2);
                }];
            }
            else
            {
                CGFloat pointy = panPoint.y < HEIGHT/2 ? HEIGHT/2 :panPoint.y;
                [UIView animateWithDuration:0.2 animations:^{
                    self.center = CGPointMake(WIDTH/2, pointy);
                }];
            }
        }
        else if(panPoint.x > kScreenWidth/2)
        {
            if(panPoint.y <= 40+HEIGHT/2 && panPoint.x < kScreenWidth-WIDTH/2-20 )
            {
                [UIView animateWithDuration:0.2 animations:^{
                    self.center = CGPointMake(panPoint.x, HEIGHT/2);
                }];
            }
            else if(panPoint.y >= kScreenHeight-40-HEIGHT/2 && panPoint.x < kScreenWidth-WIDTH/2-20)
            {
                [UIView animateWithDuration:0.2 animations:^{
                    self.center = CGPointMake(panPoint.x, 480-HEIGHT/2);
                }];
            }
            else if (panPoint.x > kScreenWidth-WIDTH/2-15 && panPoint.y < HEIGHT/2)
            {
                [UIView animateWithDuration:0.2 animations:^{
                    self.center = CGPointMake(kScreenWidth-WIDTH/2, HEIGHT/2);
                }];
            }
            else
            {
                CGFloat pointy = panPoint.y > kScreenHeight-HEIGHT/2 ? kScreenHeight-HEIGHT/2 :panPoint.y;
                [UIView animateWithDuration:0.2 animations:^{
                    self.center = CGPointMake(320-WIDTH/2, pointy);
                }];
            }
        }
    }
}
2017-05-21 22:45:29 zengxyuyu 阅读数 2170
  • 19年录制HTML5/CSS3教程js视频教程电商项目综合实战

    * * 课程版本:这是一套综合html+css+html5+css3+js所有前端必备知识点的电商项目实战视频教程,本套课程分为6章26节。 * 核心内容 * * * * 深度讲解仿百度搜索下拉提示及防抖函数 * 深度讲解绝对定位布局实现商品二级分类 * 深度剖析轮播图实现原理 * 详解商品秒杀专区倒计时 * 本地json数据模拟前后端数据交互 * 深入了解悬浮菜单、滚动监听、返回顶部功能 * 完整学习路线推荐

    102 人正在学习 去看看 张颜源

在一些场合里,我们使用悬浮窗会有很大的便利,比如IOS系统的悬浮窗,360或者其他手机卫士的悬浮窗等等。

本篇博客,我们创造出两个悬浮窗,通过点击小悬浮窗打开或者关闭大悬浮窗(一个播放控制器)。

代码如下:

在这之前,我们需要在manifest中申请权限:

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

并且,悬浮窗这个权限我们需要手动在手机找到应用权限管理,允许这个权限才行

小悬浮窗的界面代码float_normal_view.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="65dp"
    android:layout_height="65dp"
    android:id="@+id/ll_float_normal"
    android:background="@drawable/float_bg"
    android:gravity="center"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_show_control_view"
        android:layout_gravity="center"
        android:background="@drawable/white_ring"
        android:layout_width="35dp"
        android:orientation="vertical"
        android:layout_height="35dp" >

    </ImageView>

</LinearLayout>

大悬浮窗的界面代码float_control_view:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_float_control"
    android:layout_width="300dp"
    android:layout_height="100dp"
    android:background="@drawable/float_bg"
    android:gravity="center_horizontal|bottom"
    android:orientation="vertical">

    <SeekBar
        android:id="@+id/timeline"
        android:paddingTop="3dp"
        android:paddingBottom="3dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:focusable="true"
        android:maxHeight="5.0dip"
        android:minHeight="5.0dip"
        android:paddingLeft="16.0dip"
        android:paddingRight="16.0dip"
        android:progressDrawable="@drawable/po_seekbar"
        android:thumb="@drawable/seekbar_thumb" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:paddingBottom="10dp"
        android:paddingEnd="20dp"
        android:paddingStart="20dp"
        android:paddingTop="10dp">

        <ImageButton
            android:id="@+id/ibt_rewind"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_centerVertical="true"
            android:layout_marginEnd="20dp"
            android:layout_marginRight="20dp"
            android:layout_toLeftOf="@+id/ibt_play"
            android:layout_toStartOf="@+id/ibt_play"
            android:background="@drawable/rewind" />

        <ImageButton
            android:id="@+id/ibt_play"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_centerHorizontal="true"
            android:background="@drawable/pause" />

        <ImageButton
            android:id="@+id/ibt_forward"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp"
            android:layout_marginStart="20dp"
            android:layout_toEndOf="@+id/ibt_play"
            android:layout_toRightOf="@+id/ibt_play"
            android:background="@drawable/forward" />

    </RelativeLayout>


</LinearLayout>

入口activity(FloatActivity ):

public class FloatActivity extends Activity {

    MyWindowManager myWindowManager;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myWindowManager = MyWindowManager.getInstance();
        myWindowManager.createNormalView(this.getApplicationContext());
    }
}

悬浮窗管理器MyWindowManager:

/**
 * Created by shiweixian on 2017/3/7.
 * 悬浮窗管理器
 * 创建,移除
 * 单例模式
 */
public class MyWindowManager {

    private FloatNormalView normalView;
    private FloatControlView controlView;

    private static MyWindowManager instance;
    private WindowManager windowManager;

    private MyWindowManager() {
    }

    public static MyWindowManager getInstance() {
        if (instance == null)
            instance = new MyWindowManager();
        return instance;
    }

    private WindowManager getWindowManager(Context context) {
        if (windowManager == null)
            windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        return windowManager;
    }

    /**
     * 判断小悬浮窗是否存在
     *
     * @return
     */
    public boolean isNormalViewExists() {
        return normalView != null;
    }

    /**
     * 判断播放器这个大悬浮窗是否存在
     *
     * @return
     */
    public boolean isControlViewExists() {
        return controlView != null;
    }

    /**
     * 创建小型悬浮窗
     */
    public void createNormalView(Context context) {
        if (normalView == null) {
            normalView = new FloatNormalView(context);
        }
    }


    /**
     * 移除悬浮窗
     *
     * @param context
     */
    public void removeNormalView(Context context) {
        if (normalView != null) {
            windowManager.removeView(normalView);
            normalView = null;
        }
    }

    /**
     * 创建小型悬浮窗
     */
    public void createControlView(Context context) {
        if (controlView == null)
            controlView = new FloatControlView(context);
    }

    /**
     * 移除悬浮窗
     *
     * @param context
     */
    public void removeControlView(Context context) {
        if (controlView != null) {
            WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            windowManager.removeView(controlView);
            controlView = null;
        }
    }
}

小悬浮窗FloatNormalView:


/**
 * Created by shiwe on 2017/3/7.
 * 缩小的悬浮窗
 */
public class FloatNormalView extends LinearLayout {

    /**
     * 记录小悬浮窗的宽度
     */
    public static int viewWidth;

    /**
     * 记录小悬浮窗的高度
     */
    public static int viewHeight;

    /**
     * 记录系统状态栏的高度
     */
    private static int statusBarHeight;

    /**
     * 用于更新小悬浮窗的位置
     */
    private WindowManager windowManager;

    /**
     * 小悬浮窗的参数
     */
    private WindowManager.LayoutParams mParams;

    /**
     * 记录当前手指位置在屏幕上的横坐标值
     */
    private float xInScreen;

    /**
     * 记录当前手指位置在屏幕上的纵坐标值
     */
    private float yInScreen;

    /**
     * 记录手指按下时在屏幕上的横坐标的值
     */
    private float xDownInScreen;

    /**
     * 记录手指按下时在屏幕上的纵坐标的值
     */
    private float yDownInScreen;

    /**
     * 记录手指按下时在小悬浮窗的View上的横坐标的值
     */
    private float xInView;

    /**
     * 记录手指按下时在小悬浮窗的View上的纵坐标的值
     */
    private float yInView;

    public FloatNormalView(Context context) {
        super(context);
        windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
        View view = findViewById(R.id.ll_float_normal);
        viewWidth = view.getLayoutParams().width;
        viewHeight = view.getLayoutParams().height;
        initLayoutParams();
    }

    /**
     * 初始化参数
     */
    private void initLayoutParams() {
        //屏幕宽高
        int screenWidth = windowManager.getDefaultDisplay().getWidth();
        int screenHeight = windowManager.getDefaultDisplay().getHeight();

        mParams = new WindowManager.LayoutParams();
        //总是出现在应用程序窗口之上。
        mParams.type = WindowManager.LayoutParams.TYPE_PHONE;

        // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
        // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        //悬浮窗默认显示的位置
        mParams.gravity = Gravity.START | Gravity.TOP;
        //指定位置
        mParams.x = screenWidth - viewWidth * 2;
        mParams.y = screenHeight / 2 + viewHeight * 2;
        //悬浮窗的宽高
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.format = PixelFormat.TRANSPARENT;
        windowManager.addView(this, mParams);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度
                xInView = event.getX();
                yInView = event.getY();
                xDownInScreen = event.getRawX();
                yDownInScreen = event.getRawY() - getStatusBarHeight();
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                break;
            case MotionEvent.ACTION_MOVE:
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                // 手指移动的时候更新小悬浮窗的位置
                updateViewPosition();
                break;
            case MotionEvent.ACTION_UP:
                // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
                if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {
                    openOrCloseControlView();
                }
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。
     *
     * @param params 小悬浮窗的参数
     */
    public void setParams(WindowManager.LayoutParams params) {
        mParams = params;
    }

    /**
     * 更新小悬浮窗在屏幕中的位置。
     */
    private void updateViewPosition() {
        mParams.x = (int) (xInScreen - xInView);
        mParams.y = (int) (yInScreen - yInView);
        windowManager.updateViewLayout(this, mParams);
    }

    /**
     * 打开或关闭大悬浮窗。
     */
    private void openOrCloseControlView() {
        MyWindowManager manager = MyWindowManager.getInstance();
        if (!manager.isControlViewExists())
            manager.createControlView(getContext());
        else
            manager.removeControlView(getContext());
    }

    /**
     * 用于获取状态栏的高度。
     *
     * @return 返回状态栏高度的像素值。
     */
    private int getStatusBarHeight() {
        if (statusBarHeight == 0) {
            try {
                Class<?> c = Class.forName("com.android.internal.R$dimen");
                Object o = c.newInstance();
                Field field = c.getField("status_bar_height");
                int x = (Integer) field.get(o);
                statusBarHeight = getResources().getDimensionPixelSize(x);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return statusBarHeight;
    }
}

大悬浮窗FloatControlView:


/**
 * Created by shiwe on 2017/3/7.
 * 缩小的悬浮窗
 */
public class FloatNormalView extends LinearLayout {

    /**
     * 记录小悬浮窗的宽度
     */
    public static int viewWidth;

    /**
     * 记录小悬浮窗的高度
     */
    public static int viewHeight;

    /**
     * 记录系统状态栏的高度
     */
    private static int statusBarHeight;

    /**
     * 用于更新小悬浮窗的位置
     */
    private WindowManager windowManager;

    /**
     * 小悬浮窗的参数
     */
    private WindowManager.LayoutParams mParams;

    /**
     * 记录当前手指位置在屏幕上的横坐标值
     */
    private float xInScreen;

    /**
     * 记录当前手指位置在屏幕上的纵坐标值
     */
    private float yInScreen;

    /**
     * 记录手指按下时在屏幕上的横坐标的值
     */
    private float xDownInScreen;

    /**
     * 记录手指按下时在屏幕上的纵坐标的值
     */
    private float yDownInScreen;

    /**
     * 记录手指按下时在小悬浮窗的View上的横坐标的值
     */
    private float xInView;

    /**
     * 记录手指按下时在小悬浮窗的View上的纵坐标的值
     */
    private float yInView;

    public FloatNormalView(Context context) {
        super(context);
        windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
        View view = findViewById(R.id.ll_float_normal);
        viewWidth = view.getLayoutParams().width;
        viewHeight = view.getLayoutParams().height;
        initLayoutParams();
    }

    /**
     * 初始化参数
     */
    private void initLayoutParams() {
        //屏幕宽高
        int screenWidth = windowManager.getDefaultDisplay().getWidth();
        int screenHeight = windowManager.getDefaultDisplay().getHeight();

        mParams = new WindowManager.LayoutParams();
        //总是出现在应用程序窗口之上。
        mParams.type = WindowManager.LayoutParams.TYPE_PHONE;

        // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
        // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        //悬浮窗默认显示的位置
        mParams.gravity = Gravity.START | Gravity.TOP;
        //指定位置
        mParams.x = screenWidth - viewWidth * 2;
        mParams.y = screenHeight / 2 + viewHeight * 2;
        //悬浮窗的宽高
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.format = PixelFormat.TRANSPARENT;
        windowManager.addView(this, mParams);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度
                xInView = event.getX();
                yInView = event.getY();
                xDownInScreen = event.getRawX();
                yDownInScreen = event.getRawY() - getStatusBarHeight();
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                break;
            case MotionEvent.ACTION_MOVE:
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                // 手指移动的时候更新小悬浮窗的位置
                updateViewPosition();
                break;
            case MotionEvent.ACTION_UP:
                // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
                if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {
                    openOrCloseControlView();
                }
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置。
     *
     * @param params 小悬浮窗的参数
     */
    public void setParams(WindowManager.LayoutParams params) {
        mParams = params;
    }

    /**
     * 更新小悬浮窗在屏幕中的位置。
     */
    private void updateViewPosition() {
        mParams.x = (int) (xInScreen - xInView);
        mParams.y = (int) (yInScreen - yInView);
        windowManager.updateViewLayout(this, mParams);
    }

    /**
     * 打开或关闭大悬浮窗。
     */
    private void openOrCloseControlView() {
        MyWindowManager manager = MyWindowManager.getInstance();
        if (!manager.isControlViewExists())
            manager.createControlView(getContext());
        else
            manager.removeControlView(getContext());
    }

    /**
     * 用于获取状态栏的高度。
     *
     * @return 返回状态栏高度的像素值。
     */
    private int getStatusBarHeight() {
        if (statusBarHeight == 0) {
            try {
                Class<?> c = Class.forName("com.android.internal.R$dimen");
                Object o = c.newInstance();
                Field field = c.getField("status_bar_height");
                int x = (Integer) field.get(o);
                statusBarHeight = getResources().getDimensionPixelSize(x);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return statusBarHeight;
    }
}
2017-12-21 10:56:34 qq_19697705 阅读数 2491
  • 19年录制HTML5/CSS3教程js视频教程电商项目综合实战

    * * 课程版本:这是一套综合html+css+html5+css3+js所有前端必备知识点的电商项目实战视频教程,本套课程分为6章26节。 * 核心内容 * * * * 深度讲解仿百度搜索下拉提示及防抖函数 * 深度讲解绝对定位布局实现商品二级分类 * 深度剖析轮播图实现原理 * 详解商品秒杀专区倒计时 * 本地json数据模拟前后端数据交互 * 深入了解悬浮菜单、滚动监听、返回顶部功能 * 完整学习路线推荐

    102 人正在学习 去看看 张颜源

我们经常使用各种调试工具,或者开源库来支持悬浮窗调试信息,但苹果的私有方法就提供了UIDebuggingInformationOverlay。

系统要求:  ios10+,iOS11 目前在我自己的手机上测试是行不通的。如果英语不错的可以去看这篇,看完记得通知我一下噢。

https://www.raywenderlich.com/177890/swizzling-in-ios-11-with-uidebugginginformationoverlay

使用方法:

在AppDelegate的didFinishLaunchingWithOptions方法中加入两行代码即可。

#if DEBUG

#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

id overlayClass = NSClassFromString(@"UIDebuggingInformationOverlay");

[overlayClass performSelector:NSSelectorFromString(@"prepareDebuggingOverlay")];

#endif

调用:

运行后,用两个手指头在状态栏上同时点击下就可以显示出这个调试的悬浮层


可以看到以上几个选项:

View Hierarchy  (查看View的层级关系)

VC Hierarchy      查看ViewController层级关系)

Ivar Explorer(查看UIApplication 的成员属性)

Measure    (测量View的尺寸)

Spec Compare (对比设计图)

System Coloer Audit (系统颜色审计)

View Hierarchy

进入到这个页面后,可以看见整个view的层级结构。悬浮窗右上角有个Inspect,点击这个可以高亮显示当前选择的View。如图所示:


当前选中的是 SettingSwitchItemView。

我们在页面上选择一个swicth按钮,这时会定位到该view的条目,如图所示


我们点击右侧的“!”图标,进入到swicth的详情页面,这里展示了view的详西信息,并且可以修改一些属性,比如我们修改这个button的透明度,如图所示:

VC Hierarchy

我们回到悬浮窗的主界面,进入到VC Hierarchy界面看看。

Ivar Explorer

接下来我们去Ivar Explorer页面看一下。进到页面后,对里面的一些值不是很熟悉,不知道是哪里的一些值,不过里面,看见了我们熟悉的 AppDelegate 。

1CD86F5A-54AC-4508-99CD-0E7ECF2C47BD.png

点击去看看,有我们更加熟悉的值了,是Appdelegate里我们的一些值。

Measure

笔者认为最实用的就是这个测量功能了,它可以帮助我们测量view的大小。进入这个页面是这个样子的

AD56EB68-2F6C-48E4-94BE-486498946DA5.png

上面有三个tab选项,None, Vertical, Horizontal,选择Vertical可以测量垂直方向上的距离或者view的高度,选择Horizontal是在水平方向上进行测量。在悬浮窗上还有一个 View Mode的选项开关,开启时,会以View为单位,来测量view的大小宽高;关闭时是以非空白像素为起始端和结束端进行更宽泛的测量。如图所示:

例如:测量switch的高度

关闭ViewMode后的测量

Spec Compare

这个功能是比较spec图片和我们辛苦完成的view的,也比较实用,直接可以看出我们我们扣代码出来的界面和UI给出的设计图的差别。进入到这个页面后,点击右上角的Add进入相册把UI设计师给出的设计图添加进来,如图:

然后点击一下图片,这时会把UI设计图覆盖在界面上,然后,我们用手指从上到下滑动,就可以让UI设计图变得透明, 双击退出。如图:

System Coloer Audit

这个还不知道做什么的,点进去会是空白的列表.





2017-02-22 14:22:09 wang_yuewen 阅读数 3066
  • 19年录制HTML5/CSS3教程js视频教程电商项目综合实战

    * * 课程版本:这是一套综合html+css+html5+css3+js所有前端必备知识点的电商项目实战视频教程,本套课程分为6章26节。 * 核心内容 * * * * 深度讲解仿百度搜索下拉提示及防抖函数 * 深度讲解绝对定位布局实现商品二级分类 * 深度剖析轮播图实现原理 * 详解商品秒杀专区倒计时 * 本地json数据模拟前后端数据交互 * 深入了解悬浮菜单、滚动监听、返回顶部功能 * 完整学习路线推荐

    102 人正在学习 去看看 张颜源

一、UIWindow的简介

1.UIWindow是一种特殊的UIView,通常在一个app中只会有一个UIWindow

2.iOS程序启动完毕后,创建的第一个视图控件就是UIWindow,接着创建控制器的view,最后将控制器的view添加到UIWindow上,于是控制器的view就显示在屏幕上了

3.一个iOS程序之所以能显示到屏幕上,完全是因为它有UIWindow。也就说,没有UIWindow,就看不见任何UI界面

二、在window上添加Button

1.创建window

-(UIWindow *)window
{
    if (!_window)
    {
        id <UIApplicationDelegate> delegate = [[UIApplication sharedApplication] delegate];
        if ([delegate respondsToSelector:@selector(window)])
        {
            _window = [delegate performSelector:@selector(window)];
        }
        else
        {
            _window = [[UIApplication sharedApplication] keyWindow];
        }
    }
    return _window;
}

2.创建Buuton

-(UIButton *)levitateButton
{
    if (!_levitateButton)
    {
        _levitateButton = [UIButton buttonWithType:UIButtonTypeCustom];
        _levitateButton.frame = CGRectMake(self.view.frame.size.width - 70, self.view.frame.size.height - 200, 60, 60);
        _levitateButton.backgroundColor = MainColor;
        [_levitateButton setBackgroundImage:[UIImage imageNamed:@"Button"] forState:UIControlStateNormal];
        _levitateButton.layer.cornerRadius = 30.0f;
        _levitateButton.layer.masksToBounds = YES;
        [self.window addSubview:_levitateButton];
        [_levitateButton addTarget:self action:@selector(edit) forControlEvents:UIControlEventTouchUpInside];
        UIPanGestureRecognizer *gesture = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panGestureRecognizer:)];
        gesture.delegate = self;
        [_levitateButton addGestureRecognizer:gesture];
    }
    return _levitateButton;
}

3.拉动悬浮按钮移动需要遵守手势的协议 UIGestureRecognizerDelegate

-(void)panGestureRecognizer:(UIPanGestureRecognizer *)recognizer
{
    CGPoint point = [recognizer translationInView:recognizer.view.superview];
    CGPoint center = CGPointMake(recognizer.view.center.x + point.x,recognizer.view.center.y + point.y);
    if (center.x > self.view.frame.size.width - recognizer.view.frame.size.width/2)
    {
        center.x = self.view.frame.size.width - recognizer.view.frame.size.width/2;
    }
    else if (center.x < recognizer.view.frame.size.width/2)
    {
        center.x = recognizer.view.frame.size.width/2;
    }
    else if (center.y < 109 + recognizer.view.frame.size.width/2)
    {
        center.y = 109 + recognizer.view.frame.size.width/2;
    }
    else if (center.y > self.view.frame.size.height - recognizer.view.frame.size.width/2)
    {
        center.y = self.view.frame.size.height - recognizer.view.frame.size.width/2;
    }
    [UIView animateWithDuration:0.1 animations:^{
        recognizer.view.center = center;
        [recognizer setTranslation:CGPointZero inView:recognizer.view.superview];
    }];
}
-(BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldReceiveTouch:(UITouch *)touch
{
    return YES;
}
根据自己的需要修改拉动的范围
三、总结

上面的代码还稍微繁琐点不过可以使用的,后续我会优化封装下使代码更加简洁,欢迎大家关注奔跑的蜗牛

iOS-悬浮按钮

阅读数 5475

没有更多推荐了,返回首页