图像顶部的纹波效果 – Android

我在最近的一个项目中一直在试验涟漪animation。 我在寻找一个“优雅”的解决scheme,在某些情况下使用触摸事件遇到了一些麻烦。 即与图像,特别是在列表,网格和回收视图。 animation几乎总是在视图背后生动,而不是在animation之上。 这在Buttons和TextViews中是没有问题的,但如果你有一个GridView的图像,纹波会出现在实际图像的后面或下面。 显然这不是我想要的,虽然有解决scheme,我认为是一个解决办法,我希望有一些简单的我只是不知道。

我使用下面的代码来实现图像的自定义网格视图。 我会给完整的代码点击这里,所以你可以按照如果你select。

现在只是重要的东西。 为了让我的形象animation触摸我需要这个

button_ripple.xml

<ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/cream_background"> <item> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <!-- Pressed --> <item android:drawable="@color/button_selected" android:state_pressed="true"/> <!-- Selected --> <item android:drawable="@color/button_selected" android:state_selected="true"/> <!-- Focus --> <item android:drawable="@color/button_selected" android:state_focused="true"/> <!-- Default --> <item android:drawable="@color/transparent"/> </selector> </item> </ripple> 

custom_grid.xml

 <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:orientation="horizontal"> <ImageView android:id="@+id/sceneGridItem" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@drawable/button_ripple" android:gravity="center_horizontal"/> </LinearLayout> 

activity_main.xml中

 <GridView android:id="@+id/sceneGrid" android:layout_marginTop="15dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:verticalSpacing="15dp" android:numColumns="5" /> 

所有的魔法和问题发生的线是当我设置背景。 虽然这实际上给了我一个涟漪animation在我的imageview,它animation背后的imageview。 我希望animation出现图像的顶部 。 所以我尝试了一些不同的东西

将整个网格背景设置为button_ripple。

 <GridView android:id="@+id/sceneGrid" android:layout_marginTop="15dp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:verticalSpacing="15dp" android:background="@drawable/button_ripple" android:numColumns="5" /> 

它完全符合你的想法,现在整个网格都有一个半透明的背景,不pipe从网格中心按下整个网格animation的图像。 虽然这很酷,但不是我想要的。

将根/父背景设置为button_ripple。

 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:background="@drawable/button_ripple" android:orientation="horizontal"> 

现在这个区域变大了,并且填满了网格的整个单元格(不仅仅是图像),然而它并没有把它带到前面。

将custom_grid.xml更改为RelativeLayout,并将两个ImageView放在一起

custom_grid.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal"> <ImageView android:id="@+id/gridItem" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" /> <ImageView android:id="@+id/gridItemOverlay" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:background="@drawable/button_ripple" /> </RelativeLayout> 

CustomGridAdapter.java

 .... gridItemOverLay = (ImageView) findViewById(R.id.gridItemOverlay); gridItemOverlay.bringToFront(); 

这工作。 现在底部ImageView包含我的图像和顶部animation,给我的图像的波纹animation幻想。 老实说,这是一个工作。 我觉得这不是它的意图。 所以我问你好人,有没有更好的方法,甚至不同的方式?

我喜欢android开发人员的答案,所以我决定调查如何做他的代码解决scheme的第2步。

