自定义View篇Android项目Android 技术开发

Android卡片布局(圆角阴影)的几种实现及分析

2018-11-23  本文已影响198人  Man不经心

前言

在开发中,为了界面美观,圆角view和阴影效果是开发中经常遇到的UI场景,比如银行卡效果,卡片式itemView布局,Banner图等,开发中我们通过各种方式实现了这种效果,但是哪种方案最好呢,接下来本文将比较几种常见的圆角阴影布局实现,并从内存占用角度分析它们的优缺点.


卡片式布局.png

解决方案

在Android开发中,有以下几种解决方案:

存在的问题

以上几种方案都能实现圆角阴影布局的实现,但是如果出现图片顶边展示的场景时,并不能能保证图片也是圆角的,经过验证,得出以下结论并例举了几种实现方式:

代码实现

    //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内边距,Glide配合形变transform 处理图片圆角
    if (Build.VERSION.SDK_INT < 21) {
        //去除5.0以下cardView内间距
        cardview.setUseCompatPadding(false);
        cardview.setPreventCornerOverlap(false);
        //圆角形变,除去左上左下两个角,设置右上右下两个角为圆角
        CornerTransform transform = new CornerTransform(this, dip2px(10));
        transform.setExceptCorner(true, false, true, false);
        Glide.with(this)
                .load(url3)
                .apply(RequestOptions.centerCropTransform())//先centerCrop再设置图片圆角,否则会覆盖圆角效果
                .apply(RequestOptions.bitmapTransform(transform))
                .into(cover);
    
        return;
    }

<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">
 <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"/>
<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">

内存使用分析

通过以上几种方式实现了圆角阴影图片顶边显示的效果,但是哪一种更好呢,或者说哪一种更加适合我们的开发呢,其实简单的一些静态页面展示我认为这几种方案都不错,但是当我们在RecycleView中进行滑动时,控件的性能变得特别重要,所以使用了RecycleView模拟了他们在开发中的使用:
在RecycleView页面,我使用了95张图片,加载10页,通过AndroidStudio自带内存检测工具记录了他们在页面加前和加载十次之后的稳定内存值,使用的手机是VIVO X21A;
下面是测试结果:

解决方案 页面加载前内存值 10次加载后内存值 消耗内存
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

可以看出,无论是在过度绘制情况方面还是内存使用角度,cardview都是性能最好的,自定义RelativeLayout或者FrameLayout则在性能上较差

使用中的选择

自定义RelativeLayout或者FrameLayout的方式上面被验证为性能不够优秀,那么CardView的两种实现即5.0以上cardview直接实现和5.0以下cardview+glide特殊处理图片及cardview+RcImage两种那种更好呢,

可以看出,5.0以下设备占比仅一成左后,所以5.0以下的代码执行占比较低,综合来看,选用cardView+glide5.0以下特殊处理图片的方式更好一些

源码分析

自定义cardView 打造一个通用的新闻标签视图 PictureTextCardView(V1.0) 自定义cardview.jpg

继承自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));
            imgGravity = ta.getString(R.styleable.PictureTextCardView_img_gravity);
            ta.recycle();
        }

        init(context);

    }

提供各个子view的获取方法及一些设置

public interface  ICardInterface{
    /**
     * 初始化布局,direction为图片位置:1左,2顶部,3右
     * @param direction
     */
    void setViews(int direction);
    TextView getTitle();
    TextView getContents();
    TextView getRemark();
    ImageView getImageView();
    void setTitleVisible(boolean visible);
    void setContentsVisible(boolean visible);
    void setRemarkVisible(boolean visible);
    void setImageVisible(boolean visible);
    void setTitleText(String str);
    void setContentsText(String str);
    void setRemarkText(String str);
    void setImageView(Context context,String str);

}

使用同cardView一样 建议通过xml控制一些布局属性

<com.transportmm.tsportapp.mvp.ui.dsh.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="@dimen/dp_10"
            />
/>

配合代码填充布局内容(如果xml中设置了img_gravity属性,setViewsLayout方法不要执行,防止view重绘)

    if (Build.VERSION.SDK_INT<21){
        ptcardviewLeft.setUseCompatPadding(false);
        ptcardviewLeft.setPreventCornerOverlap(false);
    }
    ptcardviewLeft.setViewsLayout(PictureTextCardView.POSITION_LEFT);
    ptcardviewLeft.getTitle().setText("自定义cardview:PictureTextCardView");
    ptcardviewLeft.getContents().setText("图片靠左展示");
    ptcardviewLeft.getRemark().setText("2018-11-23");
    Glide.with(this)
            .load(url)
            .apply(RequestOptions.centerCropTransform())
            .into(ptcardviewLeft.getImageView());
        

内存使用分析:

解决方案 页面加载前内存值 10次加载后内存值 消耗内存
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
PictureTextCardView 104.34 201.05 96.71

可以看出,PictureTextCardView性能介于原生CardView和CardView+RCImageView之间,过度绘制表现方面跟其他自定义view一致.

项目demo地址

上一篇下一篇

猜你喜欢

热点阅读