Почему мой пользовательский вид android не квадратный?
Я создал пользовательский вид для отображения игровой доски для игры, которую я разрабатываю. Игровое поле всегда должно быть квадратным. Поэтому и высота должна быть одинаковой. Я следовал в этом уроке для достижения этой цели.
я добавил следующий код в мой пользовательский вид:
@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 () возвращают правильные значения. Как может быть, что вид еще больше?
5 ответов
по какой-то причине ваше представление не работает в RelativeLayout. Для измерения альбомной ориентации возникают следующие ограничения (на моем телефоне):
width = MeasureSpec: ровно 404; height = MeasureSpec: AT_MOST 388
из-за этого фактическая ширина заканчивается иначе, чем измеренная ширина (на моем телефоне измерено=388, фактическое=404). Вот почему вы видите фон, выходящий за пределы прямой кишки. Вы можете проверить это самостоятельно, добавив некоторые журналы в onDraw и onMeasure.
кроме того, соблюдение режима измерения не помогает, измерение по-прежнему заканчивается тем же результатом, что и выше.
решение: использовать другой макет. LinearLayout и FrameLayout работали на меня. Также неплохо использовать фактический размер представления в onDraw, с getWidth () и getHeight ().
Если вам действительно нужен 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>
подкласс 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);
}
}
Edit: я пробовал с нормальным видом
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));
}
}
он рисует квадрат красный прямоугольник на экране моего мобильного телефона!--6-->
Это мой код, я использую этот код и он работает нормально. Не совсем то, что вы ищете, но может дать вам идею. Исходя из моего требования, высота изображения должна быть такой же длины, как и ширина.
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);
я не уверен, какое решение будет служить необходимости, но код из учебника имеет фундаментальную проблему дизайна-он не интерпретирует MeasureSpec
параметры onMeasure()
правильно. Ответ в onMeasure настраиваемое представление объяснений объясняет правильное интерпрестирование этих параметров.
что происходит в этом примере onMeasure()
вызывается дважды для каждого вхождения чертежа для пользовательского представления.
для первого вызова оба параметра MeasureSpecs указывают режим AT_MOST
и дайте начальные значения без учета полей и отступов. Таким образом, код из учебника законно делает ширину и высоту, чтобы соответствовать.
для второго вызова, однако, содержащий RelativeLayout принимает поля и отступы во внимание, и проходит MeasureSpec
на Width
С EXACT
режим, в то время как 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" >
в обоих случаях имеет смысл изменить реализацию onMeasure()
в отношении EXACT
mode (для регистрации проблемы, по крайней мере).
код onMeasure()
код не является однозначным.
Измените свой код следующим образом и установите match_parent как width & height в 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);
}