CardView& Source code analysis

Keywords: Android xml Attribute Google

Preface

CardView as a card control was introduced in Android 5.0 system, inherited from the effect of adding rounded shadows in FragmentLayout layout. Google introduced MD design Elevation and Z-axis displacement in 5.0. The purpose is to highlight the hierarchical relationship between different elements and to be more cool when displaying lists or grids. S Go!

Effect ~


The Simple Application of Part 1 CardView Card

To configure

  1. dependencies {  
  2.     compile fileTree(include: ['*.jar'], dir: 'libs')  
  3.     androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {  
  4.         exclude group: 'com.android.support', module: 'support-annotations'  
  5.     })  
  6.     compile 'com.android.support:cardview-v7:25.0.1'  
  7. }  

Code:

  1. <android.support.v7.widget.CardView  
  2.     android:id="@+id/cardview"  
  3.     android:layout_width="150dp"  
  4.     android:layout_height="150dp"  
  5.     app:cardCornerRadius="8dp"  
  6.     app:cardElevation="10dp">  
  7.   
  8.     <ImageView  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:src="@drawable/pic2"/>  
  12.   
  13. </android.support.v7.widget.CardView>  
  14.   
  15. <Space  
  16.     android:layout_width="match_parent"  
  17.     android:layout_height="20dp"/>  
  18.   
  19. <android.support.v7.widget.CardView  
  20.     android:layout_width="150dp"  
  21.     android:layout_height="150dp"  
  22.     app:cardCornerRadius="8dp"  
  23.     app:cardElevation="10dp">  
  24.   
  25.     <TextView  
  26.         android:layout_width="wrap_content"  
  27.         android:layout_height="150dp"  
  28.         android:background="#5f00"  
  29.         android:singleLine="false"  
  30.         android:text="Whoever says it You're the only one."  
  31.         />  
  32.   
  33. </android.support.v7.widget.CardView>  
tips:

1. Space: Space control

2. App: card Corner Radius=": Set the radius of the card roundness

3. App: cardElevation=": Value of Z-axis

Effect ~

Upper pit:

1. Shadow effect 4.4 is stronger than 5.1 for the same cardElevation value.

In 2 and 5.1, the characters are close to rounded corners and not beautiful.

Solution:

1. After setting up CardElevation in the lower version, CardView will automatically leave space for shadow display, while Lollipop will need to manually set Margin margin to reserve space. Here we define two layouts (of course, you can also write two dimen.xml).

Low version settings

  1. android:layout_margin="0dp"  
High version settings (generally the same size as CardElevation shadows)

  1. android:layout_margin="16dp"    
2. For the problem of text close to rounded corners, we need to set the padding Content attribute to be compatible. Here we compare the effects of setting android:padding and android: content Padding. The difference is obvious, so no explanation is given.


From the above, we can see that we need to set content Padding, but here we should pay attention to the fact that more than 5.0 will automatically clip pictures. It is beautiful that we do not need to set content Padding.


Next, set the ripple effect for CardView

  1. android:clickable="true"  
  2. android:foreground="?attr/selectableItemBackground"  
Of course, you can also use ripple to define water ripple. The above code is only valid after 5.0, but it was not valid before. Don't forget to set clickable here.

Finally, set the animation for CardView (just click here to make its shadow bigger)

  1. android:stateListAnimator="@drawable/state_animator"  
state_animator.xml:
  1. <selector  
  2.     xmlns:android="http://schemas.android.com/apk/res/android">  
  3.         <item  
  4.             android:state_pressed="true">  
  5.                 <objectAnimator  
  6.                     android:duration="@android:integer/config_shortAnimTime"  
  7.                     android:propertyName="translationZ"  
  8.                     android:valueTo="15dp"  
  9.                     android:valueType="floatType"  
  10.                 ></objectAnimator>  
  11.         </item>  
  12.         <item>  
  13.                 <objectAnimator  
  14.                     android:duration="@android:integer/config_shortAnimTime"  
  15.                     android:propertyName="translationZ"  
  16.                     android:valueTo="0dp"  
  17.                     android:valueType="floatType"></objectAnimator>  
  18.         </item>  
  19. </selector>  

Effect ~


Attach Setting Basic Properties

app:cardBackgroundColor This is setting the background color.
App: card Corner Radius This is to set the rounded corner size.
App: card Elevation, which is the shadow of setting the z axis.
App: cardMax Elevation This is the maximum height of the z axis.
App: cardUse CompatPadding or not
App: card Prevent Corner Overlap whether to use Prevent Corner Overlap
app:contentPadding Sets the content padding
App: content Padding Left sets the left padding of content
app:contentPaddingTop Sets Upper padding of Content
App: ContentPadding Right Sets the Right padding of Content
app:contentPaddingBottom sets the bottom padding of the content


