2

I have a RecyclerView containing its children:

enter image description here

As you can see in the picture, each item can be expanded (for example item i in the picture). In my project I use this blur library to blur things, for example, clicking the FloatingActionButton expands it to a dialog, and the dialog's surrounding is blurred, like this:

enter image description here

I want that an expanded RecyclerViews item will have its surrounding blurred as well. I tried this:

(Item.xml:)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.github.mmin18.widget.RealtimeBlurView
        android:id="@+id/blur_view_per_active_goal_card"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        app:realtimeBlurRadius="20dp"
        app:realtimeOverlayColor="#8000"
        android:visibility="visible"/>

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        app:cardCornerRadius="15dp"
        app:cardElevation="10dp">

        <RelativeLayout
            android:id="@+id/root_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/active_goal_card"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="4dp"
                android:orientation="horizontal"
                android:padding="10dp"
                android:weightSum="10">

                <com.example.performancemeasurement.customViews.CustomProgressBar.CustomProgressBar
                    android:id="@+id/active_goal_item_progress_bar"
                    android:layout_width="0dp"
                    android:layout_height="50dp"
                    android:layout_weight="9"
                    android:transitionName="progressBar"
                    android:visibility="visible"
                    app:enable_gradient="true" />

                <ImageButton
                    android:id="@+id/active_goal_item_open_close_image_button"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:background="?attr/selectableItemBackground"
                    android:scaleType="fitCenter"
                    android:src="@drawable/down_vector" />

            </LinearLayout>

            <RelativeLayout
                android:id="@+id/expanded_active_goal_card"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="4dp"
                android:visibility="gone">


                <TextView
                    android:id="@+id/expanded_active_goal_card_label"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dp"
                    android:layout_centerHorizontal="true"
                    android:text="Goal Name"
                    android:textColor="@android:color/black"
                    android:textSize="40sp"
                    android:textStyle="bold" />


                <com.example.performancemeasurement.customViews.CustomProgressBar.CustomProgressBar
                    android:id="@+id/expanded_active_goal_card_progress_bar"
                    android:layout_width="wrap_content"
                    android:layout_height="30dp"
                    android:layout_below="@id/expanded_active_goal_card_label"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentEnd="true"
                    android:layout_marginStart="20dp"
                    android:layout_marginEnd="20dp"
                    android:layout_centerHorizontal="true"
                    android:transitionName="progressBar"
                    app:enable_gradient="true" />

                <TextView
                    android:id="@+id/expanded_active_goal_card_description"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/expanded_active_goal_card_progress_bar"
                    android:layout_centerHorizontal="true"
                    android:layout_margin="10dp"
                    android:maxLines="3"
                    android:text="Goal's Description"
                    android:textColor="#6F6F6F"
                    android:textSize="20sp"
                    android:textStyle="normal" />

                <LinearLayout
                    android:id="@+id/expanded_active_goal_card_sub_goals_label_container"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/expanded_active_goal_card_description"
                    android:layout_centerHorizontal="true"
                    android:layout_margin="5dp"
                    android:gravity="center_vertical"
                    android:orientation="horizontal"
                    android:weightSum="14">

                    <View
                        android:id="@+id/expanded_active_goal_card_sub_goal_label_left"
                        android:layout_width="0dp"
                        android:layout_height="3dp"
                        android:layout_weight="4"
                        android:background="@color/light_gray" />

                    <TextView
                        android:id="@+id/expanded_active_goal_card_sub_goals_label"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_margin="5dp"
                        android:layout_weight="6"
                        android:gravity="center"
                        android:text="SubGoals"
                        android:textColor="#5A5A5A"
                        android:textSize="25sp"
                        android:textStyle="bold" />

                    <View
                        android:id="@+id/expanded_active_goal_card_sub_goal_label_right"
                        android:layout_width="0dp"
                        android:layout_height="3dp"
                        android:layout_weight="4"
                        android:background="@color/light_gray" />

                </LinearLayout>

                <RelativeLayout
                    android:id="@+id/expanded_active_goal_card_sub_goals_container"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/expanded_active_goal_card_sub_goals_label_container"
                    android:layout_centerHorizontal="true"
                    android:layout_margin="10dp"
                    android:animateLayoutChanges="true"
                    android:elevation="15dp"
                    android:gravity="center">

                    <androidx.recyclerview.widget.RecyclerView
                        android:id="@+id/expanded_active_goal_card_sub_goals_recycler_view"
                        android:layout_width="match_parent"
                        android:layout_height="100dp" />

                </RelativeLayout>

            </RelativeLayout>

        </RelativeLayout>

    </androidx.cardview.widget.CardView>


