根据屏幕尺寸调整自定义视图的大小

4

我一直在试图找到解决这个问题的方法,但也许我并没有理解Android中的"onMeasure()"方法如何工作!

这是我第一次在Android中创建"动态"自定义视图,所以我学到了需要重写"onMeasure()"方法,以便根据屏幕大小调整您的视图大小。 我希望我的视图宽度为屏幕宽度的一半,再加上一点额外的空间(这个数字是任意的,只要相对较小,在3和5之间),因此我编写了以下代码:

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    int halfWidth = (MeasureSpec.getSize(widthMeasureSpec) / 2) - 5;

    this.setMeasuredDimension((int) halfWidth, (int) halfWidth);
}

我确定这种方式做事情是很糟糕的,因为我在网上找到的每个地方都比我尝试实现类似功能的代码要多得多!
当我将这些视图并排放置在XML中时,第一个视图的大小正确(确实是屏幕宽度的一半再减少一点),但第二个视图要小得多,可能只有上一个视图宽度的一半再减少一点!(我会发布一张图片,但不幸的是我没有足够的声望这样做...)
应该发生的是无论我将它们放在XML的什么位置,这两个框都应该是相同的大小。它们应该都是屏幕宽度的一半再减去一点额外的空间。
我知道这与我使用的“setMeasuredDimension()”有关,但我不知道是什么。我真的希望有人能为我澄清一下!谢谢! :)

嗨,请看一下“layout_weight”:https://dev59.com/xG865IYBdhLWcg3wFqdV - GHz
4个回答

3

今天我通过一些实验和Tim的建议成功完成了这个任务。我离开“onMeasure()”,并只编写了如下代码:

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
    // Call the super class.
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);

    // Set the dimensions of this view.
    this.setMeasuredDimension((int) width, (int) width);
}

宽度是一个全局的浮点数。宽度是从我在视图中编写的重置方法中推导出来的:

public void initSize(float size)
{
    width = size;
    this.invalidate();
}

我调用 "invalidate()" 方法来重新绘制视图。不过,需要有某个东西来调用这个方法,所以我在 Activity 中进行了调用。首先,我使用以下代码在 Activity 而非 View 中获取屏幕的大小:

Display thisDisplay = this.getWindowManager().getDefaultDisplay();
Point desiredSize = new Point();
display.getSize(desiredSize);
int width = size.x;

您曾经可以在"thisDisplay"上调用一个名为“getWidth()”的方法,但现在该方法已被弃用,因此应改用“getSize()”。 最后,在初始化构造函数之后,调用"initSize()"方法:

nameBox = (InfoBoxView) findViewById(R.id.nameBox);
    nameBox.initSize(width);

现在,所有的视图大小将会相同。我不确定这是否是最好的方法,但是它目前可以工作,并且我想在这里发布它,让每个人都知道。:)


2

虽然这是一个老问题,但是在onMeasure方法中可以使视图变成正方形并且小于屏幕的一半,而不需要在Activity中编写任何额外的代码。您可以将其更改为生成任何其他尺寸比例。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    // get view dimensions suggested by layout
    int width = MeasureSpec.getSize(widthMeasureSpec);
    int height = MeasureSpec.getSize(heightMeasureSpec);
    // width should never be more than half of a screen
    int displayWidth = getContext().getResources().getDisplayMetrics().widthPixels;
    width = Math.min(width, displayWidth/2);
    // always make it a square (use the smaller dimension of the two)
    if (width > height) {
        width = height;
    } else {
        height = width;
    }
    // use the values
    super.onMeasure(
            MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
            MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
    );
}

1

我认为问题在于父视图的大小也已经减小,可能是根据您的说法减少了一半。因此,我建议使用以下代码:

@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
   // Prevent reducing the size of the parent view
   super.onMeasure(widthMeasureSpec, heightMeasureSpec);

   int halfWidth = (MeasureSpec.getSize(widthMeasureSpec) / 2) - 5;

   this.setMeasuredDimension((int) halfWidth, (int) halfWidth);
}

另外,您的XML布局是什么样子的?那可能也需要修复,尽管这不太可能是问题的原因。

我还没有在我的Android SDK上尝试过这个。


你能确切地解释一下在这里调用super.onMeasure会实现什么作用吗?它只会调用未被重写的onMeasure方法,设置测量的尺寸,然后返回Override函数后会立即被覆盖掉。你所描述的对我来说毫无意义,但如果你愿意可以尝试解释一下你的答案。 - Tim
我知道在onMeasure中调用父类方法可能会很复杂。但基本上,我认为这将解决原始问题“但第二个视图要小得多,可能只有最后一个视图宽度的一半...”,正如Thanizer所说的那样。总之,我认为View.onMeasure应该实现我们所有人都在思考的方式 :)然后当然,代码后面是实际期望的实现。 - The Original Android
正如Tim所说,我有点困惑这样做会有什么作用。我在SDK上尝试了一下,但它没有起到任何作用。不过还是感谢你的想法! - Thanizer

0

我认为你需要在onMeasure的文档中仔细阅读并了解其工作原理。

实际上,它根据视图所在的布局容器类型多次调用,并且具体细节因一个ViewGroup而异。通常情况下,第一次测量时尺寸最宽松,然后随着更多组件的布局变得更加精细。

当onMeasure被调用时,首先要查看您收到的MeasureSpec类型,通过调用MeasureSpec.getMode(widthMeasureSpec)来获取。这将返回MeasureSpec.AT_MOSTMeasureSpec.EXACTLY之一。

如果返回AT_MOST,则告诉您此时视图可以填充的最大可能区域。当您布置第一个视图时,它可能会说您的第一个视图最多可以使用整个屏幕的宽度,因为没有其他组件被布置。然后,通过将容器的整个宽度除以二,调用setMeasuredDimension告诉容器您希望使用水平布局的50%。

接下来,当你的第二个视图调用onMeasure时,容器的水平宽度只有一半可用,因为你已经为第一个视图请求了屏幕的一半。

然后,你将这个数字除以二,留下第二个视图只有容器水平宽度的25%,而第一个视图得到了水平宽度的50%。

我建议覆盖onMeasure不是让视图使用屏幕一半的正确方法,尽管我想解释一下它的工作原理。您是否认为您无法仅使用标准xml布局实现此目的?

编辑:再次阅读您的问题,我认为您的问题在于您假设widthMeasureSpec意味着屏幕宽度(实际上并非如此)。您可以尝试正确查询屏幕的宽度,并基于此调用setMeasureDimension,但在某些情况下可能无法正常工作。如果您不小心,您可能会请求大于基于onMeasure分配给您的宽度。或者有时您将使用MeasureSpec.EXACTLY调用onMeasure,此时您别无选择,只能使用提供的宽度或高度。


我实际上已经查过如何查询屏幕宽度,但似乎每个人都有不同的解决方案。在你看来,询问屏幕宽度的正确解决方案是什么? - Thanizer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接