ScrollView 重点分析

打开ScrollView,跟我一起看源码。

重点1:ScrollView中的container : Node* 的锚点AnchorPoint是被重置为Vec2(0,0),也就是设置大地图的时候要求用左下角作为依赖的参照点。但是,看看Layer的源码,

// Layer
Layer::Layer()
: _touchEnabled(false)
, _accelerometerEnabled(false)
, _keyboardEnabled(false)
, _touchListener(nullptr)
, _keyboardListener(nullptr)
, _accelerationListener(nullptr)
, _touchMode(Touch::DispatchMode::ALL_AT_ONCE)
, _swallowsTouches(true)
{
    _ignoreAnchorPointForPosition = true;
    setAnchorPoint(Vec2(0.5f, 0.5f));
}

ScrollView继承自Layer。

首先回顾一下Layer的坐标系相关:

在Node中有个属性_ignoreAnchorPointForPosition,默认为false,不能忽略锚点做节点坐标系中的影响。

Layer和Scene继承自Node,默认contentSize是也为0的,不同的是_ignoreAnchorPointForPosition为true,挂载在其他渲染树是都是以(0,0)为参考点,无论怎么设置_ignoreAnchorPointForPosition属性都不会对其位置产生变化。以父节点的左下角为节点坐标系为起点很容易符合我们的想法,但是要知道确切意识到是以父节点的contentSize的左下角为原点。而Node、Layer、Scene等容器默认的contentSize是(0,0).而在Sprite为容器的节点坐标系中就不太好控制了,每次应该认真去计算父节点的
contentSize的左下角位置,才能设置当年子节点的具体位置。这是父节点的相关,当然子节点都是以自身锚地为参照点去setPisition().例如,这样子才能将ScrollView正确的显示在屏幕中间,要把ScrollView的cententSize的左下角点因为设置了大小产生的偏移移除掉——这个时候我们多么希望contentSize的_ignoreAnchorPointForPosition也是false,这样子就可以使用类似精灵的操作模式,~~可惜不是。例子代码:

pScrollView->setPosition(visibleSize.width/2 - pScrollView->getContentSize().width/2,visibleSize.height/2-pScrollView->getContentSize().height/2);

知道了container的锚点相关,我们就可以正确的处理ScrollView的显示问题了。

dragging,拖拽。当touchBegan时设置这个状态,和设置初始的移动位置:

bool ScrollView::onTouchBegan(Touch* touch, Event* event)
{
    if (!this->isVisible())
    {
        return false;
    }

    Rect frame = getViewRect();

    //dispatcher does not know about clipping. reject touches outside visible bounds.
    if (_touches.size() > 2 ||
        _touchMoved          ||
        !frame.containsPoint(touch->getLocation()))
    {
        return false;
    }

    if (std::find(_touches.begin(), _touches.end(), touch) == _touches.end())
    {
        _touches.push_back(touch);
    }

    if (_touches.size() == 1)
    { // scrolling
        _touchPoint     = this->convertTouchToNodeSpace(touch);
        _touchMoved     = false;
        _dragging     = true; //dragging started
        _scrollDistance = Vec2(0.0f, 0.0f);
        _touchLength    = 0.0f;
    }
    else if (_touches.size() == 2)
    {
        _touchPoint = (this->convertTouchToNodeSpace(_touches[0]).getMidpoint(
                        this->convertTouchToNodeSpace(_touches[1])));

        _touchLength = _container->convertTouchToNodeSpace(_touches[0]).getDistance(
                       _container->convertTouchToNodeSpace(_touches[1]));

        _dragging  = false;
    }
    return true;
}

拖拽开始后就播放相关的动画和设置坐标:

触摸结束之后Reset除位移以为的状态:

void ScrollView::onTouchEnded(Touch* touch, Event* event)
{
    if (!this->isVisible())
    {
        return;
    }

    auto touchIter = std::find(_touches.begin(), _touches.end(), touch);

    if (touchIter != _touches.end())
    {
        if (_touches.size() == 1 && _touchMoved)
        {
            this->schedule(schedule_selector(ScrollView::deaccelerateScrolling));
        }
        _touches.erase(touchIter);
    } 

    if (_touches.size() == 0)
    {
        _dragging = false;
        _touchMoved = false;
    }
}

开始减速直至停止播放动画:

void ScrollView::deaccelerateScrolling(float dt)
{
    if (_dragging)
    {
        this->unschedule(schedule_selector(ScrollView::deaccelerateScrolling));
        return;
    }

    float newX, newY;
    Vec2 maxInset, minInset;

    _container->setPosition(_container->getPosition() + _scrollDistance);

    if (_bounceable)
    {
        maxInset = _maxInset;
        minInset = _minInset;
    }
    else
    {
        maxInset = this->maxContainerOffset();
        minInset = this->minContainerOffset();
    }

    newX = _container->getPosition().x;
    newY = _container->getPosition().y;

    _scrollDistance     = _scrollDistance * SCROLL_DEACCEL_RATE;
    this->setContentOffset(Vec2(newX,newY));

    if ((fabsf(_scrollDistance.x) <= SCROLL_DEACCEL_DIST &&
         fabsf(_scrollDistance.y) <= SCROLL_DEACCEL_DIST) ||
        newY >= maxInset.y || newY <= minInset.y ||
        newX >= maxInset.x || newX <= minInset.x)
    {
        this->unschedule(schedule_selector(ScrollView::deaccelerateScrolling));
        this->relocateContainer(true);
    }
}

