qt 列表(qtableview/qtablewidget/qlistwidget)触摸屏滑动卡死问题

解决列表滑动卡死问题

项目运行环境:window10 系统的平板,使用到qt 列表(仿照android列表),但是qwidget对于触屏操作支持个人感觉不是太好。列表滑动时有加速和拖动效果,测试发现,快速滑动没有结束或者拖动没有恢复时,返回父界面,再次进入子界面会卡死。困扰很久,大量测试最后发现在状态更新后增加滚动停止能解决问题,记录一下,同时也给遇到同样问题的小伙伴一种解决思路

对于有滚动的界面或者控件,需要滚动加速或者拖动就要用到QScroller类。QScroller类是控制触屏滑动类,此类构造方法是私有的开发无法自己创建和释放,这个就是导致问题的地方。

让我们看看问题
手指点击屏幕会相应触屏事件


QGestureRecognizer::Result QFlickGestureRecognizer::recognize(QGesture *state,
                                                              QObject *watched,
                                                              QEvent *event)
{
    Q_UNUSED(watched);

    ......忽略

    // Check for an active scroller at globalPos
    if (inputType == QScroller::InputPress) {
    	//拿到滚动类对象,可能有n个对象
        const auto activeScrollers = QScroller::activeScrollers();
        for (QScroller *as : activeScrollers) {
            if (as != scroller) {
                QRegion scrollerRegion;

                if (QWidget *w = qobject_cast<QWidget *>(as->target()))//取得数据
                ......忽略                   
            }
        }
    }

    ......忽略
    return result;
}

as->target()方法

QObject *QScroller::target() const
{
    Q_D(const QScroller);
    return d->target; //d指针为空程序崩溃
}

d指针为什么是空呢?

全局对象

typedef QMap<QObject *, QScroller *> ScrollerHash;
//存放QScroller对象指针
//创建QScroller方法,可以看到qt_allScrollers其实只会存放一个对象
Q_GLOBAL_STATIC(ScrollerHash, qt_allScrollers) 
//存放每一次事件对应的QScroller对象指针,如果只有一个控件其实指向的对象是同一个
Q_GLOBAL_STATIC(QList<QScroller *>, qt_activeScrollers)

保存滚动事件qt_activeScrollers

void QScrollerPrivate::setState(QScroller::State newstate)
{
    Q_Q(QScroller);
    bool sendLastScroll = false;

    if (state == newstate)
        return;

    qScrollerDebug() << q << "QScroller::setState(" << stateName(newstate) << ')';

    ......忽略
    qSwap(state, newstate);
	......忽略
    
	//每一次触屏滑动产生事件依次是
	//QScroller::Pressed
	//QScroller::Dragging
	//QScroller::Scrolling
	//如果正在滚动中(QScroller::Scrolling),再次点击就会产生拖动事件(QScroller::Dragging),
	//此处意味着qt_activeScrollers可以保存很多QScroller对象指针,这就是一个雷
    if (state == QScroller::Dragging || state == QScroller::Scrolling)
        qt_activeScrollers()->push_back(q);
    else
        qt_activeScrollers()->removeOne(q);
    emit q->stateChanged(state);
}

析构方法

/*!
    \internal
*/
QScroller::~QScroller()
{
    Q_D(QScroller);
#ifndef QT_NO_GESTURES
    QGestureRecognizer::unregisterRecognizer(d->recognizerType);
    // do not delete the recognizer. The QGestureManager is doing this.
    d->recognizer = 0;
#endif
    qt_allScrollers()->remove(d->target);
    qt_activeScrollers()->removeOne(this);//NND 移除当前对象,意味着其他指针还在这个全局对象中

    delete d_ptr;
}

只要qt_activeScrollers()存放多个指针而且是指向同一个对象则再次触屏滑动就会出现空指针而崩溃

一个半残的解决方法后续有空再找一找完美的解决方法

触屏滑动需要设置

this->grabGesture(Qt::SwipeGesture, Qt::GestureFlag::DontStartGestureOnChildren);
QScroller::grabGesture(this, QScroller::TouchGesture);

//禁止列表控件拖动
QScrollerProperties properties = QScroller::scroller(this)->scrollerProperties();
QVariant overshootPolicy = QVariant::fromValue<QScrollerProperties::OvershootPolicy>(QScrollerProperties::OvershootAlwaysOff); //关闭拖动
properties.setScrollMetric(QScrollerProperties::VerticalOvershootPolicy, overshootPolicy);
QScroller::scroller(this)->setScrollerProperties(properties); //设置触屏滚动属性,防止拖动和滚动相互影响 

重要的地方

connect(QScroller::scroller(this), &QScroller::stateChanged, this, [=](const QScroller::State &newState)
{
	qDebug() << "activeScrollers-count:" << QScroller::activeScrollers().count();
	qDebug() << "stateChanged" << newState;

	if (QScroller::activeScrollers().count() > 2)
	{
		QList<QScroller *> scrollers = QScroller::activeScrollers();
		foreach(auto bean, scrollers)
		{
			qDebug() << "more :" << bean->state();
			bean->stop();
		}
	}

	//能产生两个事件绝对是要停止滚动的,这样才能保证退出再进入不会崩溃
	if (newState == QScroller::Pressed || newState == QScroller::Inactive)
	{
		QList<QScroller *> scrollers = QScroller::activeScrollers();
		foreach(auto bean, scrollers)
		{
			qDebug() << "stop :" << bean->state();
			bean->stop();
		}
	}
});

控件析构时

this->ungrabGesture(Qt::SwipeGesture);
QScroller::ungrabGesture(this);
QList<QScroller *> scrollers = QScroller::activeScrollers();
foreach(auto bean, scrollers)
{
	if (bean != nullptr)
	{
		qDebug() << "~:" << bean->state();
		bean->stop();
	}
	//emit destroyed(bean);
}

滚动加速还有另一种方法就是滑动时主动调用QScroller的stop方法禁止默认的滚动然后调用scrollTo方法自己滚动

关于列表触屏和鼠标事件研究
qt 滚动类方法和部分属性解释

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值