我创建了一个自定义视图来显示我正在开发的游戏的游戏板。游戏板必须始终是一个正方形。因此,和高度应该是一样的。我跟着this tutorial来实现这个目标。
我将以下代码添加到自定义视图中:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int height = getMeasuredHeight();
int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom();
int maxWidth = (int) (heigthWithoutPadding * RATIO);
int maxHeight = (int) (widthWithoutPadding / RATIO);
if (widthWithoutPadding > maxWidth) {
width = maxWidth + getPaddingLeft() + getPaddingRight();
} else {
height = maxHeight + getPaddingTop() + getPaddingBottom();
}
setMeasuredDimension(width, height);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint p = new Paint();
p.setStyle(Paint.Style.STROKE);
p.setStrokeWidth(3);
canvas.drawRect(0, 0, getMeasuredWidth() - 1, getMeasuredHeight() - 1, p);
}
我的自定义视图的布局如下所示:
<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity">
<view
android:layout_width="wrap_content"
android:layout_height="wrap_content"
class="com.peerkesoftware.nanograms.controls.GameBoard"
android:id="@+id/view"
android:background="@android:color/holo_purple"/>
</RelativeLayout>
onDraw方法中绘制的正方形始终为正方形。这很好。
在布局中,我添加自定义视图并为视图提供背景颜色。当我以纵向模式在设备上显示它时,一切正常,背景颜色填满了我在onDraw方法中绘制的方块。
当我将设备切换到横向模式时,方块仍然是正方形,但背景颜色的区域比那个大。它的高度相同,但宽度更大。但我不明白为什么。 getMeasuredWidth()和getMeasuredHeight()方法返回正确的值。怎么可能这个观点还要大呢?
答案 0 :(得分:5)
出于某种原因,您的观点在RelativeLayout中不起作用。对于横向测量,最终会出现以下约束(在我的手机上):
width = MeasureSpec:完全404; height = MeasureSpec:AT_MOST 388
由于这个实际宽度最终不同于测量宽度(在我的手机上测量= 388,实际= 404)。这就是为什么你看到背景延伸到你的矩形之外的原因。您可以通过向onDraw和onMeasure添加一些日志来自行检查。
此外,尊重测量模式并没有帮助,测量结果与上述结果相同。
解决方案是使用不同的布局。 LinearLayout和FrameLayout对我有用。 使用getWidth()和getHeight()在onDraw中使用实际视图大小也是一个好主意。
如果你真的需要RelativeLayout,你可以添加一个子FrameLayout来保存你的视图:
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent" >
<view
android:id="@+id/view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
class="com.peerkesoftware.nanograms.controls.GameBoard"
android:background="@android:color/holo_purple" />
</FrameLayout>
</RelativeLayout>
答案 1 :(得分:1)
Subclass ImageView并覆盖onMeasure
并传递给
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
全班:
public class SquaredImageView extends ImageView {
public SquaredImageView(Context context) {
super(context);
}
public SquaredImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquaredImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, widthMeasureSpec);
}
}
编辑:我尝试了普通视图
public class CustomView extends View {
public CustomView(Context context) {
super(context);
setBackgroundColor(Color.RED);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = getMeasuredWidth();
int heigth = getMeasuredHeight();
int dimen = (width > heigth) ? heigth : width;
setMeasuredDimension(dimen, dimen);
}
}
和TestActivity
public class TestActivity extends Activity {
protected void onCreate(android.os.Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new CustomView(this));
}
}
它在我的手机屏幕上绘制了一个方形的红色矩形
答案 2 :(得分:0)
这是我的代码,我正在使用此代码,它工作正常。 不是你正在寻找的东西,但可能会给你一个想法。 根据我的要求,图像视图的高度应与宽度相同。
public class SquareImageView extends ImageView {
private static final String TAG = "SquareImageView";
public SquareImageView(Context context) {
super(context);
}
public SquareImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SquareImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//int h = this.getMeasuredHeight();
int w = this.getMeasuredWidth();
setMeasuredDimension(w, w);
}
}
在xml中定义为:
<com.something.widget.SquareImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/ivProfilePicture"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:scaleType="fitXY"
android:background="@drawable/bg_curve_white_2"
android:contentDescription="@string/general_content_description"
android:src="@drawable/ic_default_logo"
android:layout_marginTop="@dimen/side_margin"/>
最后在片段中,它就像是:
SquareImageView ivProfilePicture = (SquareImageView) view.findViewById(R.id.ivProfilePicture);
答案 3 :(得分:0)
我不确定哪种解决方案可以满足需求,但教程中的代码存在基本的设计问题 - 它无法正确解释MeasureSpec
的{{1}}参数。 onMeasure custom view explanation中的答案解释了这些参数的正确插入。
在此示例中,对于自定义视图的每次绘制,都会调用onMeasure()
两次。
对于第一次调用,两个MeasureSpecs都指定模式onMeasure()
并给出初始值而不考虑边距和填充。因此,教程中的代码合法地使宽度和高度匹配。
但是,对于第二个调用,包含RelativeLayout的内容会考虑边距和填充,并使用AT_MOST
模式传递MeasureSpec
,Width
传递EXACT
{1}}模式。
对于垂直屏幕,这可以正常工作,因为Height
减少了边距,而高度值仍然是屏幕的高度(也减少了边距)。因此,在第二次调用代码期间合法地减少高度以匹配现在更小的宽度,并且一切正常。
但是,对于水平屏幕方向,“宽度”设置为第一次调用时定义的值,而AT_MOST
则按边距减少。当代码尝试将Width
设置为与Height
匹配时,此设置操作无法正常运行,因为Width
模式为Height
。因此,视图Width
和EXACT
的属性最终会显示不同的值。
有几种简单的方法可以使代码工作:你可以简单地删除垂直边距:
Width
或者,如果边距很重要,您可以将包含布局更改为绝对:
MeasuredWidth
在这两种情况下,将<RelativeLayout 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:paddingBottom="@dimen/zero"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/zero"
tools:context=".MainActivity" >
的实现更改为尊重<AbsoluteLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
模式(至少记录问题)似乎是有意义的。
答案 4 :(得分:0)
您的onMeasure()
代码并不简单。
更改您的代码如下&amp;将match_parent设置为width&amp; xml的高度(实际上它不重要)
void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
int widthWithoutPadding = width - getPaddingLeft() - getPaddingRight();
int heigthWithoutPadding = height - getPaddingTop() - getPaddingBottom();
int maxWidth = (int) (heigthWithoutPadding * RATIO);
int maxHeight = (int) (widthWithoutPadding / RATIO);
if (widthWithoutPadding > maxWidth) {
width = maxWidth + getPaddingLeft() + getPaddingRight();
} else {
height = maxHeight + getPaddingTop() + getPaddingBottom();
}
setMeasuredDimension(width, height);
}
答案 5 :(得分:0)
这个简单的代码(类似于经过修正的Blackbelt代码)在LinearLayout和FrameLayout中对我有用:
protected int minNotZero(int valueA, int valueB)
{
if (valueA != 0)
if (valueB != 0)
return Math.min(valueA, valueB);
else
return valueA;
else
return valueB;
}
@Override
protected void onMeasure(int measureSpecW, int measureSpecH)
{
super.onMeasure(measureSpecW, measureSpecH);
int width = this.getMeasuredWidth();
int heigth = this.getMeasuredHeight();
int dimen = this.minNotZero(width, heigth);
this.setMeasuredDimension(dimen, dimen);
}