自定义学习——摩天轮原理

来源:互联网 发布:线性优化 编辑:程序博客网 时间:2024/06/11 14:20

这个例子是从课堂上拿过来的;是一个很好的例子,可以学习手势识别,以及事件分发,特别是转动速度和滑动之间的关系。



public class SkyWheel extends RelativeLayout {


private Paint redPaint;
private Paint blackPaint;
private Paint grayPaint;
// 圆心
PointF center = new PointF();
// 角度间隔
double cellDegree = 0;


float radius = 0;
private GestureDetector gestureDetector;


public SkyWheel(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}


public SkyWheel(Context context, AttributeSet attrs) {
this(context, attrs, 0);


}


public SkyWheel(Context context) {
this(context, null);
}


private void init() {
gestureDetector = new GestureDetector(getContext(), gestureListener);


redPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
redPaint.setColor(Color.RED);
redPaint.setStrokeWidth(2);


blackPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
blackPaint.setColor(Color.BLACK);
blackPaint.setStyle(Style.STROKE);
blackPaint.setStrokeWidth(3);


grayPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
grayPaint.setColor(Color.GRAY);
grayPaint.setStrokeWidth(2);
}


double firstChildDegree = 0;


public void setDegree(double degree) {
firstChildDegree = degree;
// requestLayout 会导致onLayout 调用
requestLayout();
// invalidate() -- >onDraw


}


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 把触摸事件都拦下来,交给自己的onTouchEvent 处理
// 不向下传递
return true;
}


@Override
public boolean onTouchEvent(MotionEvent event) {
// 用手势探测器来帮助我们完成功能
gestureDetector.onTouchEvent(event);
// 持续关注触摸事件
return true;
}


// 此方法负责孩子的摆放
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
// 在摆放之前,前去计算关键值
computeValues();
// 完成孩子的摆放
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);


// 孩子的中心点的角度
double childDegree = firstChildDegree + i * cellDegree;
// 中心点
int childCenterX = (int) (center.x + radius * Math.sin(childDegree));
int childCenterY = (int) (center.y - radius * Math.cos(childDegree));
// 依据中心点摆放孩子在父控件中的位置
child.layout(childCenterX - child.getWidth() / 2, childCenterY
- child.getHeight() / 2, childCenterX + child.getWidth()
/ 2, childCenterY + child.getHeight() / 2);
}
}


@SuppressLint("WrongCall")
@Override
protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
onDraw(canvas);
}


// ViewGroup在分发绘制的时候,不会调用自己的onDraw方法
// 需要手动触发
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);


// computeValues();


// 绘制圆心
canvas.drawCircle(center.x, center.y, 4, redPaint);
// 绘制圆
canvas.drawCircle(center.x, center.y, radius, blackPaint);
// 绘制孩子中心点与圆心的连线
int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
// 孩子的中心点的角度
double childDegree = firstChildDegree + i * cellDegree;
// 中心点
int childCenterX = (int) (center.x + radius * Math.sin(childDegree));
int childCenterY = (int) (center.y - radius * Math.cos(childDegree));
canvas.drawLine(childCenterX, childCenterY, center.x, center.y,
grayPaint);


}


};


private void computeValues() {
// 圆心
center.x = getWidth() / 2;
center.y = getHeight() / 2;
// 半径
int childCount = getChildCount();
// 遍历所有的孩子,求出最宽的孩子的宽度 和 最高的孩子的高度
int widthestChildWidth = 0;
int heightestChildheight = 0;


for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
int childWidth = child.getWidth();
int childHeight = child.getHeight();


if (childWidth > widthestChildWidth) {
widthestChildWidth = childWidth;
}
if (childHeight > heightestChildheight) {
heightestChildheight = childHeight;
}
}
radius = Math.min(getWidth() / 2 - widthestChildWidth / 2, getHeight()
/ 2 - heightestChildheight / 2);


// 角度间隔
cellDegree = Math.PI * 2 / childCount;


}


