【问题标题】:Get the position of each corner of a view, then add a view at the position获取一个视图每个角的位置,然后在该位置添加一个视图
【发布时间】:2018-12-02 08:19:56
【问题描述】:

我正在开发一个有自定义视图的 android 项目。单击自定义视图时,我希望在视图的每个角落放置一个视图(一个圆圈)。

目前我只是想让它在左上角工作,但它最终在中间。

下面是我添加视图的点击功能。

View view = LayoutInflater.from(getContext()).inflate(R.layout.view, this, false);

        TextView textItem = view.findViewById(R.id.lblItemText);

        textItem.setText("View: " + counter);

        view.setOnClickListener(new OnClickListener()
        {
            @Override
            public void onClick(View v)
            {

                Anchor anchor1 = new Anchor(getContext());

                anchor1.setLeft(v.getLeft());
                anchor1.setTop(CustomView.this.getTop());
                CustomView.this.addView(anchor1);
            }
        });

自定义视图托管在相对布局中。自定义视图扩展了 RelativeLayout,并且应该进入自定义视图左上角的锚视图扩展了按钮。

锚构造函数包含以下内容:

public Anchor(Context context)
    {
        super(context);
        this.setBackgroundResource(R.drawable.anchor);
        this.setPadding(0,0,0,0);
        this.setWidth(1);
        this.setHeight(1);
}

由于某种原因,锚点出现在中间而不是如下所示的角落

下面有点期待。

更新

几天后取得了一些进展,我确实让它工作了,除了它使用硬编码值将它放在正确的位置,这似乎不正确。我猜这仅适用于我正在测试的特定设备,另一台具有其他分辨率的设备将定位错误。

下面是我拥有的代码,希望能显示我想要实现的目标以及我现在拥有的屏幕截图。

private void createAnchorPoints()
    {

        //Main View
        ViewGroup mainView = activity.findViewById(android.R.id.content);

        int[] viewToBeResizedLoc = new int[2];
        viewToBeResized.getLocationOnScreen(viewToBeResizedLoc);


        //Add top left anchor
        Anchor topLeftAnchor = new Anchor(context, Anchor.ResizeMode.TOP_LEFT);
        FrameLayout.LayoutParams topLeftParms = new FrameLayout.LayoutParams(150,150);
        topLeftParms.leftMargin = viewToBeResizedLoc[0] - 50;
        topLeftParms.topMargin = viewToBeResizedLoc[1] - viewToBeResized.getHeight() - 30;
        topLeftAnchor.setLayoutParams(topLeftParms);
        mainView.addView(topLeftAnchor);

        //Add top right anchor
        Anchor topRightAnchor = new Anchor(context, Anchor.ResizeMode.TOP_RIGHT);
        FrameLayout.LayoutParams topRightParms = new FrameLayout.LayoutParams(150, 150);
        topRightParms.leftMargin = topLeftParms.leftMargin + viewToBeResized.getWidth() - 40;
        topRightParms.topMargin = topLeftParms.topMargin;
        topRightAnchor.setLayoutParams(topRightParms);
        mainView.addView(topRightAnchor);

        //Add bottom left anchor
        Anchor bottomLeftAnchor = new Anchor(context, Anchor.ResizeMode.BOTTOM_LEFT);
        FrameLayout.LayoutParams bottomLeftParms = new FrameLayout.LayoutParams(150, 150);
        bottomLeftParms.leftMargin = topLeftParms.leftMargin;
        bottomLeftParms.topMargin = topLeftParms.topMargin + viewToBeResized.getHeight() - 40;
        bottomLeftAnchor.setLayoutParams(bottomLeftParms);
        mainView.addView(bottomLeftAnchor);

        //Add bottom right anchor
        Anchor bottomRightAnchor = new Anchor(context, Anchor.ResizeMode.BOTTOM_RIGHT);
        FrameLayout.LayoutParams bottomRightParms = new FrameLayout.LayoutParams(150, 150);
        bottomRightParms.leftMargin = topRightParms.leftMargin;
        bottomRightParms.topMargin = bottomLeftParms.topMargin;
        bottomRightAnchor.setLayoutParams(bottomRightParms);
        mainView.addView(bottomRightAnchor);

    }

【问题讨论】:

  • getLeft(), getTop() 等返回视图相对于其父视图的坐标,即坐标系被“移位”。很可能您应该知道视图相对于根视图的坐标并进行计算 w.t.r.一点额外的解释是here

标签: android


【解决方案1】:

由于顶级布局是RelativeLayout,您将需要使用RelativeLayout 可用的视图定位来实现您想要的。 (见documentation。)

