适用场景说明
此代码针对环形滑动条末端滑动不灵敏情况做优化。使用此代码如果对控件注册监听,会导致没有stop事件,请悉知。
源代码
将此份代码命名为circleBar.h,然后#include到对应界面。
/*
* circleBar.h
*
* Created on: 2024年1月31日
* Author: Z20、Z21 86面板FAE
*/
#ifndef LOGICSELF_CIRCLEBAR_H_
#define LOGICSELF_CIRCLEBAR_H_
#include <math.h>
//Π,计算角度使用
#define PI 3.14159265
//点击到左右边缘的可触摸偏移值(比如起始角度-135度,现在偏移10度那么你点-140度位置也能触发)
#define TOUCH_OFFSET 10 //单位:度
namespace
{
/*
* @brife 获取中心坐标
* @param circleBar 环形滑条指针
* @return ctrlPos控件中心坐标
* */
static const MotionEvent getCtrlCenterPos(const ZKCircleBar* circleBar)
{
const int left = circleBar->getPosition().mLeft;
const int top = circleBar->getPosition().mTop;
const int width = circleBar->getPosition().mWidth;
const int height = circleBar->getPosition().mHeight;
//中心坐标
MotionEvent center;
center.mX = left + width/2;
center.mY = top + height/2;
return center;
}
/*
* @brife 计算X、Y的偏移
* @param center 中心位置
* @param ev 触摸事件
* @return 返回X、Y偏移
* */
static const MotionEvent calculateCoordinateOffset(const MotionEvent& center, const MotionEvent& ev)
{
const int xOffset = ev.mX - center.mX;
const int yOffset = ev.mY - center.mY;
MotionEvent offset;
offset.mX = xOffset;
offset.mY = yOffset;
return offset;
}
/*
* @brife 计算角度
* @param xOffset x位置偏移
* @param yOffset y位置偏移
* @return 返回角度
* */
static const double calculateAngle(const int& xOffset, const int& yOffset)
{
//判断是否在坐标轴上
if(0 == xOffset && 0 < yOffset) //Y正半轴
{
return 0;
}
else if(0 > xOffset && 0 == yOffset) //X负半轴
{
return -90;
}
else if(0 == xOffset && 0 < yOffset) //Y负半轴
{
return -180;
}
else if(0 < xOffset && 0 == yOffset) //X正半轴
{
return 90;
}
//第二象限
if(0 > xOffset && 0 > yOffset)
{
const double scale = 1.0*xOffset/yOffset;
const double angle = atan(scale) * 180 / PI;
// LOGD("--%d-- --%s-- angle:%.2lf scale:%.2lf xOffset:%d yOffset:%d\n", __LINE__, __FILE__, angle, scale, xOffset, yOffset);
return -angle;//加负号,将角度变为负值
}
//第三象限
else if(0 > xOffset && 0 < yOffset)
{
const double scale = 1.0*yOffset/xOffset;
const double angle = -90+/*角度值是负的,所以是加*/atan(scale) * 180 / PI;
return angle;
}
//第一象限
if(0 < xOffset && 0 > yOffset)
{
const double scale = 1.0*xOffset/yOffset;
const double angle = atan(scale) * 180 / PI;
return -angle; //加负号,将角度变为正值
}
//第四象限
else if(0 < xOffset && 0 < yOffset)
{
const double scale = 1.0*yOffset/xOffset;
const double angle = 90+atan(scale) * 180 / PI;
// LOGD("--%d-- --%s-- angle:%.2lf scale:%.2lf xOffset:%d yOffset:%d\n", __LINE__, __FILE__, angle, scale, xOffset, yOffset);
return angle;
}
else
{
LOGD("--%d-- --%s-- xOffset:%d yOffset:%d 计算出错!!!\n", __LINE__, __FILE__, xOffset, yOffset);
}
return 0.0;
}
/*
* @brife 是否点击到环形滑条
* @param circleBar 环形滑条指针
* @param ev 触摸事件
* @param touchRadius 触摸半径(点在控件上且在这半径之外的位置,才算点到控件)
* @param minAngle 最小角度
* @param maxAngle 最大角度
* @return true 触摸到控件;false 未触摸到控件
* */
static const bool wetherTouchCtrl(ZKCircleBar* circleBar, const MotionEvent& ev,
const int& touchRadius, const double& minAngle, const double& maxAngle)
{
static bool isTourched = false;
switch (ev.mActionStatus) {
//触摸按下判断是否点击到控件
case MotionEvent::E_ACTION_DOWN:
//控件不可见或没点击到控件返回false
if(false == circleBar->isVisible() || false == circleBar->getPosition().isHit(ev.mX, ev.mY))
{
isTourched = false;
return isTourched;
}
break;
default:
return isTourched;
break;
}
//设置控件触摸穿透
circleBar->setTouchPass(true);
//获取控件中心点
const MotionEvent center = getCtrlCenterPos(circleBar);
//中心点距离触摸点的距离
const MotionEvent offset = calculateCoordinateOffset(center, ev);
//判断点击位置是否为可滑动范围
const double angle = calculateAngle(offset.mX, offset.mY);
if(minAngle-TOUCH_OFFSET > angle || maxAngle+TOUCH_OFFSET < angle)
{
isTourched = false;
return isTourched;
}
//返回触摸点是否在触摸半径之外
isTourched = (touchRadius*touchRadius < (offset.mX*offset.mX + offset.mY*offset.mY));
return isTourched;
}
/*
* @brife 根据坐标设置环形滑动条进度值
* @param circleBar 环形滑条指针
* @param ev 触摸事件
* @param minAngle 最小角度
* @param maxAngle 最大角度
* @param touchRadius 触摸半径(默认值是0)
* */
static void setCircleBarProgress(ZKCircleBar* circleBar, const MotionEvent& ev,
const double& minAngle, const double& maxAngle, const int& touchRadius = 0)
{
//点击时为触摸到控件函数返回
if(false == wetherTouchCtrl(circleBar, ev, touchRadius, minAngle, maxAngle)){return;}
//获取控件中心点
const MotionEvent center = getCtrlCenterPos(circleBar);
//中心点距离触摸点的距离
const MotionEvent offset = calculateCoordinateOffset(center, ev);
//计算角度
double angle = calculateAngle(offset.mX, offset.mY);
// LOGD("--%d-- --%s-- center x:%d center y:%d offset x:%d offset y:%d angle:%.2lf\n", __LINE__, __FILE__,
// center.mX, center.mY, offset.mX, offset.mY, angle);
//限定角度范围
angle = (minAngle > angle)? minAngle:(maxAngle < angle)? maxAngle:angle;
//可滑动角度范围
const double totalAngle = maxAngle-minAngle;
//将角度转换为正值
angle += totalAngle/2;
//进度值
const int progress = angle/totalAngle*circleBar->getMax();
// LOGD("--%d-- --%s-- angle:%.2lf totalAngle:%.2lf progress:%d\n", __LINE__, __FILE__, angle, totalAngle, progress);
//设置进度值
circleBar->setProgress(progress);
}
}
#endif /* LOGICSELF_CIRCLEBAR_H_ */
使用说明
在界面对应的触摸回调函数直接调用即可,如下:
/**
* 有新的触摸事件时触发
* 参数:ev
* 新的触摸事件
* 返回值:true
* 表示该触摸事件在此被拦截,系统不再将此触摸事件传递到控件上
* false
* 触摸事件将继续传递到控件上
*/
static bool onXXXActivityTouchEvent(const MotionEvent &ev) {
//设置环形滑条滑动
setCircleBarProgress(circleBar, ev, -135.0, 135.0);
return false;
}