Android CardView全解析(一)
**前言:前面寫了一部落格 Android訂單流程view(超簡單!)
其中用到了(CardView),之前也用過,很爽!!所以對於CardView其實很早就想去研究一下它了,於是就有了這篇部落格了,寫這篇部落格的目的呢,主要是屬性一下google的工程師們是怎麼封裝一個控制元件的,整個過程下來,還是學習到了挺多的知識的,於是打算把我學到的一些東西分享出來,算是當作學習筆記了。**
那麼CardView是幹什麼的呢?
想必有些小夥伴對於它也並不陌生。CardView是v7包中的元件(ViewGroup),主要用來設定佈局的邊框為圓角、z軸的偏移量(這個是5.0以後才有的概念,也就是陰影的效果)。看一下它的效果:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height ="match_parent"
tools:context="com.yasin.round.MainActivity"
android:orientation="vertical"
>
<com.yasin.round.card.RoundView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:backgroundColor="#ff00"
app:conerRadius="10dp"
app:shadowSize="10dp"
app:shadowStartColor="#33000000"
app:shadowEndColor="#22000000"
>
<RelativeLayout
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="200dp"
>
<TextView
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RoundView"
/>
</RelativeLayout>
</com.yasin.round.card.RoundView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp"
app:cardBackgroundColor="#ff00"
app:cardElevation="10dp"
app:cardCornerRadius="10dp"
>
<TextView
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="v7(CardView)"
/>
</android.support.v7.widget.CardView>
</LinearLayout>
上面的RoundView是我們照著CardView實現出來的。
執行效果:
當然,系統的CardView是沒辦法修改陰影的顏色的,我們實現的RoundView對它進行了一些小小的修改,使其能支援顏色修改,當然,我也就實現了大體的部分,本篇roundview只是做演示的demo,小夥伴可不要直接拖到專案中用啊~~
廢話不多說,我們要開擼了~~~小夥伴跟緊啦!
首先明確下我們的目標:
1、可以實現圓角功能
2、可以設定陰影(z軸的偏移)
看起來很簡單,但是我們的程式碼還是比較多的,不過沒關係,我們重點看一下大神們到底是咋寫程式碼的。
首先我們定義一下我們需要用到的屬性:
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="RoundView">
<!--背景顏色-->
<attr name="backgroundColor" format="color"></attr>
<!--圓角的半徑-->
<attr name="conerRadius" format="dimension"></attr>
<!--z軸的偏移量,(陰影的大小)-->
<attr name="shadowSize" format="dimension"></attr>
<!--陰影的起始顏色-->
<attr name="shadowStartColor" format="color"></attr>
<!--陰影的結束顏色-->
<attr name="shadowEndColor" format="color"></attr>
</declare-styleable>
</resources>
定義好了我們的自定義屬性後,我們接下來建立一個view叫RoundView去繼承FrameLayout,然後初始化我們的控制元件獲取我們的自定義屬性:
public class RoundView extends FrameLayout {
/**
* 陰影的起始顏色
*/
private int mShadowStartColor;
/**
* 陰影的結束顏色
*/
private int mShadowEndColor;
/**
* 圓角半徑
*/
private float mRadius;
/**
* 陰影的起始顏色
*/
private float mElevation;
/**
* 控制元件背景顏色
*/
private ColorStateList mBackgroundColor;
public RoundView(Context context) {
super(context);
initialize(context, null, 0);
}
public RoundView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs, 0);
}
public RoundView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs, defStyleAttr);
}
private void initialize(Context context, AttributeSet attrs, int defStyleAttr) {
}
}
然後去獲取我們的自定義屬性:
private void initialize(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundView, defStyleAttr, 0);
//判斷是否有背景顏色
if (a.hasValue(R.styleable.RoundView_backgroundColor)) {
mBackgroundColor = a.getColorStateList(R.styleable.RoundView_backgroundColor);
} else {
//獲取系統的背景顏色
TypedArray aa = context.obtainStyledAttributes(new int[]{android.R.attr.colorBackground});
//獲取系統的背景顏色
int themeBackgroundColor = aa.getColor(0, 0);
//獲取背景顏色的hvs值(h色彩、s飽和度、v顏色值)
float[] hsv = new float[3];
Color.colorToHSV(themeBackgroundColor, hsv);
//當飽和度>0.5的時候,我們獲取自定義的cardview_dark_background顏色
if (hsv[2] > 0.5) {
mBackgroundColor = ColorStateList.valueOf(getResources().getColor(R.color.cardview_dark_background));
} else {
mBackgroundColor = ColorStateList.valueOf(getResources().getColor(R.color.cardview_dark_background));
}
aa.recycle();
}
mRadius = a.getDimensionPixelSize(R.styleable.RoundView_conerRadius, 0);
mElevation = a.getDimensionPixelSize(R.styleable.RoundView_shadowSize, 0);
mShadowStartColor = a.getColor(R.styleable.RoundView_shadowStartColor, getResources().getColor(R.color.cardview_shadow_start_color));
mShadowEndColor = a.getColor(R.styleable.RoundView_shadowEndColor, getResources().getColor(R.color.cardview_shadow_end_color));
a.recycle();
}
好啦!寫到這裡我們才完成了最基本的部分,接下來我們需要考慮(相容android不同版本、提高效能、程式碼上的高內聚低耦合)等一系列因素了。
考慮到不同的android版本實現圓角、陰影的方式不同的因素,我們應該把具體實現跟我們的roundview分離開來(整個下來有點mvp模式的感覺,你也可以理解為mvp模式吧)。
我們程式碼中分為三個部分:
- 建立IRoundView來處理roundview的業務邏輯
- RoundRectDrawableWithShadow(drawable)實現具體的功能
- IRoundViewDelegate(相當於roundview的代表)負責IRoundView跟drawable之間的通訊。
drawable實現好相關功能->給IRoundView->IRoundView通過IRoundViewDelegate代表類修改roundview的相關屬性。
好啦!!知道了大體的框架後,我們就開動了。
首先建立一個IRoundView類:
IRoundView
package com.yasin.round.card;
/**
* Created by leo on 17/3/31.
*/
public interface IRoundView {
/**
* 初始化view
*/
void initialize(IRoundViewDelegate roundView);
/**
* 設定圓角半徑
*/
void setRadius(IRoundViewDelegate cardView, float radius);
float getRadius(IRoundViewDelegate cardView);
/**
* 設定z軸的偏移量
*/
void setElevation(IRoundViewDelegate cardView, float elevation);
float getElevation(IRoundViewDelegate cardView);
/**
*圓角功能具體實現方法
*/
void initStatic();
}
二、具體的drawable背景實現類RoundRectDrawableWithShadow
public class RoundRectDrawableWithShadow extends Drawable {
}
三、view的代表類IRoundViewDelegate:
package com.yasin.round.card;
import android.content.res.ColorStateList;
import android.graphics.drawable.Drawable;
import android.view.View;
/**
* Created by leo on 17/3/31.
*/
public interface IRoundViewDelegate {
void setCardBackground(Drawable drawable);
Drawable getCardBackground();
View getCardView();
int getShadowStartColor();
int getShadowEndColor();
ColorStateList getBackgroundColor();
float getRadius();
float getElevation();
}
定義好了各個模組程式碼後,我們首先需要在我們的roundview中做處理了,我們在roundview中需要建立我們的代表類IRoundViewDelegate:
/**
* Created by leo on 17/3/31.
*/
public class RoundView extends FrameLayout {
.....
private IRoundViewDelegate mRoundViewDelegate = new IRoundViewDelegate() {
private Drawable bgDrawable;
@Override
public void setCardBackground(Drawable drawable) {
this.bgDrawable = drawable;
setBackgroundDrawable(drawable);
}
@Override
public Drawable getCardBackground() {
return bgDrawable;
}
@Override
public View getCardView() {
return RoundView.this;
}
@Override
public int getShadowStartColor() {
return RoundView.this.getShadowStartColor();
}
@Override
public int getShadowEndColor() {
return RoundView.this.getShadowEndColor();
}
@Override
public ColorStateList getBackgroundColor() {
return RoundView.this.getBackgroundColor();
}
@Override
public float getRadius() {
return RoundView.this.getRadius();
}
@Override
public float getElevation() {
return RoundView.this.getElevation();
}
};
}
然後我們需要把我們的mRoundViewDelegate物件在roundview初始化的時候傳遞給IRoundView(我們的邏輯處理類):
於是我們的roundivew全部程式碼就是:
package com.yasin.round.card;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import com.yasin.round.R;
/**
* Created by leo on 17/3/31.
*/
public class RoundView extends FrameLayout {
/**
* 陰影的起始顏色
*/
private int mShadowStartColor;
/**
* 陰影的結束顏色
*/
private int mShadowEndColor;
/**
* 圓角半徑
*/
private float mRadius;
/**
* 陰影的起始顏色
*/
private float mElevation;
/**
* 控制元件背景顏色
*/
private ColorStateList mBackgroundColor;
private static IRoundView roundViewImp;
static {
if (Build.VERSION.SDK_INT >= 17) {
roundViewImp = new RoundViewJellyBeanMr();
} else {
roundViewImp = new RoundViewLowImp();
}
roundViewImp.initStatic();
}
public RoundView(Context context) {
super(context);
initialize(context, null, 0);
}
public RoundView(Context context, AttributeSet attrs) {
super(context, attrs);
initialize(context, attrs, 0);
}
public RoundView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initialize(context, attrs, defStyleAttr);
}
private void initialize(Context context, AttributeSet attrs, int defStyleAttr) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.RoundView, defStyleAttr, 0);
//判斷是否有背景顏色
if (a.hasValue(R.styleable.RoundView_backgroundColor)) {
mBackgroundColor = a.getColorStateList(R.styleable.RoundView_backgroundColor);
} else {
//獲取系統的背景顏色
TypedArray aa = context.obtainStyledAttributes(new int[]{android.R.attr.colorBackground});
//獲取系統的背景顏色
int themeBackgroundColor = aa.getColor(0, 0);
//獲取背景顏色的hvs值(h色彩、s飽和度、v顏色值)
float[] hsv = new float[3];
Color.colorToHSV(themeBackgroundColor, hsv);
//當飽和度>0.5的時候,我們獲取自定義的cardview_dark_background顏色
if (hsv[2] > 0.5) {
mBackgroundColor = ColorStateList.valueOf(getResources().getColor(R.color.cardview_dark_background));
} else {
mBackgroundColor = ColorStateList.valueOf(getResources().getColor(R.color.cardview_dark_background));
}
aa.recycle();
}
mRadius = a.getDimensionPixelSize(R.styleable.RoundView_conerRadius, 0);
mElevation = a.getDimensionPixelSize(R.styleable.RoundView_shadowSize, 0);
mShadowStartColor = a.getColor(R.styleable.RoundView_shadowStartColor, getResources().getColor(R.color.cardview_shadow_start_color));
mShadowEndColor = a.getColor(R.styleable.RoundView_shadowEndColor, getResources().getColor(R.color.cardview_shadow_end_color));
a.recycle();
roundViewImp.initialize(mRoundViewDelegate);
}
public int getShadowStartColor() {
return mShadowStartColor;
}
public void setShadowStartColor(int shadowStartColor) {
this.mShadowStartColor = shadowStartColor;
}
public int getShadowEndColor() {
return mShadowEndColor;
}
public void setShadowEndColor(int shadowEndColor) {
this.mShadowEndColor = shadowEndColor;
}
public float getRadius() {
return mRadius;
}
public void setRadius(float mRadius) {
this.mRadius = mRadius;
}
public float getElevation() {
return mElevation;
}
public void setElevation(float mElevation) {
this.mElevation = mElevation;
}
public ColorStateList getBackgroundColor() {
return mBackgroundColor;
}
public void setBackgroundColor(ColorStateList backgroundColor) {
this.mBackgroundColor = backgroundColor;
}
private IRoundViewDelegate mRoundViewDelegate = new IRoundViewDelegate() {
private Drawable bgDrawable;
@Override
public void setCardBackground(Drawable drawable) {
this.bgDrawable = drawable;
setBackgroundDrawable(drawable);
}
@Override
public Drawable getCardBackground() {
return bgDrawable;
}
@Override
public View getCardView() {
return RoundView.this;
}
@Override
public int getShadowStartColor() {
return RoundView.this.getShadowStartColor();
}
@Override
public int getShadowEndColor() {
return RoundView.this.getShadowEndColor();
}
@Override
public ColorStateList getBackgroundColor() {
return RoundView.this.getBackgroundColor();
}
@Override
public float getRadius() {
return RoundView.this.getRadius();
}
@Override
public float getElevation() {
return RoundView.this.getElevation();
}
};
}
其中我們看到這麼一段程式碼:
static {
if (Build.VERSION.SDK_INT >= 17) {
roundViewImp = new RoundViewJellyBeanMr();
} else {
roundViewImp = new RoundViewLowImp();
}
roundViewImp.initStatic();
}
RoundViewJellyBeanMr跟RoundViewLowImp類就是具體的業務邏輯類了,
為什麼要做這個判斷呢?
因為我們知道我們畫圓角的時候一般都是呼叫:
canvas.drawRoundRect(bounds,cornerRadius,cornerRadius,paint);
但是drawRoundRect方法在api11-16的時候繪製效率很低,所以考慮到效能我們分為兩個類。
public class RoundViewLowImp implements IRoundView {
...
}
高版本中無非就是需要實現的圓角方式不同罷了,所以我們只需要繼承低版本RoundViewLowImp然後去實現圓角方法initStatic就可以了:
package com.yasin.round.card;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
/**
* Created by leo on 17/4/1.
*/
public class RoundViewJellyBeanMr extends RoundViewLowImp {
@Override
public void initStatic() {
}
**我們先看RoundViewLowImp中咋實現。
我們需要把我們從view中獲取到的屬性傳遞給我們的具體實現類RoundRectDrawableWithShadow中:**
package com.yasin.round.card;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
/**
* Created by leo on 17/4/1.
*/
public class RoundViewLowImp implements IRoundView {
@Override
public void initialize(IRoundViewDelegate roundView) {
RoundRectDrawableWithShadow backgroundDrawable = createDrawable(roundView);
roundView.setCardBackground(backgroundDrawable);
}
private RoundRectDrawableWithShadow createDrawable(IRoundViewDelegate roundView) {
return new RoundRectDrawableWithShadow(
roundView.getBackgroundColor(),
roundView.getShadowStartColor(),
roundView.getShadowEndColor(),
roundView.getElevation(),
roundView.getRadius());
}
@Override
public void initStatic() {
RoundRectDrawableWithShadow.mRoundRectHelper = new RoundRectDrawableWithShadow.RoundRectHelper() {
@Override
public void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, Paint paint) {
}
};
}
@Override
public void setRadius(IRoundViewDelegate cardView, float radius) {
}
@Override
public float getRadius(IRoundViewDelegate cardView) {
return 0;
}
@Override
public void setElevation(IRoundViewDelegate cardView, float elevation) {
}
@Override
public float getElevation(IRoundViewDelegate cardView) {
return 0;
}
private RoundRectDrawableWithShadow getShadowBackground(IRoundViewDelegate cardView) {
return (RoundRectDrawableWithShadow) cardView.getCardBackground();
}
}
有了我們的介面後,程式碼很容易看懂跟實現的對吧!
好啦!做了那麼多前置工作,終於是要到我們的具體實現類中了,我們這裡的實現類為RoundRectDrawableWithShadow(drawable):
package com.yasin.round.card;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
/**
* Created by leo on 17/4/1.
*/
public class RoundRectDrawableWithShadow extends Drawable {
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public void draw(Canvas canvas) {
}
}
我們去繼承一個drawable類,然後需要重寫這麼幾個方法,其它幾個看不懂也沒關係,我們重點要看並且要實現的就是draw方法:
說了那麼多東西了,我們都還沒有測試的,我們來測試一下我們的程式碼(我們在左上角畫一個弧度):
首先引入roundview:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.yasin.round.MainActivity"
android:orientation="vertical"
>
<com.yasin.round.card.RoundView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:backgroundColor="#ff00"
app:conerRadius="10dp"
app:shadowSize="10dp"
app:shadowStartColor="#33000000"
app:shadowEndColor="#22000000"
>
<RelativeLayout
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="200dp"
>
<TextView
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="RoundView"
/>
</RelativeLayout>
</com.yasin.round.card.RoundView>
<android.support.v7.widget.CardView
android:layout_width="match_parent"
android:layout_height="200dp"
android:layout_margin="10dp"
app:cardBackgroundColor="#ff00"
app:cardElevation="10dp"
app:cardCornerRadius="10dp"
>
<TextView
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="v7(CardView)"
/>
</android.support.v7.widget.CardView>
</LinearLayout>
然後修改RoundRectDrawableWithShadow中程式碼,畫一個弧度:
package com.yasin.round.card;
import android.content.res.ColorStateList;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.Drawable;
/**
* Created by leo on 17/4/1.
*/
public class RoundRectDrawableWithShadow extends Drawable {
private static final float SHADOW_MULTIPLIER = 1.5f;
public static RoundRectHelper mRoundRectHelper;
private ColorStateList mBgColor;
private int mShadowStartColor;
private int mShadowEndColor;
private float mCornerRadius;
private float mShadowSize;
private boolean mDirty = true;
private RectF mCardBounds = new RectF();
private Paint mPaint;
private Paint mCornerShadowPaint;
private Paint mEdgeShadowPaint;
private Path mCornerShadowPath;
public RoundRectDrawableWithShadow(ColorStateList bgColor, int shadowStartColor, int shadowEndColor, float shadowSize, float radius) {
this.mShadowStartColor = shadowStartColor;
this.mShadowEndColor = shadowEndColor;
this.mShadowSize = shadowSize;
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
setBackground(bgColor);
mCornerShadowPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
mCornerShadowPaint.setStyle(Paint.Style.FILL);
this.mCornerRadius = (int) (radius + .5f);
mCardBounds = new RectF();
mEdgeShadowPaint = new Paint(mCornerShadowPaint);
mEdgeShadowPaint.setAntiAlias(false);
}
private void setBackground(ColorStateList color) {
mBgColor = (color == null) ? ColorStateList.valueOf(Color.TRANSPARENT) : color;
mPaint.setColor(mBgColor.getColorForState(getState(), mBgColor.getDefaultColor()));
}
@Override
public void draw(Canvas canvas) {
if (mDirty) {
buildComponents(getBounds());
mDirty = false;
}
canvas.translate(0, mShadowSize / 2);
drawShadow(canvas);
canvas.translate(0, -mShadowSize / 2);
}
private void drawShadow(Canvas canvas) {
final float edgeShadowTop = -mCornerRadius - mShadowSize;
final float inset = mCornerRadius+ mShadowSize / 2;
final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
// LT
int saved = canvas.save();
canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
}
private void buildComponents(Rect bounds) {
final float verticalOffset = mShadowSize * SHADOW_MULTIPLIER;
mCardBounds.set(bounds.left + mShadowSize, bounds.top + verticalOffset,
bounds.right - mShadowSize, bounds.bottom - verticalOffset);
buildShadowCorners();
}
private void buildShadowCorners() {
RectF innerBounds = new RectF(-mCornerRadius, -mCornerRadius, mCornerRadius, mCornerRadius);
RectF outerBounds = new RectF(innerBounds);
outerBounds.inset(-mShadowSize, -mShadowSize);
if (mCornerShadowPath == null) {
mCornerShadowPath = new Path();
} else {
mCornerShadowPath.reset();
}
mCornerShadowPath.setFillType(Path.FillType.EVEN_ODD);
mCornerShadowPath.moveTo(-mCornerRadius, 0);
mCornerShadowPath.rLineTo(-mShadowSize, 0);
// outer arc
mCornerShadowPath.arcTo(outerBounds, 180f, 90f, false);
// inner arc
mCornerShadowPath.arcTo(innerBounds, 270f, -90f, false);
mCornerShadowPath.close();
float startRatio = mCornerRadius / (mCornerRadius + mShadowSize);
mCornerShadowPaint.setShader(new RadialGradient(0, 0, mCornerRadius + mShadowSize,
new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
new float[]{0f, startRatio, 1f}
, Shader.TileMode.CLAMP));
// we offset the content shadowSize/2 pixels up to make it more realistic.
// this is why edge shadow shader has some extra space
// When drawing bottom edge shadow, we use that extra space.
mEdgeShadowPaint.setShader(new LinearGradient(0, -mCornerRadius + mShadowSize, 0,
-mCornerRadius - mShadowSize,
new int[]{mShadowStartColor, mShadowStartColor, mShadowEndColor},
new float[]{0f, .5f, 1f}, Shader.TileMode.CLAMP));
mEdgeShadowPaint.setAntiAlias(false);
}
@Override
public void setAlpha(int alpha) {
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
static interface RoundRectHelper {
void drawRoundRect(Canvas canvas, RectF bounds, float cornerRadius, Paint paint);
}
}
看別管我是咋實現的哈,先看看效果:
可以看到,我們的roundview左上角有了一個圓角弧度,然後我們再加加程式碼:
private void drawShadow(Canvas canvas) {
final float edgeShadowTop = -mCornerRadius - mShadowSize;
final float inset = mCornerRadius+ mShadowSize / 2;
final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
// LT
int saved = canvas.save();
canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawHorizontalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.width() - 2 * inset, -mCornerRadius,
mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
}
然後寫完各個方向的draw方法:
private void drawShadow(Canvas canvas) {
final float edgeShadowTop = -mCornerRadius - mShadowSize;
final float inset = mCornerRadius+ mShadowSize / 2;
final boolean drawHorizontalEdges = mCardBounds.width() - 2 * inset > 0;
final boolean drawVerticalEdges = mCardBounds.height() - 2 * inset > 0;
// LT
int saved = canvas.save();
canvas.translate(mCardBounds.left + inset, mCardBounds.top + inset);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawHorizontalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.width() - 2 * inset, -mCornerRadius,
mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
// RB
saved = canvas.save();
canvas.translate(mCardBounds.right - inset, mCardBounds.bottom - inset);
canvas.rotate(180f);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawHorizontalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.width() - 2 * inset, -mCornerRadius ,
mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
// LB
saved = canvas.save();
canvas.translate(mCardBounds.left + inset, mCardBounds.bottom - inset);
canvas.rotate(270f);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawVerticalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
// RT
saved = canvas.save();
canvas.translate(mCardBounds.right - inset, mCardBounds.top + inset);
canvas.rotate(90f);
canvas.drawPath(mCornerShadowPath, mCornerShadowPaint);
if (drawVerticalEdges) {
canvas.drawRect(0, edgeShadowTop,
mCardBounds.height() - 2 * inset, -mCornerRadius, mEdgeShadowPaint);
}
canvas.restoreToCount(saved);
}
執行程式碼:
相關推薦
Android CardView全解析(一)
**前言:前面寫了一部落格 Android訂單流程view(超簡單!) 其中用到了(CardView),之前也用過,很爽!!所以對於CardView其實很早就想去研究一下它了,於是就有了這篇部落格了,寫這篇部落格的目的呢,主要是屬性一下google的工程師們是
一篇就夠了系列之Android Manifest全解析
前言: 前面幾篇介紹了android四大元件的知識,可以發現,四大元件都必須在一個叫AndroidManifest.xml檔案中進行註冊,那麼該檔案的作用是什麼呢?你們的內容各有什麼意義呢?帶著這些疑問,來開始下面內容的學習。官方文件,很詳細 作用:
前端入門之(vue-router全解析一)
前言: 一直想著啥時候能把vue全家桶的東西原始碼全部擼一遍,可惜無奈能力有限啊(哈哈,最近一直在補js基礎),看vuex程式碼的時候覺得也還好,基本上是邊學邊解析原始碼的方式把vuex擼了一遍(畢竟整個vuex加起來也沒多少程式碼),可當碰到vue-router的時候,首先就被它那幾千
Android DataBinding全解析,你該用這個框架了
前言: Data binding 在2015年7月釋出的Android Studio v1.3.0 版本上引入,在2016年4月Android Studio v2.0.0 上正式支援。目前為止,Data Binding 已經支援雙向綁定了。 Databind
Android TagFlowLayout完全解析 一款針對Tag的佈局(針對多個條目的單選操作)
目錄(?)[+] 一、概述 本文之前,先提一下關於上篇博文的100多萬訪問量請無視,博文被刷,我也很鬱悶,本來想把那個文章放到草稿箱,結果放不進去,還把日期弄更新了,實屬無奈。 因為本身FlowLayout本身的預期是提供一種新的佈局的方式,但是呢,在實際的開發中
Android Volley完全解析(一),初識Volley的基本用法
1. Volley簡介我們平時在開發Android應用的時候不可避免地都需要用到網路技術,而多數情況下應用程式都會使用HTTP協議來發送和接收網路資料。Android系統中主要提供了兩種方式來進行HTTP通訊,HttpURLConnection和HttpClient,幾乎在任
Android Binder 全解析(1) -- 概述
摘要 如果各位玩過《爐石傳說》,那麼可能對法師的職業卡「不穩定的傳送門」很有印象,特別是沒有歐洲玩家,經常能夠拿到其他職業的強力單卡。Android 也提供了傳送門,讓我們可以像使用本地方法一樣,呼叫其他程序的方法,他有一個響亮的名字,Binder! Binder 在 Android 是如此的重要,承當
Android Init流程解析一
*************************************************************************** ******************************************************
zencart全解析(一)
Index.php 1. Load application_top.php - see {@tutorial initsystem} 2. Set main language directory based on $_SESSION['language'] 3. Load
Android圖片載入框架最全解析(一),Glide的基本用法
現在Android上的圖片載入框架非常成熟,從最早的老牌圖片載入框架UniversalImageLoader,到後來Google推出的Volley,再到後來的新興軍Glide和Picasso,當然還有Facebook的Fresco。每一個都非常穩定,功能也都十分強大。但是它們
Android事件匯流排(一)EventBus3.0用法全解析
前言 EventBus是一款針對Android優化的釋出/訂閱事件匯流排。簡化了應用程式內各元件間、元件與後臺執行緒間的通訊。優點是開銷小,程式碼更優雅,以及將傳送者和接收者解耦。如果Activity和Activity進行互動還好說,如果Fragmen
Android圖片載入框架最全解析(一)Glide的基本用法
現在Android上的圖片載入框架非常成熟,從最早的老牌圖片載入框架UniversalImageLoader,到後來Google推出的Volley,再到後來的新興軍Glide和Picasso,當然還有Facebook的Fresco。每一個都非常穩定,功能也都
(郭霖)Android圖片載入框架最全解析(一),Glide的基本用法
本文同步發表於我的微信公眾號,掃一掃文章底部的二維碼或在微信搜尋 郭霖 即可關注,每天都有文章更新。 現在Android上的圖片載入框架非常成熟,從最早的老牌圖片載入框架UniversalImageLoader,到後來Google推出的Volley,再到後來的新興軍Glide和Picas
一篇好文之Android資料庫 SQLite全解析
這篇文章是資料庫系列篇文章的第一篇,主要講Android Sqlite資料庫儲存,後面陸續出GreenDao,LitePal, Realm,wcdb的文章,一如既往,如果遇到任何關於Android中SQLite的問題,都可以直接在我的文章底部留言,或者直接在我
Android異步載入全解析之開篇瞎扯淡
com des turn pro 能夠 eat launch 卡頓 ring Android異步載入概述 Android異步載入在Android中使用的很廣泛,除了是由於避免在主線程中做網絡操作。更是為了避免在顯示時由於時間太長而造成ANR,添加顯示的流暢性,特別是像Li
Android: 在native中訪問assets全解析
lock mp4 cpp sets 這樣的 內容 jniexport opencl href 本文總結在Android Native C++開發中訪問APK中的assets資源的方法 在CMake中添加相關NDK LIB的 依賴 因為我們接下來用到的一些函數實現在NDK庫l
Android框架原始碼解析之(一)Volley
前幾天面試CVTE,HR面掛了。讓內部一個學長幫我查看了一下面試官評價,發現二面面試官的評價如下: 廣度OK,但缺乏深究能力,深度與實踐不足 原始碼:只能說流程,細節程式碼不清楚,retrofit和volley都是。 感覺自己一方面:自己面試技巧有待提高吧(框
Android 建立與解析XML(一)—— 概述
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Fragment全解析系列(一):那些年踩過的坑
本篇主要介紹一些最常見的Fragment的坑以及官方Fragment庫的那些自身的BUG,並給出解決方案;這些BUG在你深度使用時會遇到,比如Fragment巢狀時或者單Activity+多Fragment架構時遇到的坑。 Fragment是可以讓你的app縱享絲滑的設計,如果你的app想在
Android圖片載入框架最全解析(四),玩轉Glide的回撥與監聽(筆記)
參考原文:Android圖片載入框架最全解析(四),玩轉Glide的回撥與監聽 回撥的原始碼實現 的Target物件傳入到GenericRequest當中,而Glide在圖片載入完成之後又會回撥GenericRequest的onResourceReady()方法,onReso