android:foreground属性/ setForeground()方法不适用于Button元素

时间:2018-06-21 18:02:30

标签: android button textview android-xml

Android 23起,android:foreground XML属性(以及相应的setForeground() method)应可用于所有View,而不仅仅是以前的FrameLayout实例。

但是,由于某种原因,每当我创建一个从TextView-> View继承的Button实例时,我似乎根本看不到要显示的前景。

这是一个无效的示例按钮定义,显示为前景:

    <Button
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:layout_marginEnd="10dp"
      android:text="Primary Full Width"
      style="@style/Button.Primary" />

这是Button.Primary样式的定义:

    <style name="Button.Primary" parent="Widget.AppCompat.Button">
      <item name="android:background">@drawable/button_primary_background_selector</item>
      <item name="android:foreground">@drawable/button_primary_foreground_selector</item>
      <item name="android:textAppearance">@style/TextAppearance.FF.Button</item>
      <item name="android:minHeight">80dp</item>
      <item name="android:height">80dp</item>
      <item name="android:minWidth">200dp</item>
      <item name="android:defaultWidth">288dp</item>
      <item name="android:maxWidth">400dp</item>
      <item name="android:focusable">true</item>
      <item name="android:clickable">true</item>
      <item name="android:gravity">center_vertical|center_horizontal</item>
      <item name="android:paddingStart">70dp</item>
      <item name="android:paddingEnd">70dp</item>
      <item name="android:stateListAnimator">@null</item>
      <item name="android:singleLine">true</item>
      <item name="android:ellipsize">end</item>
      <item name="android:drawablePadding">10dp</item>
</style>

我已经确认以样式或foreground定义设置<Button>属性不会改变任何内容,无论如何前景都不会显示。背景和文字一样正确显示,只是缺少前景。

我还尝试将前景设置为纯色,而不是可绘制的状态选择器,但没有成功。

2 个答案:

答案 0 :(得分:0)

逐一删除样式元素并进行重新测试后,我将问题缩小为在样式中包含android:singleLine属性。

删除此属性后,使用我的样式的所有TextViews或Button都将根据需要正确显示前景可绘制对象。查看定义了singleLine的{​​{3}}的实现,我仍在努力确定设置此属性如何导致前景被忽略,但是如果发现,我将更新此答案。

我知道singleLine的使用已被弃用,但不幸的是,我仍然需要singleLine / ellipsize属性的组合所提供的功能,而这些属性在用{{1替换时是不可用的}}的用法。

编辑:在写完上面的答案后,我决定进行更多调查,并发现更多细节。

首先,我创建了一个自AppCompatButton扩展的自定义视图,因此我可以尝试重新实现所提供的功能maxLines / singleLine(即,用换行符替换为一行显示文本)空格,然后在文本从视图中跑出时添加省略号)。

通读TextView.java,发现ellipsize属性设置为true时会调用一段代码:

singleLine

所以我尝试提取这些行并将其添加到自己的实现中:

private void applySingleLine(boolean singleLine, boolean applyTransformation,
        boolean changeMaxLines) {
    mSingleLine = singleLine;
    if (singleLine) {
        setLines(1);
        setHorizontallyScrolling(true);
        if (applyTransformation) {
            setTransformationMethod(SingleLineTransformationMethod.getInstance());
        }
    } 
    ...
}

但是当这段代码运行时,我看到了以前的前景可绘制对象不再可见的相同问题。

经过进一步测试,我能够将问题缩小为 private void updateMaxlinesLocally(int maxLines) { if (maxLines == 1) { Log.d("Button", "max lines was 1, setting to single line equivalent"); setLines(1); setHorizontallyScrolling(true); setTransformationMethod(SingleLineTransformationMethod.getInstance()); // reset any text that may have already been set setText(getText()); } else { Log.d("Button", "max lines was : " + maxLines); } } 方法调用。如果仅在自定义实现中注释掉这一行,就可以保留之前拥有的setHorizontallyScrolling(true) / singleLine功能,并且前台可绘制对象按预期显示!缩小到该方法仍然无法帮助我找出TextView基本实现的根本原因,但是如果有人对此有任何详细信息,请随时评论更多信息。

这是我最后的自定义Button类,它仅查看ellipsize属性,并在maxLines设置为1时模拟旧的singleLine功能,避免了会阻止前台运行的方法从显示。万一有用的话...

maxLines

}

并来自attrs.xml:

public class Button extends AppCompatButton {

public Button(Context context) {
    super(context);
    init(context, null, 0);
}

public Button(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context, attrs, 0);
}

public Button(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs, defStyleAttr);
}

@Override
public void setMaxLines(int maxLines) {
    super.setMaxLines(maxLines);
    updateMaxlinesLocally(maxLines);
}

private void init(Context context, AttributeSet attributeSet, int defStyleAttr) {
    int[] set = {
            android.R.attr.maxLines
    };
    TypedArray a = context.obtainStyledAttributes(
            attributeSet, R.styleable.Button);
    int maxLines = a.getInt(R.styleable.Button_android_maxLines, -1);
    a.recycle();
    updateMaxlinesLocally(maxLines);
}

private void updateMaxlinesLocally(int maxLines) {
    if (maxLines == 1) {
        Log.d("Button", "max lines was 1, setting to single line equivalent");
        setLines(1);
        // The act of setting horizontally scrolling to true is what disables foregrounds from
        // showing...
//            setHorizontallyScrolling(true);
        setTransformationMethod(SingleLineTransformationMethod.getInstance());
        // reset any text that may have already been set
        setText(getText());
    } else {
        Log.d("Button", "max lines was  : " + maxLines);
    }
}

答案 1 :(得分:0)

如Keith setHorizontallyScrolling(true)所述,滚动使前景消失。可以轻松重现该问题。

但是为什么水平滚动会影响前景?

设置水平(或垂直滚动​​)指示TextView包含的区域大于其布局范围。

如果打印scrollX / scrollY值,即使内容不能被用户滚动,通常也不会是零,而是一个很大的数字。

另一方面,前景设计为随内容滚动,因此它基本上是从画布的(0, 0)开始绘制的,而不是可见内容的左上角。因此,它的绘制距离实际视图范围很远。您可以为此View.java的源代码打开onDrawForeground

我该怎么办

  • 在设置setHorizontallyScrolling(false)属性或任何暗示滚动的功能后,通过调用singleline来禁用滚动。当心不要在尝试初始化/更新视图时启用滚动。
  • 翻译前景可绘制对象,例如通过添加setBoundsscrollX
  • 更新scrollY的边界