</RelativeLayout>

(putting the blurred view behind the item's content, and then in the RecyclerViews ViewHolder handle the blur visibility as I did in the Dialog example every time the item is expanded/shrunk, but even when I make it visible without handling it, the blur won't show. I tried to handle it programmatically as planned, but still, the blur didn't show up.

So the question is: Specifically in my case - where is the problem? Why won't the blur show? And in general... how can I blur the surrounding of a RecyclerViews expanded/focused item (using the RealtimeBlurView library or any other that suits the solution for this problem), or in other words, how can I blur the whole Recyclerview except of one of its items / how can I blur the background of specific View(blur everything except this View.

Help would be highly appreciated! (:

Nitzan Daloomy
  • 166
  • 5
  • 24

2 Answers2

1

You must put RealtimeBlurView above another view to make that view blur but in your implementation, RealtimeBlurView is below CardView, so you can't see the Bure effect. You must put it after CardView in your layout file. Try the following code:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <androidx.cardview.widget.CardView
        android:id="@+id/cardView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        app:cardCornerRadius="15dp"
        app:cardElevation="10dp">

        <RelativeLayout
            android:id="@+id/root_view"
            android:layout_width="match_parent"
            android:layout_height="match_parent">

            <LinearLayout
                android:id="@+id/active_goal_card"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="4dp"
                android:orientation="horizontal"
                android:padding="10dp"
                android:weightSum="10">

                <com.example.performancemeasurement.customViews.CustomProgressBar.CustomProgressBar
                    android:id="@+id/active_goal_item_progress_bar"
                    android:layout_width="0dp"
                    android:layout_height="50dp"
                    android:layout_weight="9"
                    android:transitionName="progressBar"
                    android:visibility="visible"
                    app:enable_gradient="true" />

                <ImageButton
                    android:id="@+id/active_goal_item_open_close_image_button"
                    android:layout_width="0dp"
                    android:layout_height="match_parent"
                    android:layout_weight="1"
                    android:background="?attr/selectableItemBackground"
                    android:scaleType="fitCenter"
                    android:src="@drawable/down_vector" />

            </LinearLayout>

            <RelativeLayout
                android:id="@+id/expanded_active_goal_card"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="4dp"
                android:visibility="gone">


                <TextView
                    android:id="@+id/expanded_active_goal_card_label"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_margin="10dp"
                    android:layout_centerHorizontal="true"
                    android:text="Goal Name"
                    android:textColor="@android:color/black"
                    android:textSize="40sp"
                    android:textStyle="bold" />


                <com.example.performancemeasurement.customViews.CustomProgressBar.CustomProgressBar
                    android:id="@+id/expanded_active_goal_card_progress_bar"
                    android:layout_width="wrap_content"
                    android:layout_height="30dp"
                    android:layout_below="@id/expanded_active_goal_card_label"
                    android:layout_alignParentStart="true"
                    android:layout_alignParentEnd="true"
                    android:layout_marginStart="20dp"
                    android:layout_marginEnd="20dp"
                    android:layout_centerHorizontal="true"
                    android:transitionName="progressBar"
                    app:enable_gradient="true" />

                <TextView
                    android:id="@+id/expanded_active_goal_card_description"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/expanded_active_goal_card_progress_bar"
                    android:layout_centerHorizontal="true"
                    android:layout_margin="10dp"
                    android:maxLines="3"
                    android:text="Goal's Description"
                    android:textColor="#6F6F6F"
                    android:textSize="20sp"
                    android:textStyle="normal" />

                <LinearLayout
                    android:id="@+id/expanded_active_goal_card_sub_goals_label_container"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_below="@id/expanded_active_goal_card_description"
                    android:layout_centerHorizontal="true"
                    android:layout_margin="5dp"
                    android:gravity="center_vertical"
                    android:orientation="horizontal"
                    android:weightSum="14">

                    <View
                        android:id="@+id/expanded_active_goal_card_sub_goal_label_left"
                        android:layout_width="0dp"
                        android:layout_height="3dp"
                        android:layout_weight="4"
                        android:background="@color/light_gray" />

                    <TextView
                        android:id="@+id/expanded_active_goal_card_sub_goals_label"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_margin="5dp"
                        android:layout_weight="6"
                        android:gravity="center"
                        android:text="SubGoals"
                        android:textColor="#5A5A5A"
                        android:textSize="25sp"
                        android:textStyle="bold" />

                    <View
                        android:id="@+id/expanded_active_goal_card_sub_goal_label_right"
                        android:layout_width="0dp"
                        android:layout_height="3dp"
                        android:layout_weight="4"
                        android:background="@color/light_gray" />

                </LinearLayout>

                <RelativeLayout
                    android:id="@+id/expanded_active_goal_card_sub_goals_container"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:layout_below="@+id/expanded_active_goal_card_sub_goals_label_container"
                    android:layout_centerHorizontal="true"
                    android:layout_margin="10dp"
                    android:animateLayoutChanges="true"
                    android:elevation="15dp"
                    android:gravity="center">

                    <androidx.recyclerview.widget.RecyclerView
                        android:id="@+id/expanded_active_goal_card_sub_goals_recycler_view"
                        android:layout_width="match_parent"
                        android:layout_height="100dp" />

                </RelativeLayout>

            </RelativeLayout>

        </RelativeLayout>

    </androidx.cardview.widget.CardView>

    <com.github.mmin18.widget.RealtimeBlurView
        android:id="@+id/blur_view_per_active_goal_card"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/cardView"
        android:layout_alignBottom="@id/cardView"
        android:layout_alignLeft="@id/cardView"
        android:layout_alignRight="@id/cardView"
        app:realtimeBlurRadius="20dp"
        app:realtimeOverlayColor="#8000"
        android:visibility="visible"/>

</RelativeLayout>


Update
Since you need to blur other items except for expanded item, you should handle it in your adapter implementation. The following code snippet shows how to implement it.

MyAdapter.kt

class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>(), View.OnClickListener {

    val items = ArrayList<Item>()
    var expandedItemIndex = -1

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyAdapter.MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_row, parent, false)
        return MyViewHolder(view, this)
    }

    override fun getItemCount() = items.size

    override fun onBindViewHolder(holder: MyAdapter.MyViewHolder, position: Int) {
        items.get(position)?.apply { holder.bind(this, expandedItemIndex) }
    }

    override fun onClick(v: View?) {
        v?.let {
            if(it.id == R.id.blur) {
                expandedItemIndex = -1
                notifyDataSetChanged()
            } else if(it.id == R.id.expand_btn) {
                val holder = it.getTag() as MyViewHolder
                if (expandedItemIndex == holder.adapterPosition) {
                    expandedItemIndex = -1;
                    notifyDataSetChanged()
                } else {
                    expandedItemIndex = holder.adapterPosition
                    notifyDataSetChanged()
                }
            }
        }
    }

    class MyViewHolder(itemView: View, onClickListener: View.OnClickListener) : RecyclerView.ViewHolder(itemView) {

        val textView : TextView = itemView.findViewById(R.id.text)
        val expandButton : ImageView = itemView.findViewById(R.id.expand_btn)
        val expandArea : View = itemView.findViewById(R.id.expanded_area);
        val blur : View = itemView.findViewById(R.id.blur);

        init {
            expandButton.setOnClickListener(onClickListener)
            blur.setOnClickListener(onClickListener)
        }

        fun bind(item: Item, expandedItemIndex: Int) {
            textView.text = item.value.toString()
            expandButton.setTag(this)
            if(expandedItemIndex >= 0) {
                blur.visibility = View.VISIBLE
                if(expandedItemIndex == adapterPosition) {
                    blur.z = -1f
                    expandArea.visibility = View.VISIBLE
                    expandButton.setImageResource(android.R.drawable.arrow_up_float)
                } else {
                    blur.z = 0f
                    expandArea.visibility = View.GONE
                    expandButton.setImageResource(android.R.drawable.arrow_down_float)
                }
            } else {
                expandArea.visibility = View.GONE
                blur.visibility = View.INVISIBLE
            }
        }
    }
}

