RefClass:
com.guo.android_extend.widget.ExtImageButton
com.guo.android_extend.widget.ExtImageView
com.guo.android_extend.widget.ExtRelativeLayout
com.guo.android_extend.RotateRunable
RotateDectection[1] 介绍了旋转检测和注册接口。这里介绍一下旋转动画的实现。
考虑到4个方向旋转,View的旋转可以分成 顺时针旋转90/逆时针旋转90/旋转180三种情况。这样旋转动画可以简单实现:
@Override
public boolean OnOrientationChanged(int degree, int offset, int flag) {
// TODO Auto-generated method stub
if (!this.isShown()) {
Log.i(TAG, "Not Shown!");
return false;
}
Animation animation = new RotateAnimation (offset, 0,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
animation.setDuration(ANIMATION_TIME);
animation.setFillAfter(true);
mHandler.post(new RotateRunable(animation, this, degree));
mCurDegree = degree;
return true;
}
degree为当前旋转角度,offset为相对之前旋转的度数,flag为顺时针或者逆时针的标记。均会由
com.guo.android_extend.CustomOrientationDetector计算传入。offset在顺时针时为正,逆时针时为负。
这样旋转的动画就完成了,在最后用handler去post一个动画的runanble即可。这个runnable也在com.guo.android_extend下。
当然不同的View可能当前的角度各不相同(这种只有在动态增加的情况下可能产生),因此必须要实现一个getCurrentOrientationDegree的方法,
这个方法是告诉CustomOrientationDetector当前这个注册监听的对象当前的角度是多少,用来计算旋转offset和degree之用。
这样计算角度和旋转的部分都集中统一于CustomOrientationDetector。
旋转显示的核心则是旋转画布,在View的onDraw中重写即可:
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
canvas.save();
canvas.rotate(-mCurDegree, this.getWidth() / 2f, this.getHeight() / 2f);
super.onDraw(canvas);
canvas.restore();
}
值得注意的是布局需要调用一个setWillNotDraw来让OnDraw生效。 布局的画布略有不同:
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
canvas.rotate(-mCurDegree, this.getWidth() / 2f, this.getHeight() / 2f);
}
在Android 3.0之后android支持布局直接rotate,3.0之前只能把画布旋转之后,同时还要把点击的点重写映射,这样才能和布局的点击位置一致。这部分的代码可能随着时间推移,2.3.3的减少会逐渐废弃,目前的处理方法是先旋转MotionEvent中的点,再重新dispatch。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
PointF newPoint = rotatePoint(new PointF(ev.getX(), ev.getY()),
new PointF(this.getWidth() / 2F, this.getHeight() / 2F),
-mCurDegree);
MotionEvent newEvent = MotionEvent.obtain(ev.getDownTime(),
ev.getEventTime(), ev.getAction(), newPoint.x, newPoint.y,
ev.getPressure(), ev.getSize(), ev.getMetaState(),
ev.getXPrecision(), ev.getYPrecision(), ev.getDeviceId(),
ev.getEdgeFlags());
// TODO Auto-generated method stub
return super.dispatchTouchEvent(newEvent);
}
/**
* @param A
* @param B center point
* @param degree
* @return
*/
private PointF rotatePoint(PointF A, PointF B, float degree) {
float radian = (float) Math.toRadians(degree);
float cos = (float) Math.cos(radian);
float sin = (float) Math.sin(radian);
float x = (float) ((A.x - B.x)* cos +(A.y - B.y) * sin + B.x);
float y = (float) (-(A.x - B.x)* sin + (A.y - B.y) * cos + B.y);
return new PointF(x, y);
}
这部分控件,可以用来制作环形菜单,目前sample中没有包含,考虑以后增加。