这是您希望在 XML 中实现的目标的模型。这个模型将展示我们如何接近实际的解决方案。我正在使用标准视图,但这没关系。该技术将应用于您的自定义视图。图片来自 Android Studio 的设计器,因此没有使用任何代码来创建图片。

activity_main.xml

<RelativeLayout 
    android:id="@+id/relativeLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/customView"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_centerInParent="true"
        android:background="@android:color/holo_green_light" />

    <ImageView
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_alignStart="@id/customView"
        android:layout_alignTop="@id/customView"
        android:src="@drawable/circle"
        android:translationX="-10dp"
        android:translationY="-10dp" />

    <ImageView
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_alignEnd="@id/customView"
        android:layout_alignTop="@id/customView"
        android:src="@drawable/circle"
        android:translationX="10dp"
        android:translationY="-10dp" />

    <ImageView
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_alignBottom="@id/customView"
        android:layout_alignStart="@id/customView"
        android:src="@drawable/circle"
        android:translationX="-10dp"
        android:translationY="10dp" />

    <ImageView
        android:layout_width="20dp"
        android:layout_height="20dp"
        android:layout_alignBottom="@id/customView"
        android:layout_alignEnd="@id/customView"
        android:src="@drawable/circle"
        android:translationX="10dp"
        android:translationY="10dp" />

</RelativeLayout>

circle.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <!-- fill color -->
    <solid android:color="@android:color/holo_red_light" />
    <size
        android:width="20dp"
        android:height="20dp" />
</shape>

实际解决方案

现在我们已经证明了模拟方法有效,现在我们必须在代码中重现该效果。我们将不得不添加圆形视图并将其定位在父 RelativeLayout 中,使用 RelativeLayout 视图定位和翻译。下面的代码只显示了左上角的位置,但其他的圆圈也以类似的方式定位。

activity_main.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Drawable circle = ContextCompat.getDrawable(this, R.drawable.circle);
        ImageView imageView = new ImageView(this);
        imageView.setImageDrawable(circle);
        int circleSize = dpToPx(CIRCLE_SIZE_DP);
        RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(circleSize, circleSize);

        // Position top left circle within the custom view.
        lp.addRule(RelativeLayout.ALIGN_START, R.id.customView);
        lp.addRule(RelativeLayout.ALIGN_TOP, R.id.customView);

        // Uncomment these 2 lines to position the top left circle with translation.
        imageView.setTranslationX(-circleSize / 2);
        imageView.setTranslationY(-circleSize / 2);

        // Uncomment these 3 lines to position the top left circle with margins.
//        View customView = findViewById(R.id.customView);
//        lp.leftMargin = customView.getLeft() - circleSize / 2;
//        lp.topMargin = customView.getTop() - circleSize / 2;

        ((RelativeLayout) findViewById(R.id.relativeLayout)).addView(imageView, lp);
    }

    private int dpToPx(int dp) {
        return (int) (dp * getResources().getDisplayMetrics().density);
    }

    private static final int CIRCLE_SIZE_DP = 20;
}

上面的代码使用了一个缩短的布局:

activity_main.xml

<RelativeLayout 
    android:id="@+id/relativeLayout"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/customView"
        android:layout_width="150dp"
        android:layout_height="150dp"
        android:layout_centerInParent="true"
        android:background="@android:color/holo_green_light" />

</RelativeLayout>

也可以使用边距产生相同的定位。使用边距的代码已被注释掉,但可以使用。 (我认为负边距也可能起作用,但我读到它们不是officially supported,所以我尽量避免它们。)

【讨论】:

  • 谢谢。我不确定这就是我所追求的。由于默认情况下不应使用角落上的锚点,这看起来像您的示例,它们将始终存在。我的计划是有一个类,它可以在构造函数中获取任何视图,然后当类实例化时,该类将在每个角添加 4 个锚点。
  • @Boardy 我没有关注你。 ImageView imageView = new ImageView(this) 行在代码中创建了锚点,因此默认情况下它不存在。唯一存在的是父 RelativeLayout 和自定义视图。给定父布局,可以添加自定义视图,因此您只需要定义父组视图,甚至可以在代码中创建。查看答案的第二部分,而不仅仅是定义锚点的第一部分。
  • 啊抱歉,我太厚了。我误读了如何在 XML 中执行此操作的初始示例,并认为这是我所追求的解决方案的一部分。这已经完成了我所追求的。感谢您的帮助
  • @Boardy 没问题。我将更新答案以使实际解决方案更加突出。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 2023-02-24
  • 2011-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多