layout_row.xml

<RelativeLayout 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:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <FrameLayout
        android:id="@+id/cardHolder"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <androidx.cardview.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="5dp"
            app:cardCornerRadius="15dp"
            app:cardElevation="5dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"
                android:padding="10dp">

                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:gravity="center"
                    android:orientation="horizontal">

                    <TextView
                        android:id="@+id/text"
                        android:layout_width="0dp"
                        android:layout_height="wrap_content"
                        android:layout_weight="1"
                        android:text="Header Text" />

                    <ImageView
                        android:id="@+id/expand_btn"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:padding="8dp"
                        android:src="@android:drawable/arrow_down_float" />

                </LinearLayout>

                <LinearLayout
                    android:id="@+id/expanded_area"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:orientation="vertical"
                    android:visibility="gone"
                    tools:visibility="visible">

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Expanded Line 1"></TextView>

                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:text="Expanded Line 2"></TextView>

                </LinearLayout>
            </LinearLayout>
        </androidx.cardview.widget.CardView>
    </FrameLayout>

    <com.github.mmin18.widget.RealtimeBlurView
        android:id="@+id/blur"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignTop="@+id/cardHolder"
        android:layout_alignBottom="@id/cardHolder"
        app:realtimeBlurRadius="20dp"
        app:realtimeOverlayColor="#8000" />
