RippleDrawable不绘制视图

时间:2015-05-15 14:23:51

标签: android android-layout android-5.0-lollipop material-design

我有一个带有KenBurnsView和ImageView的布局(只是一个切换按钮)。当我点击按钮时,会生成一个Ripple但是会在KenBurnsView下面绘制。

以前,当我将图像视图替换为KenBurnsView时,Ripple被绘制在顶部的ImageView上方。

这是我的布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/drawer"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/background"
    android:clickable="true"
    android:orientation="vertical">


    <RelativeLayout
        android:id="@+id/header"
        android:layout_width="match_parent"
        android:layout_height="@dimen/nav_drawer_header_height">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <com.flaviofaria.kenburnsview.KenBurnsView
                android:id="@+id/header_cover"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:src="@drawable/cover_1" />

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <ImageView
                    android:id="@+id/header_toggle"
                    android:layout_width="50dp"
                    android:layout_height="30dp"
                    android:layout_alignParentBottom="true"
                    android:layout_alignParentRight="true"
                    android:layout_marginBottom="10dp"
                    android:layout_marginRight="10dp"
                    android:padding="10dp"
                    android:src="@drawable/toggle_down" />

            </RelativeLayout>

        </FrameLayout>

    </RelativeLayout>

    <RelativeLayout
        android:id="@+id/nav_toggle_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"></RelativeLayout>

</LinearLayout>

这是我的涟漪可绘XML:

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@android:color/white"
    android:drawSelectorOnTop="true"> <!-- ripple color -->

</ripple>

这是我添加涟漪的方式:

toggle.setBackground(getResources().getDrawable(R.drawable.ripple));

问题是什么,因为Ripple在KenBurnsView下面得到了什么?当有一个ImageView代替KenBurnsView时,它曾经很好地工作吗?

3 个答案:

答案 0 :(得分:3)

无限的涟漪投射到祖先视图的第一个可用背景上。从托管涟漪的ImageView跟踪视图层次结构,您会发现第一个可用背景位于LinearLayout,标识为drawer

如果您在包含RelativeLayout的{​​{1}}上设置透明背景,则第一个可用的背景将是在兄弟视图上方呈现的背景,您将获得所需的效果。

旁注,ImageView应替换为RelativeLayout,这样可以提供与较便宜的布局传递相同的效果。

答案 1 :(得分:3)

我从@Budius的答案中获得了灵感和帮助,并创造了一个更好的解决方案。

ImageView使用一个hack,即使你将它设置为背景(使用setBackgroundDrawable()),它也允许RippleDrawable绘制它。

我创建了一个具有绘制前景的功能的修改后的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);
        }
    }
}

然后我将切换图像放在此图像视图中,并以编程方式应用了纹波

toggle.setForegroundResource(R.drawable.ripple); 

这将使波纹在此特定视图的范围内绘制。要使波纹划过其他视图,

<RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:clipChildren="false"
                android:clipToPadding="false">

您需要为clipChildren和clipToPadding添加false。

希望这有助于有人解决问题。

答案 2 :(得分:0)

正如您可以从自己的代码中看到的那样(&#39; setBackground&#39;),您将涟漪设置为背景,这就是为什么它的原因。在背景上绘制。

Android API 21上的ImageView添加了这个&#34; hack&#34;对于涟漪android:drawSelectorOnTop="true"。但是你使用的图书馆并没有添加相同的黑客。

您的代码本身并没有错。但Android团队无法保证第三方库的这种行为。

您可以在此处选择一些清洁度,努力程度和性能的选项:

  1. 检查ImageView源代码,克隆库,添加与其上使用的imageview相同的hack用于纹波。在它正常工作之后,请务必将请求拉回库中。
  2. 使用FrameLayout封装您的KenBurnsView并使用FrameLayout上的setForeground设置涟漪。
  3. 克隆库,向其添加前景drawable选项(类似于此How to set foreground attribute to other non FrameLayout view)。另外,请务必将此有价值的代码请求回库。