Android 可缩放的无尽环
Android Zoomable endless rings
我正在尝试创建一个无限的可缩放环,就像这样 -
每当用户缩放时,中心会创建新的小圆圈,外圈会变大。我正在使用 pinch zoom in relative layout 创建以下效果,但无法正确实现。我为此创建了一个回购协议,如果你能帮忙,请告诉我
https://github.com/rohankandwal/zoomable-growing-circles
更新:-
在提到的 Whosebug 答案上更改了 dispatchDraw
方法 -
protected void dispatchDraw(Canvas canvas) {
Paint myPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
myPaint.setStyle(Paint.Style.STROKE);
int strokeWidth = 4; // or whatever
myPaint.setStrokeWidth(strokeWidth);
myPaint.setColor(0xffff0000); //color.RED
float radius = (float) (0.5 * (width + height) * 2.5);
for (int i = 1; i <= 51; i=i+10) {
canvas.drawCircle(canvas.getWidth() / 2, canvas.getWidth() / 2, (radius) + mScaleFactor + i,
myPaint);
}
canvas.save();
super.dispatchDraw(canvas);
canvas.restore();
}
此代码允许圆圈增长 -
没有缩放的原始图像
缩放时的图像
如您所见,缩放功能正常,但不知道在缩放到一定级别时如何创建新的圆圈。
如果我理解正确你想要什么,你的代码没有太多需要改变的地方
在 OnPinchListener
中:
public boolean onScale(ScaleGestureDetector detector) {
// 1 : no zoom
final float zoomLevel = detector.getCurrentSpan() / startingSpan;
zoomableRadarLayout.scale(zoomLevel, startFocusX, startFocusY);
return true;
}
并在 ZoomableRadarLayout
中:
// current pinch
private float mScaleFactor = 1;
// previous pinches
private float mAccumulatedScaleFactor = 1;
protected void dispatchDraw(Canvas canvas) {
...
float radius = (float) (0.5 * (width + height) * 2.5);
// space between circles
int step = 10;
// radius of the outer circle
int outerRadius = (int) (radius * mScaleFactor * mAccumulatedScaleFactor);
// radius of the inner circle
int innerRadius = 0;
for (int i = outerRadius; i >= innerRadius; i = i - step) {
canvas.drawCircle(canvas.getWidth() / 2, canvas.getWidth() / 2, i, myPaint);
}
...
}
public void restore() {
// add the ending pinch to the previous ones
mAccumulatedScaleFactor = mAccumulatedScaleFactor * mScaleFactor;
mScaleFactor = 1;
this.invalidate();
}
顺便说一下,如果可能的话,建议在绘图时避免创建像 Paint
这样的绘图对象。例如将创建放在一些初始化方法中。
您可以使用这种方法:计算内圆的半径(最小半径)然后画圆并增加半径,同时圆可以在屏幕上可见(最大半径不大于屏幕中心到其中之一的距离)它的角落)。使用 ScaleGestureDetector
:
试试这个自定义视图
public class ZoomableRingView extends View {
private Paint mPaint;
private ScaleGestureDetector mScaleDetector;
private float mDeltaRadius = 0;
private final float mBaseRadius = 50;
private float mPinchBaseRadius = mBaseRadius;
private float mMinRadius = mBaseRadius;
public ZoomableRingView(Context context) {
super(context);
init(context);
}
public ZoomableRingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomableRingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public ZoomableRingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
@Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.save();
drawCircles(canvas);
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
mScaleDetector.onTouchEvent(ev);
return true;
}
private void init(Context context) {
setWillNotDraw(false);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(5);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
private void drawCircles(Canvas canvas) {
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
int centerX = canvasWidth / 2;
int centerY = canvasHeight / 2;
float maxRadius = (float) Math.sqrt(centerX * centerX + centerY * centerY);
int nCircles = (int) Math.ceil(maxRadius / mBaseRadius) + 1;
// calculate radius change
mMinRadius = mPinchBaseRadius + mDeltaRadius / 2;
// bring radius to [0..2 * mBaseRadius] interval
while (mMinRadius < 1f) {
mMinRadius += 2 * mBaseRadius;
}
while (mMinRadius > 2 * mBaseRadius) {
mMinRadius -= 2 * mBaseRadius;
}
// draw circles from min to max
float radius = mMinRadius;
for (int ixCircle = 0; ixCircle < nCircles; ixCircle++) {
canvas.drawCircle(centerX, centerY, radius, mPaint);
radius += 2 * mBaseRadius;
}
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
float startingSpan;
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mPinchBaseRadius = mMinRadius;
startingSpan = detector.getCurrentSpan();
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
mDeltaRadius = detector.getCurrentSpan() - startingSpan;
invalidate();
return true;
}
}
}
您的 activity.xml
自定义视图可以是这样的:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_channel1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<{YOUR_PACKAGE_NAME}.ZoomableRingView
android:id="@+id/zoomablering_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
你应该得到类似的东西:
我正在尝试创建一个无限的可缩放环,就像这样 -
每当用户缩放时,中心会创建新的小圆圈,外圈会变大。我正在使用 pinch zoom in relative layout 创建以下效果,但无法正确实现。我为此创建了一个回购协议,如果你能帮忙,请告诉我 https://github.com/rohankandwal/zoomable-growing-circles
更新:-
在提到的 Whosebug 答案上更改了 dispatchDraw
方法 -
protected void dispatchDraw(Canvas canvas) {
Paint myPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
myPaint.setStyle(Paint.Style.STROKE);
int strokeWidth = 4; // or whatever
myPaint.setStrokeWidth(strokeWidth);
myPaint.setColor(0xffff0000); //color.RED
float radius = (float) (0.5 * (width + height) * 2.5);
for (int i = 1; i <= 51; i=i+10) {
canvas.drawCircle(canvas.getWidth() / 2, canvas.getWidth() / 2, (radius) + mScaleFactor + i,
myPaint);
}
canvas.save();
super.dispatchDraw(canvas);
canvas.restore();
}
此代码允许圆圈增长 -
没有缩放的原始图像
缩放时的图像
如您所见,缩放功能正常,但不知道在缩放到一定级别时如何创建新的圆圈。
如果我理解正确你想要什么,你的代码没有太多需要改变的地方
在 OnPinchListener
中:
public boolean onScale(ScaleGestureDetector detector) {
// 1 : no zoom
final float zoomLevel = detector.getCurrentSpan() / startingSpan;
zoomableRadarLayout.scale(zoomLevel, startFocusX, startFocusY);
return true;
}
并在 ZoomableRadarLayout
中:
// current pinch
private float mScaleFactor = 1;
// previous pinches
private float mAccumulatedScaleFactor = 1;
protected void dispatchDraw(Canvas canvas) {
...
float radius = (float) (0.5 * (width + height) * 2.5);
// space between circles
int step = 10;
// radius of the outer circle
int outerRadius = (int) (radius * mScaleFactor * mAccumulatedScaleFactor);
// radius of the inner circle
int innerRadius = 0;
for (int i = outerRadius; i >= innerRadius; i = i - step) {
canvas.drawCircle(canvas.getWidth() / 2, canvas.getWidth() / 2, i, myPaint);
}
...
}
public void restore() {
// add the ending pinch to the previous ones
mAccumulatedScaleFactor = mAccumulatedScaleFactor * mScaleFactor;
mScaleFactor = 1;
this.invalidate();
}
顺便说一下,如果可能的话,建议在绘图时避免创建像 Paint
这样的绘图对象。例如将创建放在一些初始化方法中。
您可以使用这种方法:计算内圆的半径(最小半径)然后画圆并增加半径,同时圆可以在屏幕上可见(最大半径不大于屏幕中心到其中之一的距离)它的角落)。使用 ScaleGestureDetector
:
public class ZoomableRingView extends View {
private Paint mPaint;
private ScaleGestureDetector mScaleDetector;
private float mDeltaRadius = 0;
private final float mBaseRadius = 50;
private float mPinchBaseRadius = mBaseRadius;
private float mMinRadius = mBaseRadius;
public ZoomableRingView(Context context) {
super(context);
init(context);
}
public ZoomableRingView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(context);
}
public ZoomableRingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
public ZoomableRingView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context);
}
@Override
public void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
canvas.save();
drawCircles(canvas);
canvas.restore();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
mScaleDetector.onTouchEvent(ev);
return true;
}
private void init(Context context) {
setWillNotDraw(false);
mPaint = new Paint();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setColor(Color.RED);
mPaint.setStrokeWidth(5);
mScaleDetector = new ScaleGestureDetector(context, new ScaleListener());
}
private void drawCircles(Canvas canvas) {
int canvasWidth = canvas.getWidth();
int canvasHeight = canvas.getHeight();
int centerX = canvasWidth / 2;
int centerY = canvasHeight / 2;
float maxRadius = (float) Math.sqrt(centerX * centerX + centerY * centerY);
int nCircles = (int) Math.ceil(maxRadius / mBaseRadius) + 1;
// calculate radius change
mMinRadius = mPinchBaseRadius + mDeltaRadius / 2;
// bring radius to [0..2 * mBaseRadius] interval
while (mMinRadius < 1f) {
mMinRadius += 2 * mBaseRadius;
}
while (mMinRadius > 2 * mBaseRadius) {
mMinRadius -= 2 * mBaseRadius;
}
// draw circles from min to max
float radius = mMinRadius;
for (int ixCircle = 0; ixCircle < nCircles; ixCircle++) {
canvas.drawCircle(centerX, centerY, radius, mPaint);
radius += 2 * mBaseRadius;
}
}
private class ScaleListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
float startingSpan;
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mPinchBaseRadius = mMinRadius;
startingSpan = detector.getCurrentSpan();
return true;
}
@Override
public boolean onScale(ScaleGestureDetector detector) {
mDeltaRadius = detector.getCurrentSpan() - startingSpan;
invalidate();
return true;
}
}
}
您的 activity.xml
自定义视图可以是这样的:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/layout_channel1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<{YOUR_PACKAGE_NAME}.ZoomableRingView
android:id="@+id/zoomablering_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
你应该得到类似的东西: