Как получить полноэкранное фоновое изображение (с центральным кадрированием) без изменения размера
Я пытаюсь установить фотографию в качестве фона Activity
, Я хочу, чтобы фон был полноэкранным изображением (без границ).
Поскольку я хочу, чтобы изображение заполняло весь фон деятельности без растяжения / сжатия (т.е. непропорциональное масштабирование для X и Y), и я не против, если фотография должна быть обрезана, я использую RelativeLayout
с ImageView
(с android:scaleType="centerCrop"
) и остальная часть моего макета, состоящего из ScrollView
и его дети.
<!-- Tried this with a FrameLayout as well... -->
<RelativeLayout>
<!-- Background -->
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
<!-- Form -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView>
<LinearLayout>
...
<EditText/>
</LinearLayout>
</ScrollView>
</LinearLayout>
</RelativeLayout>
Проблема в том, что остальная часть макета имеет некоторые EditText
представления и когда программная клавиша появляется, ImageView становится изменен размер. Мне бы хотелось, чтобы фон оставался неизменным, независимо от того, видна ли программная клавиша или нет.
Я видел много вопросов о SO по поводу изменения размеров ImageViews, но (imo) нет удовлетворительных ответов. Большинство из них просто состоит из установки android:windowSoftInputMode="adjustPan"
- что не всегда практично, особенно если вы хотите, чтобы пользователь мог прокручивать упражнение, - или использование getWindow().setBackgroundDrawable()
который не обрезает изображение.
Мне удалось подкласс ImageView и переопределить его onMeasure()
(см. мой ответ здесь: ImageView изменяет размеры при открытой клавиатуре), чтобы он мог принудительно устанавливать фиксированную высоту и ширину - равную размерам экрана устройства - в соответствии с флагом, но мне интересно, есть ли лучший способ достижения желаемого результата,
Итак, подведем итог, мой вопрос: как я могу установить фон деятельности в качестве полноэкранного фото
со шкалой type = "centerCrop", чтобы фотография масштабировалась равномерно (с сохранением соотношения сторон), и поэтому оба размера (ширина и высота) будут равны или больше соответствующего размера вида;
он не изменяется при всплывающей программной клавиатуре;
ОТВЕТ:
В итоге я последовал pskink и получил подкласс BitmapDrawable
(см. его ответ ниже). Я должен был сделать некоторые корректировки, чтобы убедиться, что BackgroundBitmapDrawable
всегда масштабируется и обрезается таким образом, чтобы заполнить экран.
Вот мой последний класс, адаптированный из его ответа:
public class BackgroundBitmapDrawable extends BitmapDrawable {
private Matrix mMatrix = new Matrix();
private int moldHeight;
boolean simpleMapping = false;
public BackgroundBitmapDrawable(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
@Override
protected void onBoundsChange(Rect bounds) {
if (bounds.height() > moldHeight) {
moldHeight = bounds.height();
Bitmap b = getBitmap();
RectF src = new RectF(0, 0, b.getWidth(), b.getHeight());
RectF dst;
if (simpleMapping) {
dst = new RectF(bounds);
mMatrix.setRectToRect(src, dst, ScaleToFit.CENTER);
} else {
// Full Screen Image -> Always scale and center-crop in order to fill the screen
float dwidth = src.width();
float dheight = src.height();
float vwidth = bounds.width();
float vheight = bounds.height();
float scale;
float dx = 0, dy = 0;
if (dwidth * vheight > vwidth * dheight) {
scale = (float) vheight / (float) dheight;
dx = (vwidth - dwidth * scale) * 0.5f;
} else {
scale = (float) vwidth / (float) dwidth;
dy = (vheight - dheight * scale) * 0.5f;
}
mMatrix.setScale(scale, scale);
mMatrix.postTranslate(dx, dy);
}
}
}
@Override
public void draw(Canvas canvas) {
canvas.drawColor(0xaa00ff00);
canvas.drawBitmap(getBitmap(), mMatrix, null);
}
}
Тогда это просто вопрос создания BackgroundBitmapDrawable
и установив его в качестве фона корневого представления.
6 ответов
Попробуйте этот LayerDrawable (res/drawable/backlayer.xml):
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"
>
<item>
<shape>
<solid android:color="#f00" />
</shape>
</item>
<item>
<bitmap
android:gravity="top" android:src="@drawable/back1">
</bitmap>
</item>
</layer-list>
и установите его на макет верхнего уровня: android:background="@drawable/backlayer"
ОБНОВЛЕНИЕ: попробуйте этот BitmapDrawadle, установите его в макет верхнего уровня (setBackgroundDrawable()), если simpleMapping == true достаточно хорошо, вы можете удалить ветвь else:
class D extends BitmapDrawable {
private Matrix mMatrix = new Matrix();
private int moldHeight;
public D(Resources res, Bitmap bitmap) {
super(res, bitmap);
}
@Override
protected void onBoundsChange(Rect bounds) {
if (bounds.height() > moldHeight) {
moldHeight = bounds.height();
Bitmap b = getBitmap();
RectF src = new RectF(0, 0, b.getWidth(), b.getHeight());
RectF dst;
// if simpleMapping is good enough then remove "else" branch and
// declare "dst" as:
// RectF dst = new RectF(bounds);
boolean simpleMapping = true;
if (simpleMapping) {
dst = new RectF(bounds);
} else {
float x = bounds.exactCenterX();
dst = new RectF(x, 0, x, bounds.height());
float scale = bounds.height() / src.height();
float dx = scale * src.width() / 2;
dst.inset(-dx, 0);
}
mMatrix.setRectToRect(src, dst, ScaleToFit.CENTER);
}
}
@Override
public void draw(Canvas canvas) {
canvas.drawColor(0xaa00ff00);
canvas.drawBitmap(getBitmap(), mMatrix, null);
}
}
Решение в ответе является лучшим, чтобы использовать это:
Resources res = getActivity().getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res, R.drawable.some_image);
BackgroundBitmapDrawable background = new BackgroundBitmapDrawable(res, bitmap);
view.setBackgroundDrawable(background);
или же
view.setBackground(background);
для API 16 и выше.
После того, как я почесал голову некоторое время, это было моим "хорошим решением".
Совместное использование примера XML, который может быть полезен для всех.
<!-- RelativeLayout so that Views can overlap each other.
I believe a FrameLayout would work as well. -->
<RelativeLayout
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<!-- I've put the ImageView inside
a ScrollView with android:fillViewport="true" and android:isScrollContainer="false" -->
<ScrollView
android:id="@+id/backgroundScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:isScrollContainer="false" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<!-- The FullScreen, Center-Cropped Image Background -->
<ImageView
android:id="@+id/backgroundImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/background_image" />
</LinearLayout>
</ScrollView>
<!-- The rest of the Views, containing the data entry form... -->
<LinearLayout
android:id="@+id/form"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<ScrollView
android:id="@+id/formScrollView"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:orientation="vertical" >
<!-- The "Form", with EditTexts, Checkboxes and whatnot... -->
</LinearLayout>
</ScrollView>
</LinearLayout>
</RelativeLayout>
С помощью этого макета я выполняю две вещи:
formScrollView
, который содержит "форму" с EditTexts и т. д., изменяет размеры (как я хочу) и прокручивается, когда отображается программная клавиша.backgroundScrollView
, который содержит ImageView, не масштабируется (из-заandroid:isScrollContainer="false"
) и заполняет весь видовой экран (android:fillViewport="true"
). Таким образом, фон остается неизменным независимо от того, видна клавиатура или нет.
Не идеальное решение, как ImageView
как-то страдает очень небольшое изменение в его размере, но результат пока достаточно хорош. Если кто-то знает лучшее решение, пожалуйста, дайте мне знать.
Поместите все в FrameLayout, с первым дочерним изображением, которое будет соответствовать его родителю (frameLayout), и вы настроите его так, как хотите, чтобы ваш фон, а перед представлением изображения вы поместите все, что хотите (LinearLayout, я полагаю)
Измените свой ScaleType
android:scaleType="FitXY"
и в вашем манифесте
<activity
android:name=".activity.MainActivity"
android:windowSoftInputMode="adjustPan|stateHidden" />
Создайте ImageView в вашем XML. Поместите его ниже тега Layout. Установите для перевода Z положительное значение, скажем, 10dp, а scaleType - centerCrop.