Take a look at this small android app:
MainActivity.java:
package io.github.gsaga.toucheventtest;
import android.support.v7.app.AppCompatActivity;
i
Another solution is to wrap your image with a <ripple>
, set it as your ImageView
's background
, and use tint
and tintMode
to "hide" the src
image so the background image that has the ripple over it is visible.
<!-- In your layout file -->
<ImageView
android:adjustViewBounds="true"
android:background="@drawable/image_with_ripple"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/image"
android:tint="@android:color/transparent"
android:tintMode="src_in" />
<!-- drawable/image_with_ripple.xml -->
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?colorControlHighlight">
<item android:drawable="@drawable/image" />
</ripple>
Not only this works on API 21+ but if your image has rounded corners – or is another type of non-rectangle shape, like a star or a heart icon – the ripple will remain in its bounds instead of filling the view's rectangle bounds, which gives a better look in some cases.
See this Medium article for an animated GIF to see how this technique compares to using a <FrameLayout>
or the foreground
attribute.
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."
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.