前言
在開發(fā)中幌缝,為了界面美觀霎终,圓角view和陰影效果是開發(fā)中經(jīng)常遇到的UI場景滞磺,比如銀行卡效果,卡片式itemView布局莱褒,Banner圖等击困,開發(fā)中我們通過各種方式實現(xiàn)了這種效果,但是哪種方案最好呢广凸,接下來本文將比較幾種常見的圓角陰影布局實現(xiàn)阅茶,并從內(nèi)存占用角度分析它們的優(yōu)缺點.
解決方案
在Android開發(fā)中,有以下幾種解決方案:
- 早期開發(fā)中谅海,可以使用shape標(biāo)簽為LinearLayout脸哀、RelativeLayout等添加background實現(xiàn)圓角陰影效果;
- 自定義View扭吁,包括開源的RoundAngleFrameLayout以及RCRelativeLayout等解決方案也能為我們實現(xiàn)這種效果企蹭;
- CardView:Android5.0以后引入了cardView來幫助我們實現(xiàn)圓角陰影卡片式的效果,雖然也兼容5.0以下的版本智末,但是5.0以下cardview會有內(nèi)邊距谅摄,需要處理.
存在的問題
以上幾種方案都能實現(xiàn)圓角陰影布局的實現(xiàn),但是如果出現(xiàn)圖片頂邊展示的場景時系馆,并不能能保證圖片也是圓角的送漠,經(jīng)過驗證,得出以下結(jié)論并例舉了幾種實現(xiàn)方式:
- LinearLayout等添加shape無法保證圖片圓角顯示由蘑;
- RoundAngleFrameLayout+shape可以實現(xiàn)闽寡,不需要特殊處理圖片代兵;
- RCRelativeLayout可以實現(xiàn),不需要特殊處理圖片爷狈;
- CardView在5.0以上也可以實現(xiàn)植影,5.0以下需要處理圖片圓角;
- cardView+自定義ImageView涎永,不需要特殊處理圖片.
代碼實現(xiàn)
- cardView :cardCornerRadius控制圓角思币,cardElevation控制陰影大小,值得一提的是羡微,cardview在5.0以下展示效果會有有不同谷饿,需要特殊處理:
//xml
<android.support.v7.widget.CardView
android:id="@+id/cardview"
android:layout_width="320dp"
android:layout_height="140dp"
android:layout_marginTop="20dp"
app:cardCornerRadius="10dp"
app:cardElevation="2dp"
app:cardBackgroundColor="@color/white"
>
//Activity,adapter等
//5.0以下處理cardview內(nèi)邊距妈倔,Glide配合形變transform 處理圖片圓角
if (Build.VERSION.SDK_INT < 21) {
//去除5.0以下cardView內(nèi)間距
cardview.setUseCompatPadding(false);
cardview.setPreventCornerOverlap(false);
//圓角形變博投,除去左上左下兩個角,設(shè)置右上右下兩個角為圓角
CornerTransform transform = new CornerTransform(this, dip2px(10));
transform.setExceptCorner(true, false, true, false);
Glide.with(this)
.load(url3)
.apply(RequestOptions.centerCropTransform())//先centerCrop再設(shè)置圖片圓角盯蝴,否則會覆蓋圓角效果
.apply(RequestOptions.bitmapTransform(transform))
.into(cover);
return;
}
- RCRelativeLayout:round_corner控制圓角大小毅哗,clip_background裁切背景(必須添加才能沒有背景白邊)
<com.transportmm.tsportapp.mvp.ui.dsh.cornerview.RCRelativeLayout
android:id="@+id/rclayout"
android:layout_width="320dp"
android:layout_height="140dp"
android:layout_marginTop="20dp"
app:round_corner="10dp"
app:clip_background="true"
android:background="@color/white">
- CardView+RCImageView:RCImageView round_corner_xxx_xxx 控制圖片四個角的圓角大小
<com.transportmm.tsportapp.mvp.ui.dsh.cornerview.RCImageView
android:id="@+id/coverrci"
android:layout_width="100dp"
app:round_corner_top_right="10dp"
app:round_corner_bottom_right="10dp"
android:layout_height="match_parent"
android:layout_gravity="right"/>
- RoundAngleFrameLayout:radius控制圓角,必須添加shape保證沒有背景白邊
<com.transportmm.tsportapp.mvp.ui.dsh.cornerview.RoundAngleFrameLayout
android:id="@+id/raflayout"
android:layout_width="320dp"
android:layout_height="140dp"
android:layout_marginTop="20dp"
android:background="@drawable/shape_roundcorner"
app:radius="5dp">
內(nèi)存使用分析
通過以上幾種方式實現(xiàn)了圓角陰影圖片頂邊顯示的效果捧挺,但是哪一種更好呢虑绵,或者說哪一種更加適合我們的開發(fā)呢,其實簡單的一些靜態(tài)頁面展示我認(rèn)為這幾種方案都不錯松忍,但是當(dāng)我們在RecycleView中進(jìn)行滑動時蒸殿,控件的性能變得特別重要筷厘,所以使用了RecycleView模擬了他們在開發(fā)中的使用:
在RecycleView頁面鸣峭,我使用了95張圖片,加載10頁酥艳,通過AndroidStudio自帶內(nèi)存檢測工具記錄了他們在頁面加前和加載十次之后的穩(wěn)定內(nèi)存值摊溶,使用的手機(jī)是VIVO X21A;
下面是測試結(jié)果:
解決方案 | 頁面加載前內(nèi)存值 | 10次加載后內(nèi)存值 | 消耗內(nèi)存 |
---|---|---|---|
cardView | 104.37 | 191.66 | 87.29 |
RCRelativeLayout | 105.41 | 214.79 | 109.38 |
CardView+RCImageView | 105.33 | 202.46 | 97.13 |
RoundAngleFrameLayout | 103.85 | 213.43 | 109.58 |
下面是過度繪制情況:
可以看出充石,無論是在過度繪制情況方面還是內(nèi)存使用角度莫换,cardview都是性能最好的,自定義RelativeLayout或者FrameLayout則在性能上較差
使用中的選擇
自定義RelativeLayout或者FrameLayout的方式上面被驗證為性能不夠優(yōu)秀骤铃,那么CardView的兩種實現(xiàn)即5.0以上cardview直接實現(xiàn)和5.0以下cardview+glide特殊處理圖片及cardview+RcImage兩種那種更好呢拉岁,
- cardview 分5.0和5.0以下分版本處理的好處是內(nèi)存使用開銷小,但是需要版本處理;
- cardview+RCImageView的方式無需關(guān)注系統(tǒng)版本惰爬,但是內(nèi)存開銷較大喊暖;
不妨看一下現(xiàn)在的安卓版本分布(2018年10月26號數(shù)據(jù))
可以看出,5.0以下設(shè)備占比僅一成左后撕瞧,所以5.0以下的代碼執(zhí)行占比較低陵叽,綜合來看狞尔,選用cardView+glide5.0以下特殊處理圖片的方式更好一些
源碼分析
自定義cardView 打造一個通用的新聞標(biāo)簽視圖 PictureTextCardView(V1.0)
- 支持圖片頂部,靠左巩掺,靠右頂邊展示
- 提供各個子view的獲取方法及動態(tài)設(shè)置各個子view的填充內(nèi)容偏序;
- 除了cardview本身的屬性以外,xml中額外支持添加以下屬性胖替;
- app:img_width="xxdp"和app:img_height="xxdp"研儒,圖片的寬高;默認(rèn)100dp寬度和150dp高度刊殉;(圖片top時寬度match_parent)
- text_margin 控制文本的外邊距殉摔,默認(rèn)10dp;
- cardCornerRadius控制圓角大小,默認(rèn)5dp记焊;
- img_gravity(left,right,top)控制圖片位置 ,默認(rèn)靠右顯示逸月;
- titleColor 標(biāo)題文字顏色
- contentColor 副標(biāo)題文字顏色
- remarkColor 補(bǔ)充文字顏色
繼承自cardView
public class PictureTextCardView extends CardView implements ICardInterface{
...
...
}
初始化屬性
public PictureTextCardView(Context context) {
this(context, null);
}
public PictureTextCardView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public PictureTextCardView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
if (attrs != null) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PictureTextCardView);
radius = ta.getDimension(R.styleable.PictureTextCardView_cardCornerRadius, dip2px(5));
imgHeight = ta.getDimension(R.styleable.PictureTextCardView_img_height, dip2px(150));
imgWidth = ta.getDimension(R.styleable.PictureTextCardView_img_width, dip2px(100));
textMargin = ta.getDimension(R.styleable.PictureTextCardView_text_marign, dip2px(10));
titleColor = ta.getColor(R.styleable.PictureTextCardView_title_color, Color.parseColor("#000000"));
contentColor = ta.getColor(R.styleable.PictureTextCardView_content_color,Color.parseColor("#666666"));
remarkColor = ta.getColor(R.styleable.PictureTextCardView_remark_color,Color.parseColor("#999999"));
imgGravity = ta.getInt(R.styleable.PictureTextCardView_img_gravity,0);
ta.recycle();
}
init(context);
}
提供各個子view的獲取方法及一些設(shè)置
public interface ICardInterface{
/* 獲得控件 */
TextView getTitle();
TextView getContents();
TextView getRemark();
ImageView getImageView();
/* 可見/不可見狀態(tài) */
void setTitleVisible(boolean visible);
void setContentsVisible(boolean visible);
void setRemarkVisible(boolean visible);
void setImageVisible(boolean visible);
/* 設(shè)置控件填充內(nèi)容 */
void setImageView(Context context,String str);
void setTitleText(String str);
void setContentsText(String str);
void setRemarkText(String str);
void setTitle(String title,int textSize,int color);
void setContent(String content,int textSize,int color);
void setRemark(String remark,int textSize,int color);
}
使用同cardView一樣 建議通過xml控制一些布局屬性
<com.dsh.mydemos.myview.PictureTextCardView
android:id="@+id/ptcardview_left"
android:layout_width="320dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
app:cardCornerRadius="10dp"
app:text_marign="10dp"
app:img_width="100dp"
app:img_height="150dp"
app:img_gravity="left"
app:cardElevation="2dp"
app:cardBackgroundColor="@color/white"
android:layout_marginBottom="10dp"
/>
/>
配合代碼填充布局內(nèi)容(如果xml中設(shè)置了img_gravity屬性,setViewsLayout方法不要執(zhí)行遍膜,防止view重繪)
//圖片居左展示
ptcardviewLeft.setViewsLayout(PictureTextCardView.POSITION_LEFT);
ptcardviewLeft.setTitle("自定義cardview:PictureTextCardView",24,getResources().getColor(R.color.red));
ptcardviewLeft.setContent("圖片靠左展示",20,getResources().getColor(R.color.blue));
ptcardviewLeft.setRemark("2018-11-23",16,getResources().getColor(R.color.gray));
ptcardviewRight.setImageView(this,url4);
內(nèi)存使用分析:
解決方案 | 頁面加載前內(nèi)存值 | 10次加載后內(nèi)存值 | 消耗內(nèi)存 |
---|---|---|---|
cardView | 104.37 | 183.20 | 78.83 |
RCRelativeLayout | 105.41 | 214.79 | 109.38 |
CardView+RCImageView | 105.33 | 202.46 | 97.13 |
RoundAngleFrameLayout | 103.85 | 213.43 | 109.58 |
PictureTextCardView | 104.34 | 191.15 | 86.81 |