I am using a CardView as element inside a RecyclerView. When doing so android automatically generates margins between the cardView and the screen and between different cardViews.
<android.support.v7.widget.CardView
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:background="#color/galleryCardBGColor"
app:cardCornerRadius="2dp" >
<android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical" />
I followed the instructions in the link to integrate them into my project:
using-recyclerview-and-cardview-in-eclipse-adt
I had been previously using a linearlayout for the list element :
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
This was working perfectly fine, with no margins whatsoever between the list elements. I have now just placed the linear layout inside the cardView which has resulted in extra margins.
The reason being that i want to provide exact margins to these elements, and any margins I supply now is being added over to this preexisting margins.
I have tried supplying zero/negative paddings/margins to the cardView element but none of these work.
Any idea, I can remove these margins or otherwise know exactly how much margin is being added.
did you check if it is margin or padding? (Dev Options / show layout bounds)
CardView adds padding in platforms pre-L to draw shadows. In L, unless you set useCompatPadding=true, there should not be any gap.
Adding negative margins (although it is ugly) should work. If it is not, please add some more code on how you are adding them and how you are setting up the RecyclerView.
It worked for me. Use:
card_view:cardElevation="0dp"
card_view:cardMaxElevation="0dp"
Just described in #yigit 's answer, the CardView will add a default padding to draw it's shadow before Android L. The padding size is described in CardView's doc.
I found the way to clear this padding (also to add padding for content) is to use CardView's contentPaddingLeft(/Right/Top/Bottom) attributes.
If you want to clear the default padding, you can set the contentPadding to minus value. If you want to add content padding, set the contentPadding to the value you want.
In my case, I use these code to clear the default padding:
card_view:contentPaddingLeft="-3dp"
card_view:contentPaddingRight="-3dp"
card_view:contentPaddingTop="-3dp"
card_view:contentPaddingBottom="-3dp"
I tried #Shubham 's answer, but an IllegalStateException is thrown in RadialGradient.java with message the "radius must be > 0".
Use this two tags below:
card_view:cardPreventCornerOverlap="false"
card_view:cardUseCompatPadding="true"
add card_view:cardPreventCornerOverlap="false" to your card
It works only if your Image has Rounded Corners
I know its late but i hope it will help someone
app:cardPreventCornerOverlap="false"
setting app:cardPreventCornerOverlap to false will do the trick :)
Well, seems there is a much easier way to do it, without guessing the padding and all:
card_view:cardPreventCornerOverlap="false"
or using java:
cardView.setPreventCornerOverlap(false)
The comment from #vj9 under the accepted question helped me to calculate the offsets. Sharing it here because it might help someone else:
public static int getVerticalCardViewOffset(Context context) {
Resources res = context.getResources();
int elevation = res.getDimensionPixelSize(R.dimen.cardview_default_elevation);
int radius = res.getDimensionPixelSize(R.dimen.cardview_default_radius);
return (int) (elevation * 1.5 + (1 - Math.cos(45)) * radius);
}
public static int getHorizontalCardViewOffset(Context context) {
Resources res = context.getResources();
int elevation = res.getDimensionPixelSize(R.dimen.cardview_default_elevation);
int radius = res.getDimensionPixelSize(R.dimen.cardview_default_radius);
return (int) (elevation + (1 - Math.cos(45)) * radius);
}
use
android:adjustViewBounds="true"
on the view you want wrapped in the cardview
If you don't need the corner radius or elevation downlevel, consider using FrameLayout on pre-Lollipop.
<include layout="#layout/card_view_compat" />
layout/card_view_compat
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<include layout="#layout/content" />
</FrameLayout>
</merge>
layout-v21/card_view_compat
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:cardCornerRadius="3dp"
app:cardElevation="4dp"
>
<include layout="#layout/content" />
</android.support.v7.widget.CardView>
</merge>
myCardView.setShadowPadding(0,0,0,0);
Related
I have the following structure in my fragment layout file:
- ScrollView
- ConstraintLayout
- CardView
- *some stuff here*
- CardView
- ListView
- *list header*
- *list items generated with a custom adapter*
If I remove the outer ScrollView, I can see the whole content of the ListView, and if it's bigger than the remaining space for the 2nd CardView, I can scroll it. The 1st CardView stays in place, but the content of the 2nd one is scrollable.
However, I would like to scroll the whole fragment. I would like the 2nd CardView to expand and contain the whole ListView, and if I scroll up or down, the 1st one moves as well.
I tried several combinations of height settings. No point of showing you my actual layout XML, because it's a mess. I would like a clean slate. Is it possible to achieve?
EDIT:
I know the ListView is a scroll container itself, but I think it's a pretty common need to scroll the whole thing, so I can't understand why it's so hard to make it work.
Alright, after combining multiple answers, I have the solution that I needed.
First
I needed to use a NestedScrollView instead of a regular ScrollView.
It solves the conflict between the two scroll containers (ScrollView and ListView).
Reference: Android: ScrollView vs NestedScrollView
NOTE: My list content is dynamic, so it can be too short to fill the remaining space. I had to set android:fillViewport="true" on the NestedScrollView. If the list is longer than the remaining space, it will not cause any trouble.
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView
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:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:id="#+id/card1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- NOTE: constraints properties are missing from here for easier reading -->
<!-- card content here -->
</androidx.cardview.widget.CardView>
<androidx.cardview.widget.CardView
android:id="#+id/card2"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- NOTE: constraints properties are missing from here for easier reading -->
<ListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<!-- NOTE: this will change in step 3 -->
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
Second
Following the steps above will make the ListView collapse to the height of its first item. To solve this, I needed to create a subclass from ListView and override its onMeasure() method, so it can calculate the proper height at runtime.
Reference: Android - NestedScrollView which contains ExpandableListView doesn't scroll when expanded
NonScrollListView.java
package my.package.name
import ...
public class NonScrollListView extends ListView {
// NOTE: right click -> create constructors matching super
#Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int heightMeasureSpec_custom = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, heightMeasureSpec_custom);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
Third
I needed to use my custom View instead of the regular ListView in my layout XML.
layout.xml excerpt
<my.package.name.NonScrollListView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
This way I managed to make it work. Both cards move together on scroll, even if I tap the ListView area.
I don't know whether it causes performance issues with really long lists, because mine contains a few dozen items at most, but I have no problem on a low end Galaxy A20e.
Have recycler with CardView items. When screen open - I start alpha animation on recycler :
recyclerView.animate().alpha(1f).setStartDelay(300).start()
Recycler item:
<android.support.v7.widget.CardView
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:layout_marginStart="20dp"
android:layout_marginTop="6dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="6dp"
app:cardCornerRadius="10dp"
app:cardElevation="4dp">
...
</android.support.v7.widget.CardView>
Problem that while this animation running the shadow of CardView directed to the up but when animation ends shadow change direction to the bottom (as normal)
Problem was found only on Android 9.
P.S. sorry for my english)
I have found a partial fix,
If you add android:layerType="hardware" to the parent view the shadow no longer flips direction.
The down side is the shadow permanently points in the wrong direction.
Found a workaround.
Make sure the alpha-animation targets on CardView itself instead of its parent and its parent's alpha must be 1F.
This work in my case.
Hope it can help you too.
Here is a screenshot of what is happening:
For some reason, the last card of the RecyclerView is not showing up properly. This is a weird occurrence especially since the RecyclerView is just wrapping content.
<android.support.v7.widget.RecyclerView
android:id="#+id/cardList"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dp"
android:layout_marginLeft="8dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
Why might something like this occur? How can I make it so that the RecyclerView is not cut off?
Solved by poss: https://stackoverflow.com/users/4048794/poss
choosing wrap_content as the attribute for layout_width and layout_height causes the cut-off on RecyclerViews
RecyclerView cannot have WRAP_CONTENT as layout_height.
The solution is to set layout_height to a specific value like "100dp", like the following:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="100dp"
...
/>
Now if the value in the xml is not what you want, you can set layout_height in the code using requestLayout();
I have a RecyclerView similar to Marcin Orlowski's.
Now it works for me after I applied the solution of setting the height of layout_height in Java using requestLayout.
It cannot be set in onCreate method, however. It needs to be set in onWindowFocusChanged method.
#Override
public void onWindowFocusChanged (boolean hasFocus) {
DisplayMetrics dm = new DisplayMetrics();
this.getWindowManager().getDefaultDisplay().getMetrics(dm);
int[] loc = new int[2];
recyclerView.getLocationOnScreen(loc); // get the location of the recyclerview
int distance_to_bottom = dm.heightPixels - loc[1]; // similar to Marcin's design
// RecyclerView 直到版面的最下端
ViewGroup.LayoutParams params = recyclerView.getLayoutParams();
params.height = distance_to_bottom;
recyclerView.requestLayout(); // set it right here
}
I had the same problem.Then i try to add some space in the bottom of the recyclerview. If you try all the above solutions and still got the issue, then try this one.
add
android:clipToPadding="false"
to your recyclerview code . like this
<RecyclerView
android:id="#+id/my_rv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="10dp"
android:clipToPadding="false" />
Same happened to me, so I just added three main inside my recycler View:
First of all, give your recycler View height and width as: match parent
Secondly give bottom padding, as paddingBottom = "10dp"
Include, clipToPadding = "false"
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/postRV"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="10dp"
android:clipToPadding="false"
android:layout_marginTop="80dp"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
I am working with CardViews inside a RecyclerView with GridLayoutManager. The problem that I am facing is, the height I specify for the card view inside xml or in java, it doesn't get enforced by the CardView. It seems like the cumulative height of the child views of the CardView becomes the height of the CardView. This behavior considering the fact that it's parent's layout parameters that are enforced on its child views.
What am I missing? Am I doing something wrong?
<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:id="#+id/cardView"
android:layout_width="#dimen/card_view_width"
android:layout_height="0dp"
android:layout_marginBottom="6dp"
android:elevation="10dp"
android:orientation="horizontal"
card_view:cardCornerRadius="2dp"
>
<ImageView
android:layout_width="#dimen/card_view_width"
android:layout_height="10dp"
></ImageView>
</android.support.v7.widget.CardView>
In this case the height of the card view is 0dp but still the layout designer preview & when the app runs on the device, the size of the card is 10dp.
Best Regards
What do you expect to happen, with what you told the program?
You tell the CardVeiw to have a height of 0dp with this:
android:layout_height="0dp"
Then you place an ImageView inside, and tell it to have a height of 10dp.
So of course the CardView makes itself taller to allow the ImageView to fit properly.
If you want the inner elements to match the height of the CardView, set the layout_height of the CardView to whatever you want, like 20dp:
android:layout_height="20dp"
Then have the inner elements match_parent:
android:layout_height="match_parent"
Why are you setting visibility to 0? IF you are trying to hide it, use `visibility = View.GONE'.
ALso, don't use elevation which won't work before L. Insteed, use cardElevation so that it will work before L as well.
Try to set android:layout_height="wrap_content" in your CardView.
I need to add to add ListView with complicated items background: different for even/odd and rounded corners at the top and bottom. It looks like this:
I have implemented all this stuff via level-list, but there is one more thing I want to do.
Now the bottom item is near the bottom of the screen. It is better to add some space.
I don't want to add bottom margin to ListView, I need margin only for last item.
The ways I see to do this:
Footer
A kind of hack – add footer with empty TextView to ListView. But footers are quite unstable things, they usually disappear after notifyDataSetChanged and there is no way to get them back
Image with transparent pixels
I asked designer to add transparent pixels to bottom background resource. Unfortunately, in this case vertical centering is completely broken.
For example, there is 9patch like this:
And layout like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<!-- View with background with transparent pixels on bottom -->
<LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content"
android:id="#+id/item"
android:background="#drawable/some_bgr"
android:padding="10dp"
>
<TextView android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1"
android:text="Title"
android:layout_gravity="center"
android:textSize="18sp"
/>
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content"
android:text="Detail"
android:layout_gravity="center"
android:textSize="18sp"
/>
</LinearLayout>
<!-- Just for marking place took by view -->
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"
android:layout_below="#id/item"
android:background="#88ff55"
/>
</RelativeLayout>
The result:
As you see, centering is not working. Unfortunately.
(BTW, if specify this 9patch as background for TextView, centering works good. If you know any article, explaining this, please let me know.)
Add bottom margin to last item in Adapter implementation
That should work, but for unknown reason I still can't get it work.
I don't like this way, because I don't like to modify dimensions in code.
So
There is already imaginary way – construct some XML drawable with particular bitmap and margin. According to drawables concept it should be possible, but I can't find implementation. May be somebody knows?
Any other ideas?
In your ListView, set a paddingBottom and clipToPadding="false".
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="8dp"
android:clipToPadding="false"
android:scrollbarStyle="outsideOverlay"/>
This also works for RecyclerView.
Only use android:scrollbarStyle="outsideOverlay" if you want the scroll bar to not overflow into the padded area.
add an empty footer in your list like this:
TextView empty = new TextView(this);
empty.setHeight(150);
listview.addFooterView(empty);
you can also do it from code if you want, for example here I react to
to EditText different situations:
if(s.toString().length()>0)
{
contacts_lv.setClipToPadding(false);
contacts_lv.setPadding(0,0,0,270*screenDensity);
}
else
{
contacts_lv.setClipToPadding(true);
contacts_lv.setPadding(0,0,0,0);
}
Clocksmith's answer is the best and pretty clever. You can also create an empty footer view.
Add these two lines in your listView XML code:
android:transcriptMode="alwaysScroll"
android:stackFromBottom="true"
Another solution might be that you make a mock view with certain height.
In your adapter in getViewCount return 2.
In getCount return yourData.size+1.
In getViewType check if the element is last element return 2;
Use this type in getView to populate the mockview.
I guess you want to add margin only to last item:
So you can do in this manner, in your getview method the index of the list item and check if its the last item, then progrmatically add margin to the view.