I stumbled upon something weird. I am using the same CardView component in more than one place in my app, all with the same elevation (3dp), and they all looks the same. However, for one scenario I need to embed the CardView inside a ScrollView. I didn't change anything in the CardView configuration, but the shadow now looks different.
This is my layout:
<ScrollView 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="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:padding="16dp"
android:scrollbars="none">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:clipToPadding="false"
app:cardCornerRadius="8dp"
app:cardElevation="3dp"
app:cardUseCompatPadding="true">
<!-- some other views-->
</androidx.cardview.widget.CardView>
</ScrollView>
In the picture below you can see on the right the CardView and on the left the two shadows: on the top the shadow generated by the CardView alone, on the bottom the one generated when the CardView is inside a ScrollView. As you can see the second one appears darker and thicker.
NOTE: The background color (#FAFAFA) is the same in both scenario.
For both cases, I would check the following properties as it could contribute to the difference you're seeing:
getTranslationZ()
getElevation()
getOutlineAmbientShadowColor() (this is only valid in API 28+)
getOutlineSpotShadowColor() (this is only valid in API 28+)
Plus, checking Layout inspector to see if there's any view that could affect to the rendering.
Also, this article from Sebastiano Poggi could be of help.
Related
I'm facing a tricky situation here and I don't know how to solve this problem.
In my project I have a custom BottomSheetDialogFragment and in the layout a FrameLayout to add or replace Fragments.
Now I have a Fragment and inside I have a RecyclerView with the height:="wrap_content" because I want the BottomSheetDialogFragment only use the necessary space. Everything looks great, the problem appear when I put another view inside of the same layout and set the RecyclerView bellow or above of that view.
The RecyclerView ignores the size of the other view (or views) and always grows to the max screen size, and then it's no possible to see a few elements and even scroll.
I saw a solution, some developers are suggesting to add paddingBottom equals to the height of the view. But in my case doesn't works because I want to have a dynamic solution.
Above I'll share a few images of the problem and GitHub Repository with a sample.
Thanks for your attention!
I've manage to do what you need just need to use this as your fragment_sample.xml:
<LinearLayout 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"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/rclItems"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"/>
<Button
android:id="#+id/btnAddMoreItems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/rclItems"
android:text="#string/add_1_item"/>
</LinearLayout>
Explanation
Using a LinearLayout gives you the ability to work with weight, and the vertical orientation allows you to place an item below the other. The weight on the recyclerview will increase the height of it as needed until filling the screen. The next item you add would be added to the recyclerview but you'll need to scroll the list to see it
The android developers blog says that :-
The scrolling containers in your bottom sheet must support nested scrolling .
Try changing your fragment_sample.xml as below to make the recyclerview scroll working and to make the add button persistent.
<?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">
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:id="#+id/next"
android:layout_above="#id/btnAddMoreItems"
android:layout_height="wrap_content">
<android.support.v7.widget.RecyclerView
android:id="#+id/rclItems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</android.support.v4.widget.NestedScrollView>
<Button
android:id="#+id/btnAddMoreItems"
android:layout_width="match_parent"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
android:text="#string/add_1_item"/>
</RelativeLayout>
Note: making bottomsheet layout a child view of CoordinatorLayout will allow you to get the implement BottomSheetBehavior and recieve its transitions callbacks .
So I'm learning app development for Android, and I'm a bit stuck. I'm trying to make a RecyclerView with CardViews, but there is just too much space between CardViews. This is what it looks like.
I'm going for more of the look that the Google app has with the feed.
This is more like what I'm shooting for.
Anyways, I've searched and searched on this website for a solution to my problem and nothing seems to be working for me. I'm hoping that someone can give me something that will actually work.
Here's the xml for my cardview:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
android:paddingBottom="10dp">
<android.support.v7.widget.CardView
android:id="#+id/cv"
android:layout_width="match_parent"
android:layout_height="120dp"
app:cardCornerRadius="10dp"
app:cardElevation="5dp"
app:cardUseCompatPadding="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="#+id/note_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="false"
android:layout_below="#+id/event_time"
android:textSize="24sp" />
<TextView
android:id="#+id/note_content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/note_title" />
</RelativeLayout>
</android.support.v7.widget.CardView>
The top and bottom spaces is caused by your padding
android:paddingBottom="10dp", remove this or reduce it
Decrease the PadingBotom of the root constraintLayout
you could put 3 or 4 dp instead of 10dp.
android:paddingBottom="10dp"
Also I do not know if the copy of your code was not good ... the constraintLayout must close at the end:
after this:
</android.support.v7.widget.CardView>
you must have this:
</android.support.constraint.ConstraintLayout>
From the documentation for CardView:
Before Lollipop, CardView adds padding to its content and draws shadows to that area. ... If you want CardView to add inner padding on platforms Lollipop and after as well, you can call setUseCompatPadding(boolean) and pass true.
In your layout, you have this attribute on your CardView tag:
app:cardUseCompatPadding="true"
This does the same thing as the method in the quote above.
As a result, even if you had zero margin on all sides of your cards, users would still see space in between them because of the inner padding behavior. If you want really tight spacing, you'll have to remove this attribute (though of course you'll still get the larger spacing pre-Lollipop).
I have a CardView within a ConstraintLayout.
This view is then inflated and added to a LinearLayout.
I would like the CardView to cast a shadow on the next CardView, but it seems to be clipped by the ConstraintLayout. How can I get the elevation shadow to show on the CardView without providing more padding or margins for it?
i.e. How can I cast the shadow on an adjacent item?
<LinearLayout>
<ConstraintLayout>
<CardView> <-- This view's shadow is cut
...
</CardView>
</ConstraintLayout>
<ConstraintLayout>
<CardView>
...
</CardView>
</ConstraintLayout>
<LinearLayout>
Try adding this comment inside your card view that's enough
card_view:cardUseCompatPadding="true"
How can I get the elevation shadow to show on the CardView without providing more padding or margins for it?
i.e. How can I cast the shadow on an adjacent item?
You can't, for both a practical reason and a conceptual one.
The practical reason comes down to how the Android framework draws CardView shadows. On older API levels, the card is inset within its own bounds and that extra space is used to draw the shadow. So even if you had two cards immediately next to each other, they'd still have "space" between them due to these insets. From the documentation:
Before Lollipop, CardView adds padding to its content and draws shadows to that area.
On Lollipop and higher, shadows are drawn within the parent's bounds, not within the CardView's bounds. Still, the parent will clip the shadow if there isn't enough space between the card's edges and the parent's edges to draw the whole shadow. In your example, each CardView is in a separate parent, so even if you put those parents immediately next to each other, each parent would still clip its card's shadow.
As for the conceptual reason, that's just not how shadows work (in nature). If you have two cards at the same elevation, even if they're right next to each other, the top card wouldn't cast a shadow on the bottom card.
Try to add
app:cardElevation="10dp"
In order to use app:cardElevation you have to add to your LinearLayout:
xmlns:app="http://schemas.android.com/apk/res-auto"
I tried this in a test project and the elevation should work this way. Below is my code:
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="ro.helpproject.funcode.help.MainActivity">
<android.support.constraint.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="50dp"
app:cardElevation="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TEST TEST" />
</android.support.v7.widget.CardView>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="50dp"
app:cardElevation="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TEST TEST" />
</android.support.v7.widget.CardView>
</android.support.constraint.ConstraintLayout>
</LinearLayout>
CardView needs padding around it for shadow to appear.
If app:cardUseCompatPadding="true" adds the shadow for you but you don't want the extra padding from 4 directions (LRTB), you need to add margin to the CardView yourself.
Enable Developer options -> Show layout boundaries and if you see that there is no empty space around your CardView. Then that's probably the reason shadow not appearing.
I have a relative layout with a margin and a floating action button that is nested inside this layout.
<?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="match_parent"
android:layout_margin="#dimen/activityMargin"
android:orientation="vertical"
android:clipToPadding="false">
<android.support.design.widget.FloatingActionButton
android:id="#+id/id_FABSave"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
app:srcCompat="#drawable/ic_save_white"/>
</RelativeLayout>
As you can see in the attached picture, the drop shadow of the floating action button is cut off. How does this happen and how can it be fixed?
In your relative layout tag, use padding instead of margin and add the attribute android:clipToPadding="false" to avoid the shadows being cut.
<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="match_parent"
android:padding="#dimen/activityMargin"
android:clipToPadding="false">
Problem is that shadows are cut by bounds of view or view group. To tackle this issue you have to use:
android:clipChildren
Defines whether a child is limited to draw inside of its bounds or not.
android:clipToPadding
Defines whether the ViewGroup will clip its children and resize (but not clip) any EdgeEffect to its padding, if padding is not zero.
Problem is that you have to set this to many views in xml if you want to render shadow. I resolved this issue on level of themes.xml. In my top level theme I just set:
<item name="android:clipChildren">false</item>
<item name="android:clipToPadding">false</item>
Then, if there is space on screen, shadow is rendered. I hope it won't break something else.
EDIT: It breaks some views. For example CameraPreview will set black background to whole screen. Be careful with scrolling views, etc.
I am using a cardview as the root of a custom view I am writing. I using the v7 support library. My XML looks like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="6dp"
card_view:cardElevation="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- some other views -->
</LinearLayout>
</android.support.v7.widget.CardView>
My problem is that I am getting a white border around my card view. It looks like it is there to indicate elevation as it is thicker on the right side. I've tried adjusting cardElevation and MaxCardElevation in my XML like so :
card_view:cardElevation="0dp"
and in code in my custom view that extends CardView and uses this layout:
setCardElevation(0);
setMaxCardElevation(0);
But the white border persists. I'm not sure how to get rid of it. If anyone had any input into why this is happening or suggestions on how I can remove the white border it would be appreciated. Thanks much.
I know it's a bit late, but for anyone having a similar problem:
I had the same issue: A white border was shown on pre-lollipop devices.
I solved it setting the cardPreventCornerOverlap to false on your XML.
Like this:
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginRight="6dp"
card_view:cardPreventCornerOverlap="false">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- some other views -->
</LinearLayout>
</android.support.v7.widget.CardView>
Support CardView doesn't support content clipping, because it's expensive on older devices. It's possible to clip content using Canvas.saveLayer/restoreLayer and PorterDuff modes. This is how Carbon implements rounded corners with correct content clipping. See the image:
I might be late in the game, but I had the same issue. Just wanted to share a simple solution!
My custom view extended a CardView and I had a margin applied to the root element (i.e. CardView) in the XML just like in the original question. This ended up giving me the extra white border like this:
Solution was to move the margin from root of the Custom View XML to the declaration of your custom view (check comments in the snippet)
Code snippet:
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/cvSettings"
style="#style/DefaultCardViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/default_margin" <!-- Remove this -->
app:cardElevation="#dimen/default_card_elevation">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
.
.
.
</android.support.v7.widget.CardView>
Ended up just moving over the margin to my custom view declaration which got rid of the extra white border:
<com.example.android.custom.MyCustomView
android:id="#+id/custom_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/default_margin" <!-- Move it here-->
cv:pt_widgetColor="#color/colorAccent" />
After the change, much cleaner :) :
use cardBackgroundColor="color" in xml card_view
sample code :
<android.support.v7.widget.CardView
android:id="#+id/card_view_khaterat"
android:layout_width="250dp"
android:layout_height="100dp"
android:layout_gravity="center_horizontal"
app:cardBackgroundColor="#f56f6c"/>