使用 RecyclerView 和 CardView 进行触摸反馈

Posted

技术标签:

【中文标题】使用 RecyclerView 和 CardView 进行触摸反馈【英文标题】:Touch feedback with RecyclerView and CardView 【发布时间】:2015-01-13 16:24:18 【问题描述】:

我很想为我的开源库启用触摸反馈。

我创建了一个RecyclerView 和一个CardViewCardView 包含具有不同onClick 操作的不同区域。现在,如果用户单击卡片中的任何位置,我很想触发涟漪效应,但我无法实现此行为。

这是我的列表项, 你也可以在 GitHub 上找到它:https://github.com/mikepenz/AboutLibraries/blob/master/library/src/main/res/layout/listitem_opensource.xml

<RelativeLayout
    android:layout_
    android:layout_
    android:clickable="true"
    android:background="@drawable/button_rect_normal"/>

<LinearLayout
    android:padding="6dp"
    android:layout_
    android:layout_
    android:orientation="vertical">

    <LinearLayout
        android:gravity="center_vertical"
        android:paddingLeft="8dp"
        android:paddingRight="8dp"
        android:orientation="horizontal"
        android:layout_
        android:layout_>

        <TextView
            android:id="@+id/libraryName"
            android:textColor="@color/title_openSource"
            android:textSize="@dimen/textSizeLarge_openSource"
            android:textStyle="normal"
            android:layout_
            android:layout_weight="5"
            android:layout_
            android:ellipsize="end"
            android:maxLines="1"/>

        <TextView
            android:id="@+id/libraryCreator"
            android:textColor="@color/text_openSource"
            android:textStyle="normal"
            android:layout_
            android:layout_weight="2"
            android:layout_
            android:layout_marginTop="2dp"
            android:gravity="right"
            android:maxLines="2"
            android:textSize="@dimen/textSizeSmall_openSource"/>
    </LinearLayout>

    <View
        android:layout_
        android:layout_
        android:layout_marginTop="4dp"
        android:background="@color/dividerLight_openSource"/>

    <TextView
        android:id="@+id/libraryDescription"
        android:textSize="@dimen/textSizeSmall_openSource"
        android:textStyle="normal"
        android:textColor="@color/text_openSource"
        android:padding="8dp"
        android:layout_
        android:layout_
        android:maxLines="20">
    </TextView>

    <View
        android:id="@+id/libraryBottomDivider"
        android:layout_
        android:layout_
        android:layout_marginTop="4dp"
        android:background="@color/dividerLight_openSource"/>

    <LinearLayout
        android:id="@+id/libraryBottomContainer"
        android:gravity="center_vertical"
        android:paddingLeft="8dp"
        android:paddingTop="4dp"
        android:paddingRight="8dp"
        android:orientation="horizontal"
        android:layout_
        android:layout_>

        <TextView
            android:id="@+id/libraryVersion"
            android:textColor="@color/text_openSource"
            android:textStyle="normal"
            android:layout_
            android:layout_weight="1"
            android:layout_
            android:gravity="left"
            android:maxLines="1"
            android:textSize="@dimen/textSizeSmall_openSource"/>

        <TextView
            android:id="@+id/libraryLicense"
            android:textColor="@color/text_openSource"
            android:textStyle="normal"
            android:layout_
            android:layout_weight="1"
            android:layout_
            android:gravity="right"
            android:maxLines="1"
            android:textSize="@dimen/textSizeSmall_openSource"/>
    </LinearLayout>
</LinearLayout>

onClick 设置在此布局的 3 个部分上。 libraryCreatorlibraryDescriptionlibraryBottomContainer

我希望有人知道这里出了什么问题。

感谢您的帮助。

【问题讨论】:

【参考方案1】:

假设您使用的是 Material/Appcompat 主题和 Lollipop,我通过使 CardView 具有以下属性来实现这一点:

android:focusable="true"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"

【讨论】:

不,这不起作用:/。仍然没有点击反馈。就在没有 onClick 监听器的区域 @Gak2,我认为您需要设置android:background 而不是android:foreground。设置它使它对我有用。 使用前台对我没有任何帮助。如果您的建议有什么问题,请进一步解释。 我猜你的问题是因为 android:foreground 只适用于 FrameLayouts 或继承 FrameLayout 的类,如 CardView。如果你尝试在RelativeLayout或LinearLayout中设置它,它不会起作用 正如@AvinashR 所说,设置 android:background 有效。但是,更好的增强是:android:clickable="true" android:background="?attr/selectableItemBackground" 这样,背景使用棒棒糖前设备上的默认触摸选择器和棒棒糖及更高版本上的美丽波纹:-)【参考方案2】:

