Abd*_*o21 6 java android rotation android-custom-view kotlin
我有一个名为的自定义视图MyView,它有两个锚点。我希望能够从其锚点旋转此视图,其中一个锚点充当旋转中心,而用户拖动另一个锚点。
为了简单起见,我画了一条线,但实际上,我会画别的东西。步骤如下:
setPivotX()和使另一个锚点成为旋转中心setPivotY()。这是相关代码:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
tapX = event.getX();
tapY = event.getY();
if (anchorRect1.contains(tapX, tapY)) {
viewState = ViewState.ANCHOR1;
setPivotX(anchorRect2.centerX());
setPivotY(anchorRect2.centerY());
} else if (anchorRect2.contains(tapX, tapY)) {
viewState = ViewState.ANCHOR2;
setPivotX(anchorRect1.centerX());
setPivotY(anchorRect1.centerY());
} else {
viewState = ViewState.VIEW;
}
break;
}
case MotionEvent.ACTION_MOVE: {
switch (viewState) {
//Anchor 1 clicked
case ANCHOR1: {
float angle = (float) Math.toDegrees(Math.atan2(event.getY() - anchorRect2.centerY(), getWidth()));
setRotation(getRotation() - angle);
break;
}
//Anchor 2 clicked
case ANCHOR2: {
float angle = (float) Math.toDegrees(Math.atan2(event.getY() - anchorRect1.centerY(), getWidth()));
setRotation(getRotation() + angle);
break;
}
//View Clicked
case VIEW: {
float deltaX = event.getX() - tapX;
float deltaY = event.getY() - tapY;
// Transform point
PointF transformedPoint = transformPoint(deltaX, deltaY, getRotation());
//Perform the rotation
setX(getX() + transformedPoint.x);
setY(getY() + transformedPoint.y);
break;
}
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
break;
}
}
return true;
}
Run Code Online (Sandbox Code Playgroud)
这是当前输出的屏幕截图:
问题是,当我第一次旋转视图时,它可以正常工作,但是当我从另一个锚点旋转它时,它会跳到另一个位置。我相信这是因为旋转不会影响视图的位置。为了解决这个问题,我需要重新计算视图的新位置,以便它相应地移动,但我不知道该怎么做。
这就是我想要实现的目标(使用 GeoGebra 工具创建):
此外,我相信这个问题不仅限于Android平台,因为我在使用Qt框架实现此功能时也遇到过类似的问题。
我投入了大量的时间来解决这个问题,如果您能提供任何帮助,我们将不胜感激。
--------------------开始编辑---------------------------- --------
根据这里的答案,当前实现的问题似乎是视图没有保存之前的转换。每次更新枢轴点时,所有先前的转换都会重置,这会导致视图行为不正确。
为了解决这个问题,我们需要链接这些转换(一个接着一个应用)。在 Qt 中,这可以通过将转换保存在列表中来实现,如以下代码所示:
QList<QGraphicsTransform*> trans;
auto angle = qRadiansToDegrees(qAtan2( _anchor2.y() -p.y(), width));
QGraphicsRotation* rot = new QGraphicsRotation;
rot->setOrigin(QVector3D(cp2.x(), cp2.y(), 0));
rot->setAxis(Qt::ZAxis);
rot->setAngle(angle);
trans.push_back(rot);
setTransformations(_trans);
Run Code Online (Sandbox Code Playgroud)
实现此行为的另一种方法是直接使用QTransform类。isRelative在这种情况下,将参数设置为 true非常重要setTransform(),以便将转换链接在一起。
QTransform t;
auto angle = qRadiansToDegrees(qAtan2(_anchor2.y() -p.y(), width));
t.translate(cp2.x(), cp2.y());
t.rotate(angle);
t.translate(-cp2.x(), -cp2.y());
setTransform(t, true);
Run Code Online (Sandbox Code Playgroud)
在Android中,相当于QTransform的是Matrix类。然而,与QT不同的是,ViewAndroid中的类没有setMatrix()方法。这意味着在执行转换时,您不能简单地将当前矩阵应用于视图,这使得使用起来更加困难。
-------------------编辑结束---------------------------------------- ----------
这是重现该问题的存储库,下面是完整的代码:
MyView.java
public class MyView extends View {
enum ViewState {
ANCHOR1, ANCHOR2, VIEW
}
public static final int RADIUS = 24; // Anchor radius
public static final int OFFSET = 1;
private final Paint paint;
public RectF anchorRect1;
public RectF anchorRect2;
private ViewState viewState;
private float tapX;
private float tapY;
public MyView(Context context) {
super(context);
paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
anchorRect1 = new RectF();
anchorRect2 = new RectF();
}
@Override
protected void onDraw(Canvas canvas) {
var width = getWidth();
var height = getHeight();
//Draw line between anchor points
canvas.drawLine(
anchorRect1.centerX(), anchorRect1.centerY(),
anchorRect2.centerX(), anchorRect2.centerY(), paint);
//Draw anchor circle
canvas.drawCircle(anchorRect1.centerX(), anchorRect1.centerY(), RADIUS, paint);
canvas.drawCircle(anchorRect2.centerX(), anchorRect2.centerY(), RADIUS, paint);
//Draw bounding rect
canvas.drawRect(OFFSET, OFFSET, width - OFFSET, height - OFFSET, paint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
//Recompute anchors rect
anchorRect1.set(
OFFSET, h /2f - RADIUS + OFFSET,
2*RADIUS, h /2f + RADIUS - OFFSET
);
anchorRect2.set(
w - 2*RADIUS, h /2f - RADIUS + OFFSET,
w - OFFSET, h /2f + RADIUS - OFFSET
);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Measure exactly
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(width, height);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
tapX = event.getX();
tapY = event.getY();
// Anchor 1 clicked
if (anchorRect1.contains(tapX, tapY)) {
viewState = ViewState.ANCHOR1;
setPivotX(anchorRect2.centerX());
setPivotY(anchorRect2.centerY());
}
// Anchor 2 clicked
else if (anchorRect2.contains(tapX, tapY)) {
viewState = ViewState.ANCHOR2;
setPivotX(anchorRect1.centerX());
setPivotY(anchorRect1.centerY());
}
// View body clicked
else {
viewState = ViewState.VIEW;
}
break;
}
case MotionEvent.ACTION_MOVE: {
switch (viewState) {
//Anchor 1 clicked
case ANCHOR1: {
float angle = (float) Math.toDegrees(Math.atan2(event.getY() - anchorRect2.centerY(), getWidth()));
setRotation(getRotation() - angle);
break;
}
//Anchor 2 clicked
case ANCHOR2: {
float angle = (float) Math.toDegrees(Math.atan2(event.getY() - anchorRect1.centerY(), getWidth()));
setRotation(getRotation() + angle);
break;
}
//View Clicked
case VIEW: {
float deltaX = event.getX() - tapX;
float deltaY = event.getY() - tapY;
// Transform point
PointF transformedPoint = transformPoint(deltaX, deltaY, getRotation());
//Perform the rotation
setX(getX() + transformedPoint.x);
setY(getY() + transformedPoint.y);
break;
}
}
break;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
break;
}
}
return true;
}
private PointF transformPoint(float x, float y, float degree) {
//https://en.wikipedia.org/wiki/Rotation_matrix
double radian = Math.toRadians(degree);
float xp = (float) (x*Math.cos(radian) - y*Math.sin(radian));
float yp = (float) (x*Math.sin(radian) + y*Math.cos(radian));
return new PointF(xp, yp);
}
}
Run Code Online (Sandbox Code Playgroud)
固定位置布局.java
public class FixedPosLayout extends ViewGroup {
public FixedPosLayout(Context context) {
super(context);
}
public FixedPosLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FixedPosLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
for (int i = 0; i<count; ++i) {
View child = getChildAt(i);
FixedPosLayout.LayoutParams params =
(FixedPosLayout.LayoutParams) child.getLayoutParams();
int left = params.x;
int top = params.y;
child.layout(left, top,
left + child.getMeasuredWidth(),
top + child.getMeasuredHeight());
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// Measure this view
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// Measure children
int count = getChildCount();
for (int i = 0; i<count; ++i) {
View child = getChildAt(i);
FixedPosLayout.LayoutParams lp =
(FixedPosLayout.LayoutParams) child.getLayoutParams();
int childWidthMeasureSpec =
MeasureSpec.makeMeasureSpec(lp.width, MeasureSpec.EXACTLY);
int childHeightMeasureSpec =
MeasureSpec.makeMeasureSpec(lp.height, MeasureSpec.EXACTLY);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
}
public static class LayoutParams extends ViewGroup.LayoutParams {
public int x;
public int y;
public LayoutParams(int width, int height, int x, int y) {
super(width, height);
this.x = x;
this.y = y;
}
}
}
Run Code Online (Sandbox Code Playgroud)
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FixedPosLayout fixedPosLayout = findViewById(R.id.fixedPosLayout);
MyView myView = new MyView(this);
FixedPosLayout.LayoutParams params = new FixedPosLayout.LayoutParams(300, 80, 200, 400);
fixedPosLayout.addView(myView, params);
}
}
Run Code Online (Sandbox Code Playgroud)
main_activity.xml
<com.abdo.rotateviewquestion.FixedPosLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/fixedPosLayout"
tools:context=".MainActivity"/>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
216 次 |
| 最近记录: |