</RelativeLayout>

MyAdapterJava.Java (Java implementaion of adapter)

public class MyAdapterJava extends RecyclerView.Adapter<MyAdapterJava.MyViewHolder> implements View.OnClickListener{

    List<Item> items = new ArrayList<Item>();
    int expandedItemIndex = -1;

    @NonNull
    @Override
    public MyAdapterJava.MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_row, parent, false);
        return new MyViewHolder(view, this);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    @Override
    public void onBindViewHolder(@NonNull MyAdapterJava.MyViewHolder holder, int position) {
        holder.bind(items.get(position), expandedItemIndex);
    }

    @Override
    public void onClick(View v) {
        if(v != null) {
            if(v.getId() == R.id.blur) {
                expandedItemIndex = -1;
                notifyDataSetChanged();
            } else if(v.getId() == R.id.expand_btn) {
                MyViewHolder holder = (MyViewHolder) v.getTag();
                if (expandedItemIndex == holder.getAdapterPosition()) {
                    expandedItemIndex = -1;
                    notifyDataSetChanged();
                } else {
                    expandedItemIndex = holder.getAdapterPosition();
                    notifyDataSetChanged();
                }
            }
        }
    }

    static class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textView;
        ImageView expandButton;
        View expandArea;
        View blur;

        public MyViewHolder(View itemView, View.OnClickListener onClickListener) {
            super(itemView);

            textView = itemView.findViewById(R.id.text);
            expandButton = itemView.findViewById(R.id.expand_btn);
            expandArea = itemView.findViewById(R.id.expanded_area);
            blur = itemView.findViewById(R.id.blur);

            expandButton.setOnClickListener(onClickListener);
            blur.setOnClickListener(onClickListener);
        }

        public void bind(Item item, int expandedItemIndex) {
            textView.setText(String.valueOf(item.getValue()));
            expandButton.setTag(this);
            if(expandedItemIndex >= 0) {
                blur.setVisibility(View.VISIBLE);
                if(expandedItemIndex == getAdapterPosition()) {
                    blur.setZ(-1f);
                    expandArea.setVisibility(View.VISIBLE);
                    expandButton.setImageResource(android.R.drawable.arrow_up_float);
                } else {
                    blur.setZ(0f);
                    expandArea.setVisibility(View.GONE);
                    expandButton.setImageResource(android.R.drawable.arrow_down_float);
                }
            } else {
                expandArea.setVisibility(View.GONE);
                blur.setVisibility(View.INVISIBLE);
            }
        }
    }
}

The result will be like this: enter image description here


PS:
1. Since this code is using View.SetZ(float) to handle Blur view Z position, it works on API 21 and above. For supporting lower API versions, you should add another Blur view before FrameLayout (cardHolder) view in layout file and instead of using View.setZ(float) method, make proper Blur view visible.
2. When an item is expanded and other items are blurred, scrolling of RecyclerView still works but it doesn't affect on the functionality of my answer. If you need to disable it, you should define a callback in your Adapter and call it in override fun onClick(v: View?) method to notify activity or fragment to disable/enable RecyclerView scrolling.