CardLayout 只是 ViewGroup 的一个子类,所以设置:

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

应该可以解决问题。

更新:

(看起来您正在尝试使用自定义波纹颜色。)

尝试使用带有自定义颜色的可绘制波纹作为背景(我没有使用过,因此无法验证此行为。)请参阅:documentation 或此question 以帮助您入门。

【讨论】:

我如何以编程方式设置它? 我找到了答案:***.com/questions/20531516/… 我通过设置 android:focusable="true"android:clickable="true" 来完成这项工作。【参考方案3】:

对我来说:

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

终于成功了。 查看 recyclerView 的背景 / 项目后面的内容,还可以查看与 recyclerView 一起使用的涟漪效果。

【讨论】:

【参考方案4】:

以上答案都不适合我,或者太复杂了。

然后我意识到我必须在 THE RECYCLERVIEW ADAPTER LAYOUT 中设置以下属性。

android:background="?android:attr/selectableItemBackground"
android:focusable="true"
android:clickable="true"

然后我开始工作了。

【讨论】:

API 级别至少需要 11 级 @war_Hero 这真的是个问题吗? 在某种程度上是的,因为 recyclerview 旨在工作到版本 7 从好的方面来说,大多数较新的应用程序都支持 jellybean 及更高版本,因此您的回答也有效 从技术上讲,事实并非如此。我可以通过在你的 sn-p 中寻找线索来解决我的问题。真正的问题是你不应该到处使用android:clickable="true" uncesseriliy。它应该只在 CardView 中。然后就可以了。【参考方案5】:

我现在能够解决问题。

问题是我在 TextViews 上使用了 onClickListener,而这个点击监听器阻止了 RippleForeground 收到有关触摸事件的通知。所以解决方案是在那些 TextView 上实现一个 TouchListener 并通过触摸事件。

这个类真的很简单,到处都可以使用:

package com.mikepenz.aboutlibraries.util;

import android.support.v7.widget.CardView;
import android.view.MotionEvent;
import android.view.View;

/**
 * Created by mikepenz on 16.04.15.
 */
public class RippleForegroundListener implements View.OnTouchListener 
    CardView cardView;

    public RippleForegroundListener setCardView(CardView cardView) 
        this.cardView = cardView;
        return this;
    

    @Override
    public boolean onTouch(View v, MotionEvent event) 
        // Convert to card view coordinates. Assumes the host view is
        // a direct child and the card view is not scrollable.
        float x = event.getX() + v.getLeft();
        float y = event.getY() + v.getTop();

        if (android.os.Build.VERSION.SDK_INT >= 21) 
            // Simulate motion on the card view.
            cardView.drawableHotspotChanged(x, y);
        

        // Simulate pressed state on the card view.
        switch (event.getActionMasked()) 
            case MotionEvent.ACTION_DOWN:
                cardView.setPressed(true);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                cardView.setPressed(false);
                break;
        

        // Pass all events through to the host view.
        return false;
    

可以通过添加这个作为TouchListener来使用:

RippleForegroundListener rippleForegroundListener = new RippleForegroundListener();
older.libraryCreator.setOnTouchListener(rippleForegroundListener);

这个监听器只会将触摸事件传递给 CardView 并触发正确的波纹效果。 (您也可以修改它以获取任何视图。不应仅限于 CardView)

【讨论】:

older.libraryCreator.setOnTouchListener(rippleForegroundListener); 是什么? @MohitKhaitan 应该将 Touch 传递给 RippleView 的视图。还有一个更新版本,没有保留上下文。 github.com/mikepenz/AboutLibraries/blob/master/library/src/main/… 此处使用:github.com/mikepenz/AboutLibraries/blob/… 我对此表示赞同,因为与其他答案不同,它正确指出了我的 LinearLayout(在我的情况下,我使用 LinearLayout 而不是 CardView)没有获得选择效果的原因。但是您的解决方案似乎并不那么容易。这是谷歌的意图吗?这不能单独在布局XML文件中实现吗? 我通过继承TextView创建了UntouchableTextView,并覆盖了onTouchEvent并返回false。方法体只是“返回 false”,但样板代码(尤其是构造函数)有点烦人。现在我在布局 XML 文件中用 UntouchableTextView 替换了 TextView,LinearLayout 获得了触摸事件。我希望 TextView 有一个简单的属性,比如 android:hitTest="false" 所以这可以在一行中完成...... 现在我终于知道是什么问题了,但是你的解决方案有一个问题:滑动事件也被识别为点击,点击效果正常【参考方案6】:
android:foreground="?android:attr/selectableItemBackground"

