Android Studio 自定义视图坐标有偏移

问题描述 投票:0回答:1

我正在 Android Studio 中开发一个应用程序,可以让用户可视化家谱。

对于家庭成员的个人资料,我创建了自定义视图

PersonCard


public class PersonCard extends FrameLayout {
    private ImageView imageView;
    private LinearLayout textContainer;
    private View topAnchor, bottomAnchor, leftAnchor, rightAnchor;

    private int imageSize;

    public PersonCard(Context context) {
        super(context);
        init(null);
    }

    public PersonCard(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

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

    private void init(AttributeSet attrs) {
        setBackgroundResource(R.drawable.person_card);

        int padding = getResources().getDimensionPixelSize(R.dimen.person_card_padding);
        setPadding(padding, padding, padding, padding);

        if (attrs != null) {
            TypedArray a = getContext().getTheme().obtainStyledAttributes(
                    attrs,
                    R.styleable.PersonCard,
                    0, 0);

            try {
                imageSize = a.getDimensionPixelSize(R.styleable.PersonCard_imageSize, 0);
            } finally {
                a.recycle();
            }
        }

        imageView = new ImageView(getContext());
        FrameLayout.LayoutParams imageParams = new FrameLayout.LayoutParams(
                imageSize,
                imageSize
        );
        imageParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
        imageView.setLayoutParams(imageParams);
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
        imageView.setImageResource(R.drawable.default_avatar);

        textContainer = new LinearLayout(getContext());
        FrameLayout.LayoutParams textContainerParams = new FrameLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT
        );
        textContainerParams.gravity = Gravity.CENTER_HORIZONTAL | Gravity.TOP;
        textContainerParams.topMargin = imageSize + padding;
        textContainer.setLayoutParams(textContainerParams);
        textContainer.setOrientation(LinearLayout.VERTICAL);

        for (int i = 0; i < 3; i++) {
            TextView textView = new TextView(getContext());
            textView.setLayoutParams(new LinearLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
            ));
            textView.setText("Text " + (i + 1));
            textView.setTextColor(ContextCompat.getColor(getContext(), R.color.black));
            textView.setPadding(16, 16, 16, 8);
            textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
            textContainer.addView(textView);
        }

        topAnchor = new View(getContext());
        bottomAnchor = new View(getContext());
        leftAnchor = new View(getContext());
        rightAnchor = new View(getContext());

        int anchorColor = ContextCompat.getColor(getContext(), R.color.tea_rose);
        int anchorSize = 10;

        topAnchor.setBackgroundColor(anchorColor);
        bottomAnchor.setBackgroundColor(anchorColor);
        leftAnchor.setBackgroundColor(anchorColor);
        rightAnchor.setBackgroundColor(anchorColor);

        FrameLayout.LayoutParams topAnchorParams = new FrameLayout.LayoutParams(
                anchorSize,
                anchorSize
        );
        topAnchorParams.gravity = Gravity.TOP | Gravity.CENTER_HORIZONTAL;
        topAnchorParams.topMargin = -padding;
        topAnchor.setLayoutParams(topAnchorParams);

        FrameLayout.LayoutParams bottomAnchorParams = new FrameLayout.LayoutParams(
                anchorSize,
                anchorSize
        );
        bottomAnchorParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
        bottomAnchorParams.bottomMargin = -padding;
        bottomAnchor.setLayoutParams(bottomAnchorParams);

        FrameLayout.LayoutParams leftAnchorParams = new FrameLayout.LayoutParams(
                anchorSize,
                anchorSize
        );
        leftAnchorParams.gravity = Gravity.CENTER_VERTICAL | Gravity.LEFT;
        leftAnchorParams.leftMargin = -padding;
        leftAnchor.setLayoutParams(leftAnchorParams);

        FrameLayout.LayoutParams rightAnchorParams = new FrameLayout.LayoutParams(
                anchorSize,
                anchorSize
        );
        rightAnchorParams.gravity = Gravity.CENTER_VERTICAL | Gravity.RIGHT;
        rightAnchorParams.rightMargin = -padding;
        rightAnchor.setLayoutParams(rightAnchorParams);