Part 2, CardView Source Code Analysis

  1. public class CardView extends FrameLayout {  

CardView inherits FrameLayout and has the characteristics of FrameLayout hierarchy.

  1. private static final CardViewImpl IMPL;  
  2.   
  3. static {  
  4.     if (Build.VERSION.SDK_INT >= 21) {  
  5.         IMPL = new CardViewApi21();  
  6.     } else if (Build.VERSION.SDK_INT >= 17) {  
  7.         IMPL = new CardViewJellybeanMr1();  
  8.     } else {  
  9.         IMPL = new CardViewGingerbread();  
  10.     }  
  11.     IMPL.initStatic();  
  12. }  

Once initialized, versions are judged to define different implementation classes

  1. final Rect mContentPadding = new Rect();  
  2.   
  3. final Rect mShadowBounds = new Rect();  
Looking at the corresponding fields, we can see that one is to set the content margin, and the other is to set the size of the shadow.

And then into the construction method

  1. private void initialize(Context context, AttributeSet attrs, int defStyleAttr) {  
  2.     TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CardView, defStyleAttr,  
  3.             R.style.CardView);  
  4.     ColorStateList backgroundColor;  
  5.     if (a.hasValue(R.styleable.CardView_cardBackgroundColor)) {  
  6.         backgroundColor = a.getColorStateList(R.styleable.CardView_cardBackgroundColor);  
  7.     } else {  
  8.         // There isn't one set, so we'll compute one based on the theme  
  9.         final TypedArray aa = getContext().obtainStyledAttributes(COLOR_BACKGROUND_ATTR);  
  10.         final int themeColorBackground = aa.getColor(00);  
  11.         aa.recycle();  
Here it determines if you have cardBackgroundColor set, and if not, gets the android.R.attr.colorBackground attribute from the topic.

View the onMesure method

  1. @Override  
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  3.     if (!(IMPL instanceof CardViewApi21)) {  
  4.         final int widthMode = MeasureSpec.getMode(widthMeasureSpec);  
  5.         switch (widthMode) {  
  6.             case MeasureSpec.EXACTLY:  
  7.             case MeasureSpec.AT_MOST:  
  8.                 final int minWidth = (int) Math.ceil(IMPL.getMinWidth(mCardViewDelegate));  
  9.                 widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minWidth,  
  10.                         MeasureSpec.getSize(widthMeasureSpec)), widthMode);  
  11.                 break;  
  12.         }  
  13.   
  14.         final int heightMode = MeasureSpec.getMode(heightMeasureSpec);  
  15.         switch (heightMode) {  
  16.             case MeasureSpec.EXACTLY:  
  17.             case MeasureSpec.AT_MOST:  
  18.                 final int minHeight = (int) Math.ceil(IMPL.getMinHeight(mCardViewDelegate));  
  19.                 heightMeasureSpec = MeasureSpec.makeMeasureSpec(Math.max(minHeight,  
  20.                         MeasureSpec.getSize(heightMeasureSpec)), heightMode);  
  21.                 break;  
  22.         }  
  23.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  24.     } else {  
  25.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  26.     }  
  27. }  
It is judged here that if the API is greater than 5.0, it will not be processed, and if it is less than, it will reserve a shadow space.

Here we enter the CardView Api21 class

  1. class CardViewApi21 implements CardViewImpl {  
  2.   
  3.     @Override  
  4.     public void initialize(CardViewDelegate cardView, Context context,  
  5.                 ColorStateList backgroundColor, float radius, float elevation, float maxElevation) {  
  6.         final RoundRectDrawable background = new RoundRectDrawable(backgroundColor, radius);//Fillet rectangle  
  7.         cardView.setCardBackground(background);//Setting rounded corners for CardView  
  8.   
  9.         View view = cardView.getCardView();//Get the CardView control  
  10.         view.setClipToOutline(true);//Tailoring  
  11.         view.setElevation(elevation);//Set Shadow Size  
  12.         setMaxElevation(cardView, maxElevation);//Set the maximum shadow size  
  13.     }  
The purpose of this class is to highlight the unique features of API version 21. Since setting shadows is bound to set padding, and then look at it.
  1. @Override  
  2. public void updatePadding(CardViewDelegate cardView) {  
  3.     if (!cardView.getUseCompatPadding()) {  
  4.         cardView.setShadowPadding(0000);  
  5.         return;  
  6.     }  
  7.     float elevation = getMaxElevation(cardView);  
  8.     final float radius = getRadius(cardView);  
  9.     int hPadding = (int) Math.ceil(RoundRectDrawableWithShadow  
  10.             .calculateHorizontalPadding(elevation, radius, cardView.getPreventCornerOverlap()));  
  11.     int vPadding = (int) Math.ceil(RoundRectDrawableWithShadow  
  12.             .calculateVerticalPadding(elevation, radius, cardView.getPreventCornerOverlap()));  
  13.     cardView.setShadowPadding(hPadding, vPadding, hPadding, vPadding);  
  14. }  
Calculate the corresponding padding value and send it back to CardView Delegate object CardView, but CardViewDelegate is an interface. By looking at CardView, CardViewDelegate is the inner class of CardView.
  1. private final CardViewDelegate mCardViewDelegate = new CardViewDelegate() {  
  2.      private Drawable mCardBackground;  
Enter the setShadowPadding method
  1. public void setShadowPadding(int left, int top, int right, int bottom) {  
  2.     mShadowBounds.set(left, top, right, bottom);  
  3.     CardView.super.setPadding(left + mContentPadding.left, top + mContentPadding.top,  
  4.             right + mContentPadding.right, bottom + mContentPadding.bottom);  
  5. }  
There are margins for mShadowBounds and for parent View, so if you set content Padding, the margins will show the background of CardView.

So why is padding ineffective?

  1. @Override  
  2. public void setPadding(int left, int top, int right, int bottom) {  
  3.     // NO OP  
  4. }  
  5.   
  6. public void setPaddingRelative(int start, int top, int end, int bottom) {  
  7.     // NO OP  
  8. }  
You can see that CardView's setPadding doesn't do anything so it doesn't show. So far, the analysis has been completed.

Posted by sogno on Sat, 13 Apr 2019 09:36:34 -0700