如果使用前景代替背景,则不需要使用可点击或可聚焦

【讨论】:

尝试了所有其他答案,只需将其添加到 CardView! 也能完美搭配不同颜色的卡片。谢谢!【参考方案7】:

就我而言,我还需要显示自定义背景和涟漪效果。所以我实现这一点的方法是在我的回收站视图项目的根布局上应用这两个属性:

android:background="@drawable/custom_bg"
android:foreground="?android:attr/selectableItemBackground"

【讨论】:

【参考方案8】:

和蒋有同样的问题:

我必须将属性设置为布局文件,这会使 RecycleView 膨胀。

我的主布局:(不要在此处添加属性!)

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_>


<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:scrollbars="vertical"
    android:layout_
    android:layout_ />

</RelativeLayout>

我的 RecycleViewAdapter:

            View v = inflater.inflate(R.layout.list_center_item, parent, false);

我的 list_center_item.xml(在此处添加属性!

<?xml version="1.0" encoding="utf-8"?>


<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_
android:layout_
android:gravity="center"


android:background="?android:attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
/>

【讨论】:

【参考方案9】:

您必须在 CardView 下使用的布局中添加以下行

android:clickable="true"
android:focusable="true"
android:background="@drawable/my_ripple"

例子

<android.support.v7.widget.CardView android:layout_
android:layout_
xmlns:android="http://schemas.android.com/apk/res/android">

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_
android:layout_
android:clickable="true"
android:focusable="true"
android:background="@drawable/my_ripple">

【讨论】:

@drawable/my_ripple 的可绘制文件是什么?【参考方案10】:

在我的情况下,实际的愚蠢问题如下......

从技术上讲,情况并非像每个人在我的情况下建议的那样。真正的问题是你不应该在LayoutViewRelativeViewTextView 的任何地方都使用android:clickable="true" uncesseriliy。

它应该只在 CardView 中。 然后它就像魅力一样工作

android:focusable="true"不是真正的再次要求。

只需在您的 CardView 中添加以下内容

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


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_
    android:layout_>

    <android.support.v7.widget.CardView
        android:id="@+id/card_view"
        android:layout_
        android:layout_
        android:layout_gravity="center"
        android:layout_margin="@dimen/card_margin"
        android:foreground="?android:attr/selectableItemBackground"
        android:clickable="true"
        android:elevation="3dp"
        card_view:cardCornerRadius="@dimen/card_corner_radius">

        <RelativeLayout
            android:layout_
            android:layout_>

            <ImageView
                android:id="@+id/card_template_item_image"
                android:layout_
                android:layout_
                android:scaleType="fitXY" />

            <TextView
                android:id="@+id/card_template_item_title"
                android:layout_
                android:layout_
                android:layout_below="@id/card_template_item_image"
                android:paddingLeft="@dimen/card_title_padding"
                android:paddingRight="@dimen/card_title_padding"
                android:paddingTop="@dimen/card_title_padding"
                android:textSize="@dimen/card_title_size"
                android:text="@string/place_holder_item_name"/>

            <TextView
                android:id="@+id/card_template_item_sub_title"
                android:layout_
                android:layout_
                android:layout_below="@id/card_template_item_title"
                android:paddingBottom="@dimen/card_sub_title_padding"
                android:paddingLeft="@dimen/card_sub_title_padding"
                android:paddingRight="@dimen/card_sub_title_padding"
                android:textSize="@dimen/card_sub_title_size"
                android:text="@string/place_holder_item_category"/>

        </RelativeLayout>

    </android.support.v7.widget.CardView>

</LinearLayout>

【讨论】:

以上是关于使用 RecyclerView 和 CardView 进行触摸反馈的主要内容,如果未能解决你的问题,请参考以下文章

怎么导入recyclerview包

RecyclerView小结

RecyclerView详解

Android RecyclerView使用简述

Android RecyclerView使用简述

Android RecyclerView使用简述