3

I'm trying to create a CardView layout which will be re-used to display a tweet on the profile page and in a RecyclerView on the home page. The difference is that the RecyclerView will inflate the layout itself and the profile page will use a custom view.

However, when using the custom view on the profile page it creates an additional CardView behind it. My best guess is that this happens because the custom view extends from the CardView class and the RecyclerView of course does not extend this class.

So what I end up with is a CardView with my layout inside of it like shown on the image below: layout in CardView

While in the RecyclerView it looks as it should: layout inflated in RecyclerView

I also tried extending the custom-view class with LinearLayout and FrameLayout instead of CardView but with the CardView still as the root in tweet.xml. This got rid of the problem partially, but even with setClipToPadding(false) and setClipChilderen(false) it still would cut of part of the CardView's shadow border.

I have tried extending from CardView while having the root in tweet.xml be a LinearLayout or FrameLayout and doing styling inside Java. But this resulted in another 'flaw' when using the card radius dimen value with setRadius(R.dimen.card_corner_radius) (which was 8dp). And I ended up with this.

So how do I keep the CardView as the root in my xml file while using it as a custom-view?

tweet.xml Preview

<?xml version="1.0" encoding="utf-8"?>
<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:elevation="2dp"
    app:cardCornerRadius="@dimen/card_corner_radius"
    app:contentPaddingBottom="8dp"
    android:foreground="?attr/selectableItemBackground"
    android:layout_marginStart="@dimen/card_spacing_horizontal"
    android:layout_marginEnd="@dimen/card_spacing_horizontal"
    android:layout_marginTop="@dimen/card_spacing_vertical"
    android:clickable="true"
    android:focusable="true"
    style="@style/CardView">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <ImageView
            android:id="@+id/image"
            android:layout_width="match_parent"
            android:layout_height="128dp"
            android:scaleType="centerCrop"
            android:src="@drawable/ic_default_image"
            android:foreground="?attr/selectableItemBackground"
            android:visibility="visible" />

        <!-- Rest of layout -->

    </android.support.constraint.ConstraintLayout>

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

TweetView.java

public class TweetView extends CardView {

    public TweetView(Context context) {
        this(context, null /* attrs */);
    }

    public TweetView(Context context, AttributeSet attrs) {
        this(context, attrs, R.style.AppTheme);
    }

    public TweetView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        LayoutInflater inflater = LayoutInflater.from(context);
        inflater.inflate(R.layout.tweet, this);
    }
}

fragment_profile.xml Preview

<?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"
    tools:context=".fragments.ProfileFragment">

    <nl.robinpfeiffer.parrot.twitter.views.TweetView
        android:layout_width="250dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"/>

</LinearLayout>

EDIT

I tried changing the code of TweetView.java like @Eminem suggested. But the CardView still stays the same; nested in another CardView.

TweetView.java Updated

LayoutInflater mInflater;

public TweetView(Context context) {
    super(context);
    mInflater = LayoutInflater.from(context);
    init();
}

public TweetView(Context context, AttributeSet attrs) {
    super(context, attrs);
    mInflater = LayoutInflater.from(context);
    init();
}

public TweetView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    mInflater = LayoutInflater.from(context);
    init();
}

private void init() {
    View v = mInflater.inflate(R.layout.tweet, this, true);
}

EDIT 2:

I didn't pay very much attention when the java styling failed and forgot that it should have been setRadius(getResources().getDimension(R.dimen.card_corner_radius)); instead of setRadius(R.dimen.card_corner_radius);, as the latter one uses the resource integer id value for the radius. Hence the way too rounded corners.

SMR
  • 6,628
  • 2
  • 35
  • 56
  • by compound you mean reusable layout? –  May 16 '18 at 12:52
  • @Eminem Yes, it is used for the profile and the homescreen. – Robin Pfeiffer May 16 '18 at 13:01
  • I dont get it where is your difficulty? –  May 16 '18 at 13:03
  • @Eminem In this [case](https://i.stack.imgur.com/06m67.png) it creates an additional `CardView` around the layout without having specified that anywhere in the code. That is what I'm trying to prevent. – Robin Pfeiffer May 16 '18 at 13:19
  • https://stackoverflow.com/a/22780035/9025311 –  May 16 '18 at 14:05
  • @Eminem So I tried that, but the problem stays the same. There is still a `CardView` around the `CardView` that my layout creates. – Robin Pfeiffer May 16 '18 at 14:25
  • then remove thia ` –  May 16 '18 at 15:06
  • @Eminem that is my layout, so I don't really see how removing it would be useful since I then don't have the compound-view anymore. And thus leaving me with an empty fragment. – Robin Pfeiffer May 16 '18 at 15:27
  • because when you extend it from Cardview class it already creates one but you do already in your layout file,give it a try –  May 16 '18 at 18:13
  • @Eminem I tried that but then styling via Java doens't work as well as via xml, see [this picture](https://i.stack.imgur.com/9flIA.png). Anyway I think that If I just remove the `CardView` from the layout and extend the `tweet.xml` layout from `ConstriantLayout` and just put that view in a `CardView` when I need a card around it. Essentially side-stepping the problem. OR is there an option to disable generating an extra card when extending from `CardView`? – Robin Pfeiffer May 16 '18 at 18:55
  • use `merge`instead of a `LinearLayout` –  May 16 '18 at 19:05

1 Answers1

2

So I finally fixed it, like @Eminem suggested, with a <merge> tag. Because I was extending my TweetView class from CardView, it automatically creates a CardView. In TweetView the layout tweet.xml get's inflated via inflate(getContext, R.layout.tweet, this) Styling is now handeled in the styles.xml files by creating a custom card style. This style needs to be applied where you use the TweetView class.

My resulting layout and class files are as follows:

tweet.xml

    <?xml version="1.0" encoding="utf-8"?>
<merge 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"
    tools:parentTag="android.support.v7.widget.CardView">

    <android.support.constraint.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clickable="true"
        android:foreground="?attr/selectableItemBackground"
        android:paddingBottom="8dp"
        android:focusable="true">

        <!-- Rest of internal layout -->

    </android.support.constraint.ConstraintLayout>

</merge>

TweetView.java

public class TweetView extends CardView implements Observer {

public TweetView(Context context) {
    super(context);
    init();
}

public TweetView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
}

public TweetView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
}

private void init() {
    inflate(getContext(), R.layout.tweet, this);
}

styles.xml

   <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="android:navigationBarColor">@color/colorPrimaryDark</item>
        <!-- Base theme items -->
    </style>

    <style name="CardTweet" parent="CardView">
        <!-- Custom card items -->
        <item name="cardCornerRadius">@dimen/card_corner_radius</item>
    </style>

And finally this is how I used the class in a standard Fragment and a RecyclerView:

fragment_profile.xml

<?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="match_parent"
    tools:context=".fragments.ProfileFragment">

        <nl.robinpfeiffer.parrot.twitter.views.TweetView
            android:layout_width="250dp"
            android:layout_height="wrap_content"
            android:layout_margin="16dp"
            android:layout_marginStart="8dp"
            android:layout_marginTop="8dp"
            style="@style/CardTweet"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

fragment_tweet.xml this is a RecyclerView item that get's inflated in the RecyclerViewAdapter

<?xml version="1.0" encoding="utf-8"?>
<nl.robinpfeiffer.parrot.twitter.views.TweetView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    style="@style/CardTweet"
    android:layout_marginEnd="@dimen/card_spacing_horizontal"
    android:layout_marginStart="@dimen/card_spacing_horizontal"
    android:layout_marginTop="@dimen/card_spacing_vertical" />

Fixed card, normal layout

Fixed card, RecyclerView