Mir Milad Hosseiny
  • 2,769
  • 15
  • 19
  • But I want the background of the card to be blurred, not the card itself... Putting the blurView after a View puts it in front of it, not behind it... – Nitzan Daloomy Feb 06 '20 at 21:35
  • I thought putting it before the very item template of the RecyclerView might put it behind it but in front of all the RecyclerView, but as you can see it's not working – Nitzan Daloomy Feb 06 '20 at 21:41
  • Do you want to blur the white area of your card? Could you please share a picture and show the area that you like to make it blur? – Mir Milad Hosseiny Feb 07 '20 at 04:52
  • I'd like to have everything but the expanded card blurred, as if the card was the dialog in the second picture (it's not the card, but in the second picture everything except the dialog is blurred. As the dialogs background is blurred, I want the expanded-cards background to be blurred)... What other pictures should I add? – Nitzan Daloomy Feb 07 '20 at 09:58
  • Do you like the final result like [this image](https://i.ibb.co/F4PmCTQ/blur.jpg)? – Mir Milad Hosseiny Feb 09 '20 at 19:23
  • yes!!! exactly! (and I want the blur to be a bit darker, and clickable, so clicking on it will shrink the card back and the blur will fade out) – Nitzan Daloomy Feb 09 '20 at 20:51
  • Look at my updated answer, I hope it will be useful. – Mir Milad Hosseiny Feb 10 '20 at 05:44
  • first of all, thanks a lot for helping! second, I don't know Kotlin yet, and converting it to Java using Android Studios tools doesn't make it readable for me either... can you please write the code in Java? and if you can please just update my adapter class that I shared the code of in the question... Anyway, you'll get the bounty because it seems like you've solved the problem and due to the lack of time 'till it ends, but I'll not give you the 'solved' mark yet... I really hope you'll help me understand the answer (: thanks a lot! – Nitzan Daloomy Feb 10 '20 at 11:44
  • I added the java implementation of my adapter. You can easily modify the `layout_row.xml` and `MyAdapterJava.java` to implement your desired UI. – Mir Milad Hosseiny Feb 10 '20 at 12:40
  • I finally had some time to try this, and I understood that this method cancels the animations... I want the card expansion to be animated... also I understood that it blurs each card separately... it can't be efficient, is it? can you please help me find a better-suited solution? (: – Nitzan Daloomy Feb 11 '20 at 21:35
  • You can add animation by yourself, or use one of the expandable adapter libraries. Yes it blurs each card separately but the final result is like what you need. – Mir Milad Hosseiny Feb 11 '20 at 22:52
  • it is what I need, but in case I'll have a lot of items on the list, won't it make the app crash or something? – Nitzan Daloomy Feb 12 '20 at 00:50
  • I did test with 100 simple items and I didn't face any problem. – Mir Milad Hosseiny Feb 12 '20 at 03:47
  • I won't give 'solve' mark to the answer, because it still doesn't work as I wanted it to... thank you for trying to help anyway! (: – Nitzan Daloomy Feb 13 '20 at 22:12
0

You can use the blurry library https://github.com/wasabeef/Blurry. Its the easiest way of making a blurred background.

With this library, you only had to use below piece of snippet

Blurry.with(context).radius(10).sampling(8).color(Color.argb(66, 255, 255, 0)).async().onto(rootView);

Hope this will fit perfect according to your requirement!!

Deeksha
  • 536
  • 5
  • 14
  • I saw this, but wasn't sure how to apply this on my rectclerview... Where should I use it, in the ViewHolder? And what's the rootView in this case? – Nitzan Daloomy Jan 29 '20 at 15:51
  • Plus, I didn't mention it but I want the card to shrink when clicking the blurred background, and I can't see how it works with this solution (If it actually solves the problem) – Nitzan Daloomy Jan 29 '20 at 20:35
  • @Deeksha this library does not work with androidX (renderscript has some serious issues on AndroidX, so pretty much no blurry library - and I tried at least 5-6 of them - works with projects using android X). – qkx Nov 13 '20 at 11:35