【问题标题】:Android: Scale a Drawable or background image?Android:缩放可绘制或背景图像?
【发布时间】:2009-09-09 16:46:19
【问题描述】:

在布局上,我想将背景图像(保持其纵横比)缩放到创建页面时分配的空间。有谁知道如何做到这一点?

我正在使用layout.setBackgroundDrawable()BitmapDrawable() 设置gravity 进行剪切和填充,但没有看到任何缩放选项。

【问题讨论】:

  • 我遇到了这个问题,并按照 Dweebo 的建议进行了 Anke 建议的更改:将 fitXY 更改为 fitCenter。效果很好。

标签: android


【解决方案1】:

要自定义背景图像缩放,请创建如下资源:

<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:gravity="center"
    android:src="@drawable/list_bkgnd" />

如果用作背景,它将在视图中居中。还有其他标志:http://developer.android.com/guide/topics/resources/drawable-resource.html

【讨论】:

  • 我尝试使用位图,但图像只是居中,而不是按照 Sam 的要求进行缩放。当我使用 ImageView 时,它工作得很好,没有麻烦/大惊小怪。 (注:在我的情况下,我也没有滚动。)为了缩放图像,我会用什么来增加位图?
  • 可能的值为:"top" | “底部” | "左" | “对” | “中心垂直” | “填充垂直” | "center_horizo​​ntal" | “填充水平” | “中心” | “填” | “clip_vertical” | “剪辑水平”
  • 这些参数都不能通过保持纵横比来缩放位图,所以答案是错误的。
  • 亲爱的 Malachiasz,'center' 确实尊重纵横比。但它不会缩小图像。这意味着使用它是一个有点危险的功能。根据目标的分辨率,您永远不知道物体的精确尺寸。谷歌的另一个半途而废的解决方案。
  • 有办法添加 scaleType 属性吗?
【解决方案2】:

尚未尝试完全按照您的意愿进行操作,但您可以使用 android:scaleType="fitXY" 缩放 ImageView
它的大小将适合您为 ImageView 提供的任何大小。

因此,您可以为您的布局创建一个 FrameLayout,将 ImageView 放入其中,然后在 FrameLayout 中添加您需要的任何其他内容以及透明背景。

<FrameLayout  
android:layout_width="fill_parent" android:layout_height="fill_parent">

  <ImageView 
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:src="@drawable/back" android:scaleType="fitXY" />

  <LinearLayout>your views</LinearLayout>
</FrameLayout>

【讨论】:

  • 是否可以在 Surface 上执行此操作。如果没有,我可以使用 FrameLayout 显示预览(对于记录器应用程序)吗?
  • @dweebo:你试过这个吗?它似乎对我不起作用。
  • 被否决,因为使用两个 UI 组件而不是一个是一个坏主意,尤其是对于像 ListView 这样繁忙的组件。滚动时很可能会遇到麻烦。正确的解决方案:stackoverflow.com/a/9362168/145046
  • 我尝试了 fitCenter 和 centerCrop 的 scaleType (以及不同密度的可绘制/图像的一些变体),它奏效了!任何一种比例类型对我来说都是有意义的——我怀疑更多的是基于图像的个人偏好。请注意,我使用了合并而不是 FrameLayout,并且在我的特定情况下没有滚动。
  • centerInside 是正确的,不是 fitXY,因为 fitXY 不保持图像的纵横比
【解决方案3】:

有一种简单的方法可以从可绘制对象中做到这一点:

your_drawable.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <item android:drawable="@color/bg_color"/>
    <item>
        <bitmap
            android:gravity="center|bottom|clip_vertical"
            android:src="@drawable/your_image" />
    </item>

</layer-list>

唯一的缺点是如果没有足够的空间,您的图像将不会完全显示,但会被剪裁,我找不到直接从可绘制对象中执行此操作的方法。但从我所做的测试来看,它工作得很好,而且它并没有剪掉那么多的图像。您可以更多地使用重力选项。

另一种方法是创建一个布局,您将在其中使用 ImageView 并将 scaleType 设置为 fitCenter强>。

【讨论】:

  • 这个解决方案绝对是我长期以来一直在寻找的解决方案,以替换我的主题的背景属性,并在视图是否有选项卡时避免图像缩放不同。感谢分享。
  • 显示错误:error: &lt;item&gt; must have a 'name' attribute.
【解决方案4】:

使用图像作为背景大小来布局:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" >

    <ImageView
        android:id="@+id/imgPlaylistItemBg"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:adjustViewBounds="true"
        android:maxHeight="0dp"
        android:scaleType="fitXY"
        android:src="@drawable/img_dsh" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >


    </LinearLayout>

</FrameLayout>

【讨论】:

  • 谢谢! android:scaleType="centerCrop" 也有效。
【解决方案5】:

要保持纵横比,您必须使用android:scaleType=fitCenterfitStart 等。使用fitXY 将不会保持图像的原始纵横比!

请注意,这仅适用于具有src 属性的图像,不适用于背景图像。

