Фиксированное соотношение сторон
Как я могу реализовать фиксированное соотношение сторон View
? Я хотел бы иметь элементы с соотношением сторон 1:1 в GridView
. Я думаю, что лучше подклассифицировать детей, чем GridView
?
EDIT: Я предполагаю, что это нужно делать программно, без проблем. Кроме того, я не хочу ограничивать размер, только соотношение сторон.
Ответы
Ответ 1
Я реализовал FixedAspectRatioFrameLayout, поэтому я могу повторно использовать его и иметь любое размещенное представление с фиксированным соотношением сторон:
public class FixedAspectRatioFrameLayout extends FrameLayout
{
private int mAspectRatioWidth;
private int mAspectRatioHeight;
public FixedAspectRatioFrameLayout(Context context)
{
super(context);
}
public FixedAspectRatioFrameLayout(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context, attrs);
}
public FixedAspectRatioFrameLayout(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs)
{
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.FixedAspectRatioFrameLayout);
mAspectRatioWidth = a.getInt(R.styleable.FixedAspectRatioFrameLayout_aspectRatioWidth, 4);
mAspectRatioHeight = a.getInt(R.styleable.FixedAspectRatioFrameLayout_aspectRatioHeight, 3);
a.recycle();
}
// **overrides**
@Override protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec)
{
int originalWidth = MeasureSpec.getSize(widthMeasureSpec);
int originalHeight = MeasureSpec.getSize(heightMeasureSpec);
int calculatedHeight = originalWidth * mAspectRatioHeight / mAspectRatioWidth;
int finalWidth, finalHeight;
if (calculatedHeight > originalHeight)
{
finalWidth = originalHeight * mAspectRatioWidth / mAspectRatioHeight;
finalHeight = originalHeight;
}
else
{
finalWidth = originalWidth;
finalHeight = calculatedHeight;
}
super.onMeasure(
MeasureSpec.makeMeasureSpec(finalWidth, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(finalHeight, MeasureSpec.EXACTLY));
}
}
Ответ 2
Для новых пользователей здесь лучшее решение без кода:
В Android SDK v22 доступна новая библиотека поддержки Библиотека поддержки процентов (MinAPI - это 7 мне кажется, не уверен):
src: android-developers.blogspot.in
Библиотека поддержки процентов предоставляет процентные размеры и поля, а также новые для этой версии возможность установки пользовательского формата с помощью приложения: aspectRatio. Установив только одну ширину или высоту и используя aspectRatio, PercentFrameLayout или PercentRelativeLayout автоматически отрегулируют другое измерение, чтобы макет использовал заданное соотношение сторон.
Чтобы добавить это в свой build.gradle:
compile 'com.android.support:percent:23.1.1'
Теперь сверните свой вид (тот, который должен быть квадратным) с PercentRelativeLayout
/PercentFrameLayout
:
<android.support.percent.PercentRelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
app:layout_aspectRatio="100%"
app:layout_widthPercent="100%"/>
</android.support.percent.PercentRelativeLayout>
Здесь вы можете увидеть пример .
Ответ 3
Чтобы не использовать стороннее решение и учитывая тот факт, что PercentFrameLayout и PercentRelativeLayout устарели в 26.0.0, я бы посоветовал вам рассмотреть возможность использования ConstraintLayout в качестве корневого макета для элементов сетки.
Ваш item_grid.xml
может выглядеть так:
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/imageview_item"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="centerCrop"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintDimensionRatio="H,1:1" />
</android.support.constraint.ConstraintLayout>
В результате вы получите что-то вроде этого:
![Fixed ratio sized grid items]()
Ответ 4
Недавно я сделал вспомогательный класс для этой самой проблемы и написал сообщение в блоге об этом.
Мяч кода выглядит следующим образом:
/**
* Measure with a specific aspect ratio<br />
* <br />
* @param widthMeasureSpec The width <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method
* @param heightMeasureSpec The height <tt>MeasureSpec</tt> passed in your <tt>View.onMeasure()</tt> method
* @param aspectRatio The aspect ratio to calculate measurements in respect to
*/
public void measure(int widthMeasureSpec, int heightMeasureSpec, double aspectRatio) {
int widthMode = MeasureSpec.getMode( widthMeasureSpec );
int widthSize = widthMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : MeasureSpec.getSize( widthMeasureSpec );
int heightMode = MeasureSpec.getMode( heightMeasureSpec );
int heightSize = heightMode == MeasureSpec.UNSPECIFIED ? Integer.MAX_VALUE : MeasureSpec.getSize( heightMeasureSpec );
if ( heightMode == MeasureSpec.EXACTLY && widthMode == MeasureSpec.EXACTLY ) {
/*
* Possibility 1: Both width and height fixed
*/
measuredWidth = widthSize;
measuredHeight = heightSize;
} else if ( heightMode == MeasureSpec.EXACTLY ) {
/*
* Possibility 2: Width dynamic, height fixed
*/
measuredWidth = (int) Math.min( widthSize, heightSize * aspectRatio );
measuredHeight = (int) (measuredWidth / aspectRatio);
} else if ( widthMode == MeasureSpec.EXACTLY ) {
/*
* Possibility 3: Width fixed, height dynamic
*/
measuredHeight = (int) Math.min( heightSize, widthSize / aspectRatio );
measuredWidth = (int) (measuredHeight * aspectRatio);
} else {
/*
* Possibility 4: Both width and height dynamic
*/
if ( widthSize > heightSize * aspectRatio ) {
measuredHeight = heightSize;
measuredWidth = (int)( measuredHeight * aspectRatio );
} else {
measuredWidth = widthSize;
measuredHeight = (int) (measuredWidth / aspectRatio);
}
}
}
Ответ 5
Я использовал и использовал реализацию Jake Wharton ImageView
(должен идти аналогично для других представлений), другие могут также наслаждаться этим:
AspectRatioImageView.java - ImageView, который учитывает соотношение сторон, применяемое к определенному измерению
Приятная вещь, которую она уже умеет в xml уже.
Ответ 6
Я создал библиотеку макетов, используя ответ TalL. Не стесняйтесь использовать его.
RatioLayouts
Монтаж
Добавьте это в начало файла
repositories {
maven {
url "http://dl.bintray.com/riteshakya037/maven"
}
}
dependencies {
compile 'com.ritesh:ratiolayout:1.0.0'
}
использование
Определите пространство имен "app" в корневом представлении в макете
xmlns:app="http://schemas.android.com/apk/res-auto"
Включить эту библиотеку в свой макет
<com.ritesh.ratiolayout.RatioRelativeLayout
android:id="@+id/activity_main_ratio_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fixed_attribute="WIDTH" // Fix one side of the layout
app:horizontal_ratio="2" // ratio of 2:3
app:vertical_ratio="3">
Обновить
С введением ConstraintLayout вам не нужно писать ни единой строки кода, ни использовать сторонних разработчиков, ни полагаться на PercentFrameLayout
который устарел в 26.0.0.
Вот пример того, как сохранить соотношение сторон 1:1 для вашего макета, используя ConstraintLayout
:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginEnd="0dp"
android:layout_marginStart="0dp"
android:layout_marginTop="0dp"
android:background="@android:color/black"
app:layout_constraintDimensionRatio="H,1:1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
</LinearLayout>
</android.support.constraint.ConstraintLayout>
Ответ 7
Просто переопределите onSizeChanged
и вычислите соотношение там.
Формула для соотношения сторон:
newHeight = original_height / original_width x new_width
это даст вам что-то вроде этого:
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
//3:5 ratio
float RATIO = 5/3;
setLayoutParams(new LayoutParams((int)RATIO * w, w));
}
надеюсь, что это поможет!
Ответ 8
Это плагин JQuery, который помогает с соотношением сторон: https://github.com/eissasoubhi/aspectratio.js
Ответ 9
ExoPlayer от Google поставляется с AspectRatioFrameLayout
который вы используете следующим образом:
<com.google.android.exoplayer2.ui.AspectRatioFrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:resize_mode="fixed_width">
<!-- https://exoplayer.dev/doc/reference/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.html#RESIZE_MODE_FIXED_WIDTH -->
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.google.android.exoplayer2.ui.AspectRatioFrameLayout>
Затем вы должны установить соотношение сторон программно:
aspectRatioFrameLayout.setAspectRatio(16f/9f)
Обратите внимание, что вы также можете установить режим изменения размера программно с помощью setResizeMode
.
Поскольку вы, очевидно, не собираетесь собирать всю библиотеку ExoPlayer для этого единственного класса, вы можете просто скопировать и вставить файл из GitHub в ваш проект (с открытым исходным кодом):
https://github.com/google/ExoPlayer/blob/release-v2/library/ui/src/main/java/com/google/android/exoplayer2/ui/AspectRatioFrameLayout.java
Не забудьте также захватить атрибут resize_mode
:
https://github.com/google/ExoPlayer/blob/release-v2/library/ui/src/main/res/values/attrs.xml#L18-L25
<attr name="resize_mode" format="enum">
<enum name="fit" value="0"/>
<enum name="fixed_width" value="1"/>
<enum name="fixed_height" value="2"/>
<enum name="fill" value="3"/>
<enum name="zoom" value="4"/>
</attr>