Take a look at this small android app:
MainActivity.java:
package io.github.gsaga.toucheventtest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
activity_main:
<ImageView android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="@drawable/ic_launcher_background"
xmlns:android="http://schemas.android.com/apk/res/android" />
The image pointed to by android:foreground
isn't displayed, but it appears if I change foreground
to src
or background
in activity_main.xml
. This code seems to follow the instructions described here:
https://developer.android.com/reference/android/view/View.html#attr_android:foreground
Why doesn't the android:foreground
tag work in above code?
NOTE:
minSdkVersion
is 19
, I'm running this app on Android 5.1
(API level 22
)
This is due to a bug which existing in Android since API level 23.
Here is the list of all the XML attributes and corresponding methods related to setting foreground drawables to views with the API level they are introduced through FrameLayout
. However, these are later moved into View
in API level 23.
╔════════════════════════════╦═════════════════════════════════════════════════╦═════════════╗
║ XML attribute ║ Method ║ Added in ║
║ ║ ║ (API level) ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ android:foreground ║ setForeground(Drawable) ║ 1 ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ android:foregroundGravity ║ setForegroundGravity(int gravity) ║ 1 ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ android:foregroundTint ║ setForegroundTintMode(PorterDuff.Mode tintMode) ║ 21 ║
╠════════════════════════════╬═════════════════════════════════════════════════╬═════════════╣
║ android:foregroundTintMode ║ setForegroundTintMode(PorterDuff.Mode tintMode) ║ 21 ║
╚════════════════════════════╩═════════════════════════════════════════════════╩═════════════╝
Android doc says setForeground(Drawable)
is added in API 1 and setForegroundTintList (ColorStateList tint)
and setForegroundTintMode (PorterDuff.Mode tintMode)
are added in API level 21 to View
. Actually they were there in FrameLayout
until it moved in API 23.
In API level < 23, you will get a warning even though it is not required. You can just suppress it. See this.
Now take a look at how these properties work on different versions.
╔═══════════╦══════════════════╦══════════════════╗
║ API level ║ By code ║ Using XML ║
╠═══════════╬══════════════════╬══════════════════╣
║ <23 ║ FrameLayout only ║ FrameLayout only ║
╠═══════════╬══════════════════╬══════════════════╣
║ >=23 ║ FrameLayout only ║ All views ║
╚═══════════╩══════════════════╩══════════════════╝
When these properties moved to View
in API level 23, they did some strange modifications on it which can be called a bug. While loading properties from XML, it checks whether the View
is a FrameLayout
which is not present inside the methods we can use for the same purpose.
View constructor, API level 23:
case R.styleable.View_foreground:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForeground(a.getDrawable(attr));
}
break;
To use android:foreground
on Android 5.1
i.e. API level 22
, you are not using android:foreground
correctly.
As it's name clearly indicating that you can set drawable on the top/foreground of any content like overlay i.e you can put some view in FrameLayout
in that you can use android:foreground
. Inside this FrameLayout add your ImageView
.
Documentation:
Defines the drawable to draw over the content. This can be used as an overlay. The foreground drawable participates in the padding of the content if the gravity is set to fill.
Below is usage example:
<FrameLayout
android:id="@+id/share"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:foreground="@drawable/ic_launcher_background>
// your ImageView here
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>
Note:
For API level > 23
it will work without FrameLayout
.
I hope this will help you.
It looks like at one time (API <23) that android:foreground
would work with only FrameLayout
as VicJordan suggests. However, for API 23+ it appears that android:foreground
will work for any view type. See this selection from the View.java source:
case R.styleable.View_foreground:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForeground(a.getDrawable(attr));
}
Here is an example of android:foreground
working on API 28 with the following layout:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="@drawable/ic_launcher_foreground"
android:src="@android:drawable/ic_delete"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Yet, on API 22, we see this:
No foreground image. So, android:foreground
works only when the API level is 23+. I agree that this is not really documented, but that is just the way it is.
Update: API 23 seems to have an issue with android:foreground
, so let's say android:foreground
works on API 24+ for general views.
Second update: Came across a couple of other posts addressing this same issue regarding setForeground()
here and here. In the accepted answer to the second question, CommonsWare identifies this as a "documentation bug."
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With