【讨论】:

    【解决方案6】:

    当您使用setBackgroundDrawable 方法设置ImageView 的Drawable 时,图像将始终被缩放。 adjustViewBounds 或不同 ScaleTypes 的参数将被忽略。我发现保持纵横比的唯一解决方案是在加载您的可绘制对象后调整ImageView 的大小。这是我使用的代码sn-p:

    // bmp is your Bitmap object
    int imgHeight = bmp.getHeight();
    int imgWidth = bmp.getWidth();
    int containerHeight = imageView.getHeight();
    int containerWidth = imageView.getWidth();
    boolean ch2cw = containerHeight > containerWidth;
    float h2w = (float) imgHeight / (float) imgWidth;
    float newContainerHeight, newContainerWidth;
    
    if (h2w > 1) {
        // height is greater than width
        if (ch2cw) {
            newContainerWidth = (float) containerWidth;
            newContainerHeight = newContainerWidth * h2w;
        } else {
            newContainerHeight = (float) containerHeight;
            newContainerWidth = newContainerHeight / h2w;
        }
    } else {
        // width is greater than height
        if (ch2cw) {
            newContainerWidth = (float) containerWidth;
            newContainerHeight = newContainerWidth / h2w; 
        } else {
            newContainerWidth = (float) containerHeight;
            newContainerHeight = newContainerWidth * h2w;       
        }
    }
    Bitmap copy = Bitmap.createScaledBitmap(bmp, (int) newContainerWidth, (int) newContainerHeight, false);
    imageView.setBackgroundDrawable(new BitmapDrawable(copy));
    LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
    imageView.setLayoutParams(params);
    imageView.setMaxHeight((int) newContainerHeight);
    imageView.setMaxWidth((int) newContainerWidth);
    

    在上面的代码中,sn-p 是 bmp 要显示的 Bitmap 对象,imageViewImageView 对象

    需要注意的重要一点是布局参数的变化。这是必要的,因为setMaxHeightsetMaxWidth 只有在定义宽度和高度以包裹内容而不是填充父对象时才会有所不同。另一方面,填充父项是开始时所需的设置,因为否则containerWidthcontainerHeight 的值都等于0。 所以,在你的布局文件中,你的 ImageView 会有这样的东西:

    ...
    <ImageView android:id="@+id/my_image_view"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"/>
    ...
    

    【讨论】:

      【解决方案7】:

      这不是最高效的解决方案,但正如有人建议的那样,您可以创建 FrameLayout 或 RelativeLayout 并将 ImageView 用作伪背景 - 其他元素将位于其上方:

      <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
          android:layout_height="match_parent"
          android:layout_width="match_parent">
      
          <ImageView
              android:id="@+id/ivBackground"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:layout_alignParentLeft="true"
              android:layout_alignParentTop="true"
              android:scaleType="fitStart"
              android:src="@drawable/menu_icon_exit" />
      
          <Button
              android:id="@+id/bSomeButton"
              android:layout_width="wrap_content"
              android:layout_height="wrap_content"
              android:layout_alignParentLeft="true"
              android:layout_alignParentTop="true"
              android:layout_marginLeft="61dp"
              android:layout_marginTop="122dp"
              android:text="Button" />
      
      </RelativeLayout>
      

      ImageView 的问题是只有 scaleTypes 可用: CENTER, CENTER_CROP, CENTER_INSIDE, FIT_CENTER,FIT_END, FIT_START, FIT_XY, MATRIX (http://etcodehome.blogspot.de/2011/05/android-imageview-scaletype-samples.html)

      并在某些情况下“缩放背景图像(保持其纵横比)”,当您希望图像填满整个屏幕(例如背景图像)并且屏幕的纵横比与图像不同时,必要scaleType 是一种 TOP_CROP,因为:

      CENTER_CROP 将缩放的图像居中,而不是将顶部边缘与图像视图的顶部边缘对齐,并且 FIT_START 适合屏幕高度而不是填充宽度。正如用户 Anke 注意到的那样,FIT_XY 没有保持纵横比。

      很高兴有人扩展了 ImageView 以支持 TOP_CROP

      public class ImageViewScaleTypeTopCrop extends ImageView {
          public ImageViewScaleTypeTopCrop(Context context) {
              super(context);
              setup();
          }
      
          public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs) {
              super(context, attrs);
              setup();
          }
      
          public ImageViewScaleTypeTopCrop(Context context, AttributeSet attrs, int defStyle) {
              super(context, attrs, defStyle);
              setup();
          }
      
          private void setup() {
              setScaleType(ScaleType.MATRIX);
          }
      
          @Override
          protected boolean setFrame(int frameLeft, int frameTop, int frameRight, int frameBottom) {
      
              float frameWidth = frameRight - frameLeft;
              float frameHeight = frameBottom - frameTop;
      
              if (getDrawable() != null) {
      
                  Matrix matrix = getImageMatrix();
                  float scaleFactor, scaleFactorWidth, scaleFactorHeight;
      
                  scaleFactorWidth = (float) frameWidth / (float) getDrawable().getIntrinsicWidth();
                  scaleFactorHeight = (float) frameHeight / (float) getDrawable().getIntrinsicHeight();
      
                  if (scaleFactorHeight > scaleFactorWidth) {
                      scaleFactor = scaleFactorHeight;
                  } else {
                      scaleFactor = scaleFactorWidth;
                  }
      
                  matrix.setScale(scaleFactor, scaleFactor, 0, 0);
                  setImageMatrix(matrix);
              }
      
              return super.setFrame(frameLeft, frameTop, frameRight, frameBottom);
          }
      
      }
      

      https://stackoverflow.com/a/14815588/2075875

      现在恕我直言,如果有人编写自定义 Drawable 来像这样缩放图像,那将是完美的。然后它可以用作背景参数。


      Reflog 建议在使用前对 drawable 进行预缩放。以下是如何执行此操作的说明: Java (Android): How to scale a drawable without Bitmap? 虽然它有缺点,但升级后的可绘制/位图将使用更多 RAM,而 ImageView 使用的动态缩放不需要更多内存。优势可能是处理器负载更少。

      【讨论】:

      • 不错的一个!完全符合我的要求:通过按比例增加宽度并裁剪图像底部来填充整个屏幕的背景图像。
      【解决方案8】:

      下面的代码使位图完美地与图像视图的大小相同。获取位图图像的高度和宽度,然后借助 imageview 的参数计算新的高度和宽度。这将为您提供具有最佳纵横比的所需图像。

      int bwidth=bitMap1.getWidth();
      int bheight=bitMap1.getHeight();
      int swidth=imageView_location.getWidth();
      int sheight=imageView_location.getHeight();
      new_width=swidth;
      new_height = (int) Math.floor((double) bheight *( (double) new_width / (double) bwidth));
      Bitmap newbitMap = Bitmap.createScaledBitmap(bitMap1,new_width,new_height, true);
      imageView_location.setImageBitmap(newbitMap)
      

      【讨论】:

        【解决方案9】:

        Dweebo 建议的工作。但在我的拙见中,这是不必要的。 背景可绘制对象本身可以很好地缩放。视图应具有固定的宽度和高度,如下例所示:

         < RelativeLayout 
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:background="@android:color/black">
        
            <LinearLayout
                android:layout_width="500dip"
                android:layout_height="450dip"
                android:layout_centerInParent="true"
                android:background="@drawable/my_drawable"
                android:orientation="vertical"
                android:padding="30dip"
                >
                ...
             </LinearLayout>
         < / RelativeLayout>
        

        【讨论】:

        • 我的理解是背景会扩大以填充视图,但不会缩小。
        【解决方案10】:

        尝试的一种选择是将图像放在drawable-nodpi 文件夹中,并将布局的背景设置为可绘制资源ID。

        这绝对适用于缩小,不过我还没有测试过放大。

        【讨论】:

          【解决方案11】:

          科特林:

          如果需要在 View 中绘制位图,缩放到 FIT。

          如果 bm 宽高比小于容器宽高比,或者相反的情况下,您可以进行适当的计算以将 bm 设置为等于容器的高度并调整宽度。

          图片:

          // binding.fragPhotoEditDrawCont is the RelativeLayout where is your view
          // bm is the Bitmap
          val ch = binding.fragPhotoEditDrawCont.height
          val cw = binding.fragPhotoEditDrawCont.width
          val bh = bm.height
          val bw = bm.width
          val rc = cw.toFloat() / ch.toFloat()
          val rb = bw.toFloat() / bh.toFloat()
          
          if (rb < rc) {
              // Bitmap Width to Height ratio is less than Container ratio
              // Means, bitmap should pin top and bottom, and have some space on sides.
              //              _____          ___
              // container = |_____|   bm = |___| 
              val bmHeight = ch - 4 //4 for container border
              val bmWidth = rb * bmHeight //new width is bm_ratio * bm_height
              binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth.toInt(), bmHeight)
          }
          else {
              val bmWidth = cw - 4 //4 for container border
              val bmHeight = 1f/rb * cw
              binding.fragPhotoEditDraw.layoutParams = RelativeLayout.LayoutParams(bmWidth, bmHeight.toInt())
          }
          

          【讨论】:

            【解决方案12】:

            您必须先对可绘制对象进行预缩放,然后才能将其用作背景

            【讨论】:

            • 这真的是唯一的选择吗?没有可以指定显示大小的ImageView的容器类型吗?
            • 我也遇到了 SurfaceView 的问题
            • 在 iOS 上没有像宽高比、宽高比填充、左上角、顶部等这样的东西吗?太烂了。。
            • 正如@Aleksej 所说,一个不真实的答案。
            【解决方案13】:

            您可以使用以下之一:

            android:gravity="fill_horizo​​ntal|clip_vertical"

            或者

            android:gravity="fill_vertical|clip_horizo​​ntal"

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2012-07-08
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-08-21
              • 1970-01-01
              相关资源
              最近更新 更多