重点在于如何计算offset的偏移位置,待续。先从tableview开始下手,处理简单的坐标偏移和移动动画,另外解决更新操作。待续!!

补记:_isUsedCellsDirty 鉴于TableView不能动态更新的问题,可以考虑把_isUsedCellsDirty设为true,然后在渲染的时候就会重新渲染啦。

时间: 2024-11-05 06:08:40

ScrollView 重点分析的相关文章

c++学习重点分析

C++是一种语言,仅仅是它的语法.特性.标准类库就已经是一门非常高深的课程,所以在开始学习的时候,必须先要打好基础.要知道当我们在学习它的时候重点应该注意什么. 一.#include “filename.h”和#include 的区别 #include “filename.h”是指编译器将从当前工作目录上开始查找此文件 #include 是指编译器将从标准库目录中开始查找此文件 二.头文件的作用 加强安全检测 通过头文件可能方便地调用库功能,而不必关心其实现方式 三.* , &修饰符的位置 对于

Android线程间通信更新UI的方法(重点分析EventBus)

Android的UI更新只能在UI线程中,即主线程.子线程中如果要进行UI更新,都是要通知主线程来进行. 几种实现方式总结如下,欢迎补充. 1.runOnUiThread() 子线程中持有当前Activity引用(假如为Activity mActivity;),即可以调用mActivity的runOnUiThread(Runnable r)方法. 2.post()和postDelay() 子线程如果持有某个View的引用,要对该View进行更新,则可调用该View对象的post(Runnable

C语言中多级指针的重点分析

一.指针简介        指针是C语言的灵魂,C语言之所以强大,很大一部分原因在于对指针的灵活运用.我们无论需要对内存的精准分配和释放,还是对接口api的使用,乃至面向对象中的类和对象的封装,都涉及到了指针.C语言的指针大致可以分为两种,一种是作为一个变量,其保存的是一段内存地址,也就是本文要谈的多级指针:另一种是作为一种数据类型,像函数指针,用于引出一种类型,主要用于回调函数. 二.指针与基础数据类型 从内存的角度看,数据类型就是一段固定大小内存块的别名. int a=0; int在32/6

linux下select,poll,epoll的使用与重点分析

好久没用I/O复用了,感觉差不多都快忘完了,记得当初刚学I/O复用的时候花了好多时间,但是由于那会不太爱写博客,导致花很多时间搞明白的东西,依然很容易忘记.俗话说眼过千遍不如手过一遍,的确,在以后的学习中,无论知识的难易亦或是重要程度如何,我都会尽量义博客的形式记录下来,这样即能用博客来督促自己学习,也能加深对知识的理解俩全其美,好了废话不说了. I/O复用的基本概述 I/O复用技术主要是用来同时监听多个套接字描述符,使得我们的程序大幅度的提高性能,一般如下情况会用到I/O复用技术 (1)程序需

HTTP协议图--HTTP 响应状态码(重点分析)

1. 状态码概述 HTTP 状态码负责表示客户端 HTTP 请求的返回结果.标记服务器端的处理是否正常.通知出现的错误等工作. HTTP 状态码如 200 OK ,以 3 位数字和原因短语组成.数字中的第一位指定了响应类别,后两位无分类. 不少返回的响应状态码都是错误的,但是用户可能察觉不到这点.比如 Web 应用程序内部发生错误,状态码依然返回 200 OK. 2. 状态码类别   类别 原因短语 1xx Informational(信息性状态码) 接收的请求正在处理 2xx Success(

HTTP协议图--HTTP 报文首部之首部字段(重点分析)

1.首部字段概述 先来回顾一下首部字段在报文的位置,HTTP 报文包含报文首部和报文主体,报文首部包含请求行(或状态行)和首部字段. 在报文众多的字段当中,HTTP 首部字段包含的信息最为丰富.首部字段同时存在于请求和响应报文内,并涵盖 HTTP 报文相关的内容信息.使用首部字段是为了给客服端和服务器端提供报文主体大小.所使用的语言.认证信息等内容. 2.首部字段结构 HTTP 首部字段是由首部字段名和字段值构成的,中间用冒号":"分隔. 另外,字段值对应单个 HTTP 首部字段可以有

Python关于self用法重点分析

在介绍Python的self用法之前,先来介绍下Python中的类和实例-- 我们知道,面向对象最重要的概念就是类(class)和实例(instance),类是抽象的模板,比如学生这个抽象的事物,可以用一个Student类来表示.而实例是根据类创建出来的一个个具体的"对象",每一个对象都从类中继承有相同的方法,但各自的数据可能不同. 1.以Student类为例,在Python中,定义类如下: class Student(object): pass12(Object)表示该类从哪个类继承

ListView源代码分析

继承关系 1.UML图 图中单独画出Scrollview是为了说明该ViewGroup并没有自带回收机制,如果要是Scrollview显示大量view,需要手动做处理. 2.继承体系的分工 (1) AdapterView An AdapterView is a view whose children aredetermined by an {@link Adapter}. a.adapter相关的抽象函数:getAdapter.setAdapter b.mEmptyView c.观察者模式

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A