你需要从Jake Wharton那里得到这段代码: https : //gist.github.com/JakeWharton/0a251d67649305d84e8a

 import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.widget.ImageView; public class ForegroundImageView extends ImageView { private Drawable foreground; public ForegroundImageView(Context context) { this(context, null); } public ForegroundImageView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ForegroundImageView); Drawable foreground = a.getDrawable(R.styleable.ForegroundImageView_android_foreground); if (foreground != null) { setForeground(foreground); } a.recycle(); } /** * Supply a drawable resource that is to be rendered on top of all of the child * views in the frame layout. * * @param drawableResId The drawable resource to be drawn on top of the children. */ public void setForegroundResource(int drawableResId) { setForeground(getContext().getResources().getDrawable(drawableResId)); } /** * Supply a Drawable that is to be rendered on top of all of the child * views in the frame layout. * * @param drawable The Drawable to be drawn on top of the children. */ public void setForeground(Drawable drawable) { if (foreground == drawable) { return; } if (foreground != null) { foreground.setCallback(null); unscheduleDrawable(foreground); } foreground = drawable; if (drawable != null) { drawable.setCallback(this); if (drawable.isStateful()) { drawable.setState(getDrawableState()); } } requestLayout(); invalidate(); } @Override protected boolean verifyDrawable(Drawable who) { return super.verifyDrawable(who) || who == foreground; } @Override public void jumpDrawablesToCurrentState() { super.jumpDrawablesToCurrentState(); if (foreground != null) foreground.jumpToCurrentState(); } @Override protected void drawableStateChanged() { super.drawableStateChanged(); if (foreground != null && foreground.isStateful()) { foreground.setState(getDrawableState()); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (foreground != null) { foreground.setBounds(0, 0, getMeasuredWidth(), getMeasuredHeight()); invalidate(); } } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); if (foreground != null) { foreground.setBounds(0, 0, w, h); invalidate(); } } @Override public void draw(Canvas canvas) { super.draw(canvas); if (foreground != null) { foreground.draw(canvas); } } } 

这是attrs.xml文件:

 <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="ForegroundImageView"> <attr name="android:foreground"/> </declare-styleable> </resources> 

现在,在你的layout.xml中创build你的ForegroundImageView:

 <com.example.ripples.ForegroundImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:scaleType="centerCrop" android:foreground="?android:selectableItemBackground" android:src="@drawable/apples" android:id="@+id/image" /> 

现在图像会涟漪。

而不是试图在适配器的每个单独的视图中添加涟漪,你可以像这样在GridView级别添加它:

 <GridView android:id="@+id/gridview" ... android:drawSelectorOnTop="true" android:listSelector="@drawable/your_ripple_drawable"/> 

也许尝试这些解决scheme之一(从这里得到的提示):

  1. 将绘图包装在RippleDrawable然后将其设置在ImageView

     Drawable image = … RippleDrawable rippledImage = new RippleDrawable( ColorStateList.valueOf(rippleColor), image, null); imageView.setImageDrawable(rippledImage); 
  2. 扩展ImageView并为其添加前景属性(如FrameLayout )。 将+ Chris Banes添加到LinearLayout看看这个例子。 如果你这样做,那么确保你通过触摸坐标,使波纹从正确的点开始:

     @Override public void drawableHotspotChanged(float x, float y) { super.drawableHotspotChanged(x, y); if (foreground != null) { foreground.setHotspot(x, y); } } 

我有一个图片库(使用RecyclerViewGridLayoutManager构build)。 图像是使用毕加索设置的。 为了给图像添加纹波, 我用一个FrameLayout包装ImageView 。 项目布局是:

 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="120dp" android:foreground="@drawable/ripple" android:clickable="true" android:focusable="true" > <ImageView android:id="@+id/grid_item_imageView" android:layout_width="match_parent" android:layout_height="120dp" android:layout_gravity="center" android:scaleType="centerInside" /> </FrameLayout> 

请注意android:foreground 。 它不像android:background 。 我已经尝试过没有 android:clickable="true"android:focusable="true" ,它也可以工作 ,但不会伤害。

然后添加一个ripple.xml绘制到res/drawable

 <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true"> <shape android:shape="rectangle"> <solid android:color="#7FF5AC8E" /> </shape> </item> <item> <shape android:shape="rectangle"> <solid android:color="@android:color/transparent" /> </shape> </item> </selector> 

请注意,当select该项目时(对于设备<5.0),这将在图像顶部显示半透明颜色。 你可以删除它,如果你不想要它。

然后添加带波纹的ripple.xml drawable到res/drawable-v21

 <ripple xmlns:android="http://schemas.android.com/apk/res/android" android:color="@color/coral"> </ripple> 

您可以使用默认的纹波效果而不是自定义的ripple.xml ,但是在图像顶部很难看到,因为它是灰色的:

 android:foreground="?android:attr/selectableItemBackground"