I am attempting to add rounding and shadows to some views on an app and am utilizing the card view library to achieve that. It is looking good on lollipop devices but am running into compatibility issues with anything pre-lollipop.
I will preface this by saying that I have looked at the answers in the questions below have found that none of them are working for me.
Appcompat CardView and Picasso no rounded Corners
Cardview - white border around card
Unnecessary padding in CardView?
The most popular answer was to add the attribute 'cardPreventOverlap=false' but this removes the rounded corners. I have tried variations of this flag and 'cardUseCompatPadding="true"' but none of them seem to do the trick. Has anyone else run into the same problem?
My code:
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
card_view:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/selector"
android:gravity="center"
android:orientation="horizontal" >
<ImageView
android:layout_width="44dp"
android:layout_height="match_parent"
android:layout_marginRight="4dp"
android:background="#color/mid_yellow"
android:padding="0dp"
android:src="#drawable/ic_add_white_24dp" />
<TextView
style="#style/Text.Primary.White"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:text="Button" />
</LinearLayout>
This is how it currently looks on Android 5.0:
The exact same code on 4.4.2 displays as:
With 'cardPreventOverlap=false':
Update Unfortunately we were not able to solve the issue; given that the app only had small install base pre5.0 we decided it was not important. We ended up going with the third option 'cardPreventOverlap=false'.
Content clipping is not supported, because is quite expensive on older devices. If you wish, you can use Carbon. It has its own CardView implementation, which correctly clip content to rounded corners. Carbon also adds content clipping and elevation to all other layouts so for your purpose you can use a LinearLayout with rounded corners and shadow. See the image:
Instead of using card_view:cardCornerRadius you can set the background of the cardview with a drawable like:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#FFFFFF"/>
<corners android:radius="4dip"/>
</shape>
You can also do this for each element in your cardview such that you have for each element the correct background color.
In that case you can specify only certain corners with:
<corners
android:topLeftRadius="4dp"
android:bottomLeftRadius="4dp"/>
For your + button for example.
Use this in combination with cardUseCompatPadding="true"
Related
I have a CardView defined as
<androidx.cardview.widget.CardView
android:layout_width="70dp"
android:layout_height="70dp"
app:cardCornerRadius="35dp"
app:cardElevation="10dp">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="fitXY"
android:src="#drawable/ic_user_default"/>
</androidx.cardview.widget.CardView>
But somehow I am getting this weird shadow, which I am not able to figure out why?
I wanted to have a circular CardView with a circular shadow around it, but instead I am getting a shadow in the right bottom corner.
What am I missing out?
I tried few things and this is what I concluded.
CardView is inside a RelativeLayout which has wrap_content as layout_height. Which I guess is wrapping the CardView only, without it's shadow. Take a look at below example.
<?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"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:layout_width="70dp"
android:layout_height="70dp"
app:cardCornerRadius="35dp"
app:cardElevation="10dp">
</androidx.cardview.widget.CardView>
</RelativeLayout>
<androidx.cardview.widget.CardView
android:layout_width="70dp"
android:layout_height="70dp"
app:cardCornerRadius="35dp"
app:cardElevation="10dp">
</androidx.cardview.widget.CardView>
</LinearLayout>
Which will show like
So that leads to a question why it's wrapping around the CardView but not CardView with its shadows?
And one side question. How do you center the shadow of CardView? If you look closely to the second CardView, the shadow is has a gravity to bottom.
I suggest you take a look at android documentation for shadows.
You said:
CardView is inside a RelativeLayout which has wrap_content as layout_height. Which I guess is wrapping the CardView only, without it's shadow. So that leads to a question why it's wrapping around the CardView but not CardView with its shadows?
The following answers why:
Shadows are drawn by the parent of the elevated view, and thus subject to standard view clipping, clipped by the parent by default.
Also:
The bounds of a view's background drawable determine the default shape of its shadow. Consider this view, defined with a background drawable:
<TextView
android:id="#+id/myview"
...
android:elevation="2dp"
android:background="#drawable/myrect" />
The background drawable is defined as a rectangle with rounded corners:
<!-- res/drawable/myrect.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="#42000000" />
<corners android:radius="5dp" />
</shape>
The view casts a shadow with rounded corners, since the background drawable defines the view's outline. Providing a custom outline overrides the default shape of a view's shadow.
This you have already achieved with app:cardCornerRadius="35dp" rather than a custom background drawable (which is equally acceptable, although I thought adding this bit of info might be of some additional help if you need it for other views in the future).
To answer the question How do you center the shadow of CardView? you may take a look at the material design guidelines. According to Material Design, shadows should come from both ambient light (front light source) and a key light (top light source):
The elevation of these light sources are by default 90 degrees and 45 degrees respectively in the android framework, and cannot be changed because that would be inconsistent with material design. However, if you want to create a custom shadow with a custom angle, you could use a gradient drawable and set that as a shadow as described here under the head Using Shape Drawable (New way to implement shadow).
Basically, you need to use the setShadowLayer method from the android.graphics.Paint class.
Hope this helps!
The deviation of CardView's shadow depends on its location in screen. As you can see in below picture, when the CardView is placed in left or right side of the screen, its shadow is deviated to left or right side too.
However, AFAIK, we have no control on the CardView's shadow perspective since there is no attribute for it to change. If you want to have a custom direction shadow, you should do it yourself.
I need to create white rectangle shape with red border (width 1px) and another red rectangle attached to it from below. Like this:
White rectangle will be EditText while red border and red rectangle will appear in order to notify the user about input errors. Nothing fancy.
I have already tried several approaches, but none of them looked good due to the same reason - the color changes towards the edge of a shape (looks like the outermost pixels are the ones that change). The issue can be seen in this magnified picture:
The hierarchy on this particular snapshot is LinearLayout with red background and padding of 1px, but I observed the same effect while attempted to use other approaches as well (including drawing the border around EditText programmatically).
This effect looks like the standard of Android, and my question is how can I disable it?
Check following example, hope it will help you.
rect_shape.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="3dp"
android:color="#EF9A9A"/>
<solid android:color="#android:color/white"/>
</shape>
layout.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/rectangle_shape"
android:orientation="vertical">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:lines="3"
android:padding="5dp"/>
<View
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="#EF9A9A"/>
<TextView
android:layout_width="match_parent"
android:layout_height="36dp"
android:layout_marginBottom="3dp"
android:layout_marginLeft="3dp"
android:layout_marginRight="3dp"
android:background="#EF5350"/>
</LinearLayout>
Output:
Just in case anybody stumbles upon this question - the described behavior was some emulator related artifact. On real devices the colors were just fine.
I am trying to add Ripple Effect to RecyclerView's item. I had a look online, but could not find what I need. I assume it has to be a custom effect. I have tried android:background attribute to the RecyclerView itself and set it to "?android:selectableItemBackground" but it did not work.:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusable="true"
android:clickable="true"
android:background="?android:selectableItemBackground"
android:id="#+id/recyclerView"
android:layout_below="#+id/tool_bar"/>
This is the RecyclerView that I am trying to add the effect to:
I figured out. The only thing that I had to do is to add this attribute:
android:background="?android:attr/selectableItemBackground"
to the root element of the layout that my RecyclerView adapter inflates like that:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:background="?android:attr/selectableItemBackground"
tools:background="#drawable/bg_gradient">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="17sp"
android:layout_marginLeft="15dp"
android:layout_marginStart="15dp"
android:id="#+id/shoppingListItem"
android:hint="#string/enter_item_hint"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/shopping_list_item_checkbox_label"
android:id="#+id/shoppingListCheckBox"
android:layout_centerVertical="true"
android:layout_marginRight="15dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:checked="false"/>
</RelativeLayout>
Result:
If you are still not able to see ripple effect, add these lines also to the root element of the layout.
android:clickable="true"
android:focusable="true"
As already answered, the simplest solution is to just add one of the following as your RecyclerView row's background:
android:background="?android:attr/selectableItemBackground"
android:background="?attr/selectableItemBackground"
However if you are experiencing problems with this method or if you want finer control over the colors, then you can do the following.
Custom Ripple Effect
This answer is starting with this simple Android RecyclerView example. It will look like the following image.
Add selector for pre API 21 devices
Before API 21 (Android 5.0 Lollipop), clicking a RecyclerView item just changed its background color (no ripple effect). That is what we are going to do, too. If you still have users with those devices, they are used to that behavior, so we aren't going to worry about them too much. (Of course, if you really want the ripple effect for them, too, you could use a custom library.)
Right click your res/drawable folder and choose New > Drawable resource file. Call it custom_ripple. Click OK and paste in the following code.
custom_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="#color/colorAccent" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<solid android:color="#android:color/transparent" />
</shape>
</item>
</selector>
I used colorAccent as the highlight color for the pressed state because it was already available, but you can define whatever color you want.
Add Ripple Effect for API 21+ devices
Right click your res/drawable folder and choose New > Drawable resource file. Call it custom_ripple again. Don't click OK, yet this time, though. From the Available qualifiers list choose Version, then click the >> button and write 21 for the Platform API level. Now click OK and paste in the following code.
v21/custom_ripple.xml
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/colorAccent">
<item
android:id="#android:id/mask"
android:drawable="#android:color/white" />
</ripple>
Again, I used colorAccent for the ripple color because it was available, but you can use whatever color you want. The mask confines the ripple effect to just the row layout. The mask color apparently doesn't matter so I just used an opaque white.
Set as the background
In your RecyclerView item's root layout, set the background to the custom ripple that we created.
android:background="#drawable/custom_ripple"
In the example project that we started with, it looks like this:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#drawable/custom_ripple"
android:padding="10dp">
<TextView
android:id="#+id/tvAnimalName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp"/>
</LinearLayout>
Finished
That's it. You should be able to run your project now. Thanks to this answer and this YouTube video for help.
I think there is one small detail that is missed.
If you still do not get the ripple effect after adding android:background="?android:attr/selectableItemBackground" try adding these following lines in the root of the layout.
android:clickable="true"
android:focusable="true"
These will make sure that the view is clickable and will enable the ripple effect with the background attribute mentioned above
add this lines in your adapter xml root view
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
A simple and custom approach is to set a view theme as outlined here.
some_view.xml
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackgroundBorderless"
android:focusable="true"
android:src="#drawable/up_arrow"
android:theme="#style/SomeButtonTheme"/>
some_style.xml
<style name="SomeButtonTheme" >
<item name="colorControlHighlight">#color/someColor</item>
</style>
Other custom implementations may be found here.
Using a Button Style
This has worked for me countlessly.
Add the Borderless Button Style to the root element of your layout.
There's no need for focusable or clickable attributes, the default styling encapsulates all that for you.
<androidx.constraintlayout.widget.ConstraintLayout
style="#android:style/Widget.Material.Button.Borderless"
android:layout_width="match_parent"
android:layout_height="wrap_content">
I'm trying to draw a box around two TextView objects in Android. Prior to this I had the two TextViews in a LinearLayout, but I'm working on flattening my view hierarchy to improve performance. When they were in the LinearLayout, the background showed up as desired, but when I create an empty layout with the same dimensions, I don't see the background show up at all.
My guess is this is because there isn't anything in the FrameLayout (I also tried an empty LinearLayout). I also tried setting this value at run-time, but same result.
This works fine in the layout editor in Eclipse, but when running on my tablet, the background just isn't there.
Any suggestions to accomplish the same thing or any suggestions for what I'm missing would help. Here's the shape:
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid android:color="#508E8F8E" />
<stroke
android:width="1dip"
android:color="#8E8F8E" />
<corners android:radius="4dp" />
</shape>
And here's the layout:
<FrameLayout
android:id="#+id/box_around_text"
android:layout_width="35dp"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:background="#drawable/background_gray"/>
<TextView
android:id="#+id/first_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:text="first_text" />
<TextView
android:id="#+id/second_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="#id/first_text"
android:text="second_text" />
The issue was that the shape must have real dimensions defined somewhere. I can make it show up if I define a size in the shape itself or if I use numbered dimensions in the layout. It would never show up if I used the 'wrap_content' or 'match_parent' options.
I upgraded to Android 4.2 and all of a sudden the linear gradient on my home widget isn't behaving as intended. instead of a gradient i seem to get a constant band. then if i restart the device the gradient starts "working" again, which is very puzzling. I have talked to other team members and their devices don't seem to have an issue. Here is a screen shot of the problem:
The gradient is applied over the bottom half of the image and should go from semi-transparent at the bottom to fully transparent at the top. Instead you can see a basically constant semi-transparent area at the bottom half of the image. The layout is something like:
<RelativeLayout>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:background="#drawable/gradient"
android:orientation="vertical" >
...
while the gradient background is
<shape>
<gradient
android:angle="90"
android:startColor="#D0000000"
android:endColor= "#00000000"
android:type="linear" />
</shape>
i found that forcing s/w acceleration on the linear layout (setting android:layerType="software") fixed the issue.