setBackgroundResource()方法丢弃了我的XML布局属性

71

我有一个视图,被用作ListView中的项目。在我的自定义适配器中,根据该项目在列表中的位置,使用View.setBackgroundResource()更改视图的背景。(我为列表中的第一个和最后一个项目分别准备了不同的资源。)

这设置了正确的背景图像,但它有一个不好的副作用,就是完全忽略了我在视图的XML定义中设置的所有内边距。

(如果我在XML中设置背景可绘制对象,并且在适配器中不尝试在运行时更改它,则所有填充都有效.)

如何更改背景图像并保留填充?这是一个错误吗?

编辑 看起来有人在这里发现了相同的问题:更改背景是否也会更改LinearLayout的填充?

5个回答

106

我也遇到了这个问题。假设你正在使用一个LayerList资源drawable?我也是这么用的。不幸的是,我没有找到“真正”的解决方法,似乎这是代码中的一个bug,但我没有追踪它。然而,我很幸运,因为在我的视图已经被正确渲染之后,我设置了“有缺陷”的背景,所以只需要在设置背景后保存和恢复padding值即可,例如:

  if(condition) {
    int bottom = theView.getPaddingBottom();
    int top = theView.getPaddingTop();
    int right = theView.getPaddingRight();
    int left = theView.getPaddingLeft();
    theView.setBackgroundResource(R.drawable.entry_bg_with_image);
    theView.setPadding(left, top, right, bottom);
  }

编辑: 作为另一种选择,您不必使用先前的填充值,还可以使用维度值:

  int pad = resources.getDimensionPixelSize(R.dimen.linear_layout_padding);
  theView.setBackgroundResource(R.drawable.entry_bg_with_image);
  theView.setPadding(pad, pad, pad, pad);

已经解决了,谢谢。在我的情况中,drawable 是一个 Nine-Patch PNG。 - Graham Borland
3
没错,确实是这样。看一下View的代码(http://j.mp/kxQJIJ),在setBackgroundDrawable()方法中。它明显是使用Drawable的padding来覆盖原有的padding。但查看Drawable资源文档,除了shape外,没有任何支持padding的内容,所以这可能就是为什么它失败惨淡的原因。 - dmon
1
而且似乎Romain Guy刚刚将其关闭为“按预期工作”。代码中有一条注释可以证明这种行为是有意的。我想如果有地方记录下来就更好了。 - dmon
16
这绝对不是“按预期工作”,当我只想通过使用setBackgroundResource()更改图像的背景资源而不影响任何其他属性时,我从来没有想到它会影响元素的填充。 - Elad Nava
3
Romain说:“设置图像会重置填充的原因是因为9-patch图像可以编码填充。如果缺失,则假定填充为0。”我的看法是应该改成“If missing **&& is9Patch()**”。 - adamdport
显示剩余4条评论

11

在dmon提出的建议基础上,这里有一个函数可以直接添加到你的实用类中,这样每当你更新资源时就不必费心了。这只是将他的代码封装在一个函数中。

public static void updateBackgroundResourceWithRetainedPadding(View view, int resourceID)
{
    int bottom = view.getPaddingBottom();
    int top = view.getPaddingTop();
    int right = view.getPaddingRight();
    int left = view.getPaddingLeft();
    view.setBackgroundResource(resourceID);
    view.setPadding(left, top, right, bottom);
}

7
这个问题在Android 5.0(棒棒糖)版本中已经被修复。
public static void setBackgroundResource(@NonNull View view, @DrawableRes int resId) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        int paddingTop = view.getPaddingTop();
        int paddingLeft = view.getPaddingLeft();
        int paddingRight = view.getPaddingRight();
        int paddingBottom = view.getPaddingBottom();
        view.setBackgroundResource(resId);
        view.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
    } else {
        view.setBackgroundResource(resId);
    }
}

1
不,这在Lollipop中并没有固定(至少在我的5.1 Moto G原生ROM上没有)。 - Henrique de Sousa
这个问题在KitKat版本之后已经被修复,所以检查应该是Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT - David
我的Pixel 2 API 29仍然没有修复。 - DIRTY DAVE

6

除了使用 dmon 提出的在代码中获取和设置填充之外,我选择的另一种解决方案是不使用填充,而是使用内部元素的边距。

根据您的布局,实际上可能需要相同数量的 XML 代码,而且根本不需要任何 Java。虽然这对我来说感觉有点不太好,但并没有到到处添加 Java 代码那么糟糕。


-2
在Monodroid中,如果我调用SetBackgroundResource函数,则顶部填充和底部填充将保持不变。
private EditText _etInput

public void Disable()
{
    _etInput.Post(() => {
        _etInput.SetBackgroundResource(Resource.Drawable.input_field_background_disabled);
        _etInput.Clickable = false;
});

然而,左填充被重置为0!? 如果没有发布,则所有填充都将重置为0。

认为这是一个值得发布的有趣发现...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接