Android卡片布局(圓角陰影)的幾種實現(xiàn)及分析

前言

在開發(fā)中幌缝,為了界面美觀霎终,圓角view和陰影效果是開發(fā)中經(jīng)常遇到的UI場景滞磺,比如銀行卡效果,卡片式itemView布局莱褒,Banner圖等击困,開發(fā)中我們通過各種方式實現(xiàn)了這種效果,但是哪種方案最好呢广凸,接下來本文將比較幾種常見的圓角陰影布局實現(xiàn)阅茶,并從內(nèi)存占用角度分析它們的優(yōu)缺點.


卡片式布局.png

解決方案

在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).jpg

代碼實現(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

下面是過度繪制情況:

過度繪制.jpg

可以看出充石,無論是在過度繪制情況方面還是內(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ù))
    版本分布.png

可以看出,5.0以下設(shè)備占比僅一成左后撕瞧,所以5.0以下的代碼執(zhí)行占比較低陵叽,綜合來看狞尔,選用cardView+glide5.0以下特殊處理圖片的方式更好一些

源碼分析

自定義cardView 打造一個通用的新聞標(biāo)簽視圖 PictureTextCardView(V1.0)

自定義cardView
  • 支持圖片頂部,靠左巩掺,靠右頂邊展示
  • 提供各個子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

可以看出碗硬,PictureTextCardView性能介于原生CardView和CardView+RCImageView之間,過度繪制表現(xiàn)方面跟其他自定義view一致.

項目demo地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瓢颅,一起剝皮案震驚了整個濱河市恩尾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌挽懦,老刑警劉巖翰意,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異信柿,居然都是意外死亡冀偶,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門渔嚷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來进鸠,“玉大人,你說我怎么就攤上這事形病】湍辏” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵漠吻,是天一觀的道長量瓜。 經(jīng)常有香客問我,道長途乃,這世上最難降的妖魔是什么绍傲? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮欺劳,結(jié)果婚禮上唧取,老公的妹妹穿的比我還像新娘铅鲤。我一直安慰自己,他們只是感情好枫弟,可當(dāng)我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布邢享。 她就那樣靜靜地躺著,像睡著了一般淡诗。 火紅的嫁衣襯著肌膚如雪骇塘。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天韩容,我揣著相機(jī)與錄音款违,去河邊找鬼。 笑死群凶,一個胖子當(dāng)著我的面吹牛插爹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播请梢,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼赠尾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了毅弧?” 一聲冷哼從身側(cè)響起气嫁,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎够坐,沒想到半個月后寸宵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡元咙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年梯影,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛾坯。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡光酣,死狀恐怖疏遏,靈堂內(nèi)的尸體忽然破棺而出脉课,到底是詐尸還是另有隱情,我是刑警寧澤财异,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布倘零,位于F島的核電站,受9級特大地震影響戳寸,放射性物質(zhì)發(fā)生泄漏呈驶。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一疫鹊、第九天 我趴在偏房一處隱蔽的房頂上張望袖瞻。 院中可真熱鬧司致,春花似錦、人聲如沸聋迎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霉晕。三九已至庭再,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間牺堰,已是汗流浹背拄轻。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留伟葫,地道東北人恨搓。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像筏养,于是被迫代替她去往敵國和親奶卓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容