private OnGestureListener gestureListener = new OnGestureListener() {


// 移动
/**
* @param e1 本组触摸事件的起始事件  down
* @param e2 此时的,最新一次move事件,触发了onScroll调用
* @param distanceX 上次的位置与最新的位置的距离(x)
* @param distanceY 上次的位置与最新的位置的距离(y)

* distance =  上一次   - 本次
* 上一次  = distance + 本次
* delta =  本次 - 上一次   


*/
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {


double oldPointDegree = getPiontDegree(e2.getX() + distanceX,
e2.getY() + distanceY);
double newPointDegree = getPiontDegree(e2.getX(), e2.getY());


double deltaDegree = newPointDegree - oldPointDegree;


setDegree(firstChildDegree + deltaDegree);


Log.d("onScroll", " ");
// 触摸转动
return false;
}


// 抬起手指时,还有速度
/**
* @param e1 本组触摸事件的起始事件  down
* @param e2 本组触摸事件的最后一次move事件  move
* @param velocityX 手指离开屏幕的速度 ,单位是像素/秒  (水平)
* @param velocityY 手指离开屏幕的速度 ,单位是像素/秒  (垂直)
*/
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// 计速器
// VelocityTracker
// 完成 像素速度 --》角速度的转化


// 获得1毫秒后手指所在的位置


// 求出1毫秒后 手指对应的角度
double degreeAfter1Ms = getPiontDegree(
e2.getX() + velocityX / 1000, e2.getY() + velocityY / 1000);
// 求出现在手指的角度
double degreeCurrent = getPiontDegree(e2.getX(), e2.getY());
// 1ms后 - 现在 = 1ms的角度变化量
double degreeDelta1Ms = degreeAfter1Ms - degreeCurrent;
// 1ms的角度变化量 * 1000 = 角速度
double degreeVelocity = degreeDelta1Ms * 1000;


// 根据角速度 让控件自行转动,并减速,直至停下来
startRotate(degreeVelocity);
Log.d("onFling", " degreeVelocity :" + degreeVelocity);
return false;
}


// 单击
@Override
public boolean onSingleTapUp(MotionEvent e) {
Log.d("onSingleTapUp", " ");
int touchIndex = findTouchChildIndex(e.getX(), e.getY());
if (touchIndex >= 0) {
// 触发孩子的点击事件
getChildAt(touchIndex).performClick();
}
return false;
}


// 按下后一小会,还没有移动
@Override
public void onShowPress(MotionEvent e) {
Log.d("onShowPress", " ");
}


// 长按
@Override
public void onLongPress(MotionEvent e) {
Log.d("onLongPress", " ");


}


// 手指按下
@Override
public boolean onDown(MotionEvent e) {
Log.d("onDown", " ");
// 如果正在惯性转动,则停下来
if (valueAnimator != null && valueAnimator.isRunning()) {
// 则停下来
valueAnimator.cancel();
}
return false;
}
};
private ValueAnimator valueAnimator;


protected double getPiontDegree(float x, float y) {
return Math.atan2(y - center.y, x - center.x);
}


protected int findTouchChildIndex(float x, float y) {
// 遍历所有的孩子,判断x和y与孩子的4个边的关系


int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
if (x > child.getLeft() && x < child.getRight()
&& y > child.getTop() && y < child.getBottom()) {
return i;
}


}


return -1;
}


protected void startRotate(double degreeVelocity) {
// 转动的时间
long duration = (long) (Math.abs(degreeVelocity) / Math.PI * 1000);


// 转动的总角度
double totalDegree = degreeVelocity * duration / 1000 / 2;
valueAnimator = ValueAnimator.ofFloat((float) firstChildDegree,
(float) (firstChildDegree + totalDegree));
valueAnimator.setDuration(duration);
// 应该是先快,后慢,设置减速插值器
valueAnimator.setInterpolator(new DecelerateInterpolator(2));


// 设置更新监听,得到当前值
valueAnimator.addUpdateListener(new AnimatorUpdateListener() {


@Override
public void onAnimationUpdate(ValueAnimator animation) {
float value = (Float) animation.getAnimatedValue();
setDegree(value);
// Log.d("onAnimationUpdate", "" + value);
}
});
valueAnimator.start();


}
}

原创粉丝点击