        addView(imageView);
        addView(textContainer);
        addView(topAnchor);
        addView(bottomAnchor);
        addView(leftAnchor);
        addView(rightAnchor);
    }

    public int[] getTopAnchorPosition() {
        int[] position = new int[2];
        topAnchor.getLocationOnScreen(position);
        return position;
    }

    public int[] getBottomAnchorPosition() {
        int[] position = new int[2];
        bottomAnchor.getLocationOnScreen(position);
        return position;
    }

    public int[] getLeftAnchorPosition() {
        int[] position = new int[2];
        leftAnchor.getLocationOnScreen(position);
        return position;
    }

    public int[] getRightAnchorPosition() {
        int[] position = new int[2];
        rightAnchor.getLocationOnScreen(position);
        return position;
    }
}

A

PersonCard
看起来像这样:

PersonCard

重要的是

PersonCard
两侧的玫瑰点,因为我计划连接不同的
PersonCards
,我的计划是使用这些空视图(玫瑰点)作为每个
PersonCards的“锚点” 
通过另一个自定义视图进行连接。

为了拥有连接到两个

PersonCards
的东西,我创建了
ConnectionLine
视图:

public class ConnectionLine extends View {
    private Paint paint;
    private float startX, startY, endX, endY;

    public ConnectionLine(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ConnectionLine(Context context) {
        super(context);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setColor(getResources().getColor(android.R.color.black)); // Set the color to black
        paint.setStrokeWidth(5);
    }

    public void setPoints(float startX, float startY, float endX, float endY) {
        this.startX = startX;
        this.startY = startY;
        this.endX = endX;
        this.endY = endY;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawLine(startX, startY, endX, endY, paint);
    }
}

这相当简单,只允许我从 A 点到 B 点画一条线。

接下来,我在我的

PersonCards
中放置了两个
ConnectionLine
和一个
Fragment
,就像这样:

Setup

在我的

ConnectionsFragment.java
中,我有以下代码:

public class ConnectionsFragment extends Fragment {

    private PersonCard personCard1, personCard2;
    private ConnectionLine connectionLine;

    public ConnectionsFragment() {
        
    }

    public static ConnectionsFragment newInstance(String param1, String param2) {
        ConnectionsFragment fragment = new ConnectionsFragment();
        return fragment;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_connections, container, false);

        personCard1 = rootView.findViewById(R.id.personCard1);
        personCard2 = rootView.findViewById(R.id.personCard2);
        connectionLine = rootView.findViewById(R.id.connectionLine);

        personCard1.post(new Runnable() {
            @Override
            public void run() {

                int[] startLocation = personCard1.getRightAnchorPosition();

                int[] endLocation = personCard2.getLeftAnchorPosition();

                connectionLine.setPoints(startLocation[0], startLocation[1], endLocation[0], endLocation[1]);
            }
        });

        return rootView;
    }
}

我正在尝试使用

anchor
personCard1
获取右侧
 personCard1.getRightAnchorPosition();
的位置,以便将它们作为我的
ConnectionLine
的起始坐标传递。与
anchor
的左侧
personCard2
相同,我使用
personCard2.getLeftAnchorPosition();
来获取左锚点的位置。

按照我的逻辑,通过这些步骤,

ConnectionLine
应该成功连接两个
personCards
。但是,当我执行应用程序时,会发生这种情况:

按照我的逻辑,通过这些步骤,

ConnectionLine
应该成功连接两个
personCards
。但是,当我执行应用程序时,会发生这种情况:

Unexpected behaviour

如您所见,ConnectionLine 离它应该在的锚点不远。

我还尝试连接其他锚点,但它们也没有完全连接到我想要的位置,而且我已经没有想法了。从我的角度来看,

ConnectionLine
不会错,因为它只是获取我给它的坐标并连接到它们。这意味着我的坐标在某种程度上一定是错误的。此外,x 坐标似乎总是正确的,但 y 坐标始终是错误的。

有人有这方面的经验吗?或者也许有解决方案?

android android-studio coordinates android-custom-view
1个回答
0
投票

事实证明,您必须用

anchor
视图的坐标减去所需的
parent
的坐标,因为视图始终相对于其父视图。你可以使用这样的东西:

public int[] getAnchorPosition(View anchor, View parent) {
    int[] anchorPosition = new int[2];
    int[] parentPosition = new int[2];
    anchor.getLocationInWindow(anchorPosition);
    parent.getLocationInWindow(parentPosition);

    return new int[]{anchorPosition[0] - parentPosition[0] + anchor.getWidth() / 2,
            anchorPosition[1] - parentPosition[1] + anchor.getHeight() / 2};
}

然后用你想要获取坐标的

anchor
调用这个方法:

public int[] getTopAnchorPosition(View parent) {
    return getAnchorPosition(topAnchor, parent);
}
© www.soinside.com 2019 - 2024. All rights reserved.