【Android】自定义高复用布局

先PO效果图

条状布局

卡片布局

以上两种基本是比较常用的布局类型,以第一个图为例进行说明。

先看xml代码,凸显一下效率~~

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/theme_white_color">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <ImageView
            android:layout_width="match_parent"
            android:layout_height="200dp"
            android:background="@mipmap/ic_top_nor"/>

        <xxx.LineLinearLayout
            android:id="@+id/line1"
            app:line_image_right="@mipmap/ic_launcher"
            app:line_title_text="巴拉巴拉巴拉"
            style="@style/line_common"
            />

        <xxx.LineLinearLayout
            android:id="@+id/line2"
            app:line_image_right="@mipmap/ic_launcher"
            app:line_title_text="巴拉巴拉巴拉"
            app:line_image_left="@mipmap/ic_back"
            style="@style/line_common"
            app:line_linepview_size_h="0dp"
            />
    </LinearLayout>

</FrameLayout>

需要设置的三个参数:

  1. app:line_image_right
  2. app:line_title_text
  3. app:line_image_left

以及一个style:style="@style/line_common"

通过应用方式可以看出来,三个参数都是在attrs中设置的。
这种复用方式和<include layout="@layout/include_header_layout"/>的区别在于,前者可以在一个页面中多样式复用,而后者只能同样式复用(View.GONE等不计入为样式变更)。

实现

实现原理大概如下:
在这里插入图片描述

先定义一个布局原型,设置一下大概长什么样
比如第一个图 可以是这样:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:id="@+id/ll_line_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <ImageView
            android:gravity="center_vertical"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/iv_line_left" />

        <TextView
            android:gravity="center_vertical"
            android:id="@+id/tv_line_title"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <ImageView
            android:gravity="center_vertical"
            android:id="@+id/iv_line_right"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </LinearLayout>

    <View
        android:id="@+id/v_line"
        android:layout_width="10dp"
        android:layout_height="10dp"/>

</LinearLayout>

里边有四个View:

  1. 靠左的图片
  2. 中间的文字
  3. 靠右的图片
  4. 下方的分割线

在attrs定义属性,大概包含了个控件的尺寸、内容、可设置的颜色、离上下左右的距离等,示例:

<!-- LineLinearLayout属性 -->
    <declare-styleable name="LineLinearLayout">
        <attr name="line_title_text" format="string" />     //标题文字内容
        <attr name="line_title_text_size" format="dimension" />  //标题文字尺寸
        <attr name="line_title_text_color" format="color" />   //标题文字颜色
        <attr name="line_title_text_h" format="dimension"/>  //标题文字高度

        <attr name="line_image_left" format="reference" />   //图片资源
        <attr name="line_image_left_width" format="dimension" /> //imageView的宽度
        <attr name="line_image_left_height" format="dimension" />  //imageView的高度

        <attr name="line_image_right" format="reference" />   //图片资源
        <attr name="line_image_right_width" format="dimension" /> //imageView的宽度
        <attr name="line_image_right_height" format="dimension" />  //imageView的高度

        <attr name="line_linepview_size_w" format="dimension"/> //分割线的宽
        <attr name="line_linepview_size_h" format="dimension"/> //分割线的高
        
        <attr name="line_color" format="color" /> //背景色
    </declare-styleable>

然后写个class,继承LinearLayout,示例:

/**
 * 描述:各页面条状布局···
 * 创建者:uichi
 * 创建时间:2019/1/14  16:26
 * 建议所有margin值都列出来
 */
public class LineLinearLayout extends LinearLayout {

    private ImageView leftImage;
    private ImageView rightImage;
    private TextView title;
    private View lineview;
    private LinearLayout linearLayout;

    //显示内容
    private String titleText;
    private int titleTextColor;
    private int titleTextSize;
    private int titleTextH;

    //左边图片
    private Drawable drawableLeft;
    private int  drawableLeftw;
    private int  drawableLefth;

    //右边图片
    private Drawable drawableRight;
    private int  drawableRightw;
    private int  drawableRighth;

    //底部分割线
    private int lineHight;
    private int lineWidth;
    private int lineColor;

    public LineLinearLayout(Context context) {
        super(context);
    }

    public LineLinearLayout(Context context,@Nullable AttributeSet attrs) {
        super(context, attrs);
        if (!isInEditMode()) {
            //解决可视化编辑器无法识别自定义控件的问题
            // 在构造函数中将Xml中定义的布局解析出来。
            LayoutInflater.from(context).inflate(R.layout.include_layout_linecard, this, true);
            leftImage = findViewById(R.id.iv_line_left);
            rightImage = findViewById(R.id.iv_line_right);
            title = findViewById(R.id.tv_line_title);
            linearLayout = findViewById(R.id.ll_line_layout);
            lineview = findViewById(R.id.v_line);
        }

        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LineLinearLayout);
        titleText = a.getString(R.styleable.LineLinearLayout_line_title_text);
        titleTextColor = a.getColor(R.styleable.LineLinearLayout_line_title_text_color, Color.WHITE);
        titleTextSize = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_title_text_size, 12);
        titleTextH = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_title_text_h, 60);

        drawableLeftw = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_image_left_width, 0);
        drawableLefth = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_image_left_height, 0);
        drawableLeft = a.getDrawable(R.styleable.LineLinearLayout_line_image_left);

        drawableRightw = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_image_right_width, 0);
        drawableRighth = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_image_right_height, 0);
        drawableRight = a.getDrawable(R.styleable.LineLinearLayout_line_image_right);

        lineHight = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_linepview_size_h, 0);
        lineWidth = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_linepview_size_w, 0);

        lineColor = a.getColor(R.styleable.LineLinearLayout_line_color, 0xffffff);

        if (!isInEditMode()) {
            //将获取到的值设置到相应位置
            LayoutParams linealayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, titleTextH);
            linealayoutParams.setMargins(0, 0, 0, 0);
            linearLayout.setBackgroundColor(lineColor);
            linearLayout.setLayoutParams(linealayoutParams);

            title.setText(titleText);
            title.setTextSize(TypedValue.COMPLEX_UNIT_PX, titleTextSize);
            title.setTextColor(titleTextColor);

            LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
            layoutParams.setMargins(0, 0, 0, 0);
            layoutParams.gravity = Gravity.CENTER;
            title.setLayoutParams(layoutParams);


            if(drawableLeft!=null){
                leftImage.setImageDrawable(drawableLeft);
                LayoutParams imageLeftLayoutParams = new LayoutParams(drawableLeftw, drawableLefth);
                imageLeftLayoutParams.gravity = Gravity.CENTER;
                imageLeftLayoutParams.setMargins(10, 0, 0, 0);
                leftImage.setLayoutParams(imageLeftLayoutParams);
            }else {
                LayoutParams imageLeftLayoutParams = new LayoutParams(0, 0);
                imageLeftLayoutParams.setMargins(0, 0, 0, 0);
                leftImage.setLayoutParams(imageLeftLayoutParams);
            }

            rightImage.setImageDrawable(drawableRight);

            LayoutParams imageRightLayoutParams = new LayoutParams(drawableRightw, drawableRighth);
            imageRightLayoutParams.gravity = Gravity.CENTER;
            imageRightLayoutParams.weight = 1;
            imageRightLayoutParams.setMargins(0, 0, 10, 0);
            rightImage.setLayoutParams(imageRightLayoutParams);

            LayoutParams imageLineLayoutParams = new LayoutParams(lineWidth, lineHight);
            //imageRightLayoutParams.gravity = Gravity.CENTER;
            lineview.setBackgroundColor(lineColor);
            lineview.setLayoutParams(imageLineLayoutParams);
        }
        a.recycle(); //这个别忘了
    }
}

大概的意思是:

  1. 先把View读出来
  2. new一个TypedArray对象,用于读取attrs
  3. 利用attrs中的值对各个布局重设宽、高、居中、内容、颜色等属性
    其中有一段:
		if(drawableLeft!=null){
              leftImage.setImageDrawable(drawableLeft);
              LayoutParams imageLeftLayoutParams = new LayoutParams(drawableLeftw, drawableLefth);
              imageLeftLayoutParams.gravity = Gravity.CENTER;
              imageLeftLayoutParams.weight = 1;
              imageLeftLayoutParams.setMargins(10, 0, 0, 0);
              leftImage.setLayoutParams(imageLeftLayoutParams);
          }else {
              LayoutParams imageLeftLayoutParams = new LayoutParams(0, 0);
              imageLeftLayoutParams.setMargins(0, 0, 0, 0);
              leftImage.setLayoutParams(imageLeftLayoutParams);
          }

drawableLeft在布局页面中有传入的话,就设置为(drawableLeftw, drawableLefth),没有传入就不占空间了,设置为(0, 0)
另外,从TypedArray中读取的数据,部分类型需要默认值,比如:

titleTextSize = a.getDimensionPixelOffset(R.styleable.LineLinearLayout_line_title_text_size, 12);

表示如果TextSize没有设置,那就默认为12。

接下来在xml布局文件中引用这个控件:

		<你的路径.LineLinearLayout
            android:id="@+id/line1"
            app:line_image_right="@mipmap/ic_launcher"
            app:line_title_text="巴拉巴拉巴拉"
            ······
            />

attrs中定义的属性基本都要赋值,但对同一个页面来说,基本的字号、背景色、尺寸等都是一致的,所以可以把attrs赋值写在style中。示例:

	<style name="line_common">
        <item name="line_image_left_height">20dp</item>
        <item name="line_image_left_width">20dp</item>
        <item name="line_color">@color/theme_white</item>
        <item name="line_title_text_size">20dp</item>
        <item name="line_title_text_color">@color/theme_blue_color</item>
        <item name="line_title_text_h">60dp</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">wrap_content</item>
        <item name="line_image_right_height">@dimen/margin_size50</item>
        <item name="line_image_right_width">60dp</item>
        <item name="line_linepview_size_w">10dp</item>
        <item name="line_linepview_size_h">2dp</item>
    </style>

然后在xml中引用,也可以在xml中重写,比如,第一图中,最后一行没有分割线,重写为:app:line_linepview_size_h="0dp"

		<你的路径.LineLinearLayout
            android:id="@+id/line2"
            app:line_image_right="@mipmap/ic_launcher"
            app:line_title_text="巴拉巴拉巴拉"
            app:line_image_left="@mipmap/ic_back"
            style="@style/line_common"
            app:line_linepview_size_h="0dp"
            />

使用的方式还是比较多的,图中的设计样式是前边一截没有分割线,后边一截有分割线,不需要去画分割线,可以反过来将需要分割线的地方空出来。
如果是两边没分割线,中间有分割线呢?
这个时候需要在attrs中再添加两个属性:layout_marginRightlayout_marginLeft,示例:

//原有的
		LayoutParams imageLineLayoutParams = new LayoutParams(lineWidth, lineHight);
            //imageRightLayoutParams.gravity = Gravity.CENTER;
            lineview.setBackgroundColor(lineColor);
            lineview.setLayoutParams(imageLineLayoutParams);
//新的
		LayoutParams imageLineLayoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, lineHight);
			imageLineLayoutParams.setMargins(marginLeft, 0, marginRight, 0);// 左、上、右、下
            imageRightLayoutParams.gravity = Gravity.CENTER; 	//居中
            lineview.setBackgroundColor(lineColor);
            lineview.setLayoutParams(imageLineLayoutParams);

再在style中增加marginLeft、marginRight设置,xml中引用时,就已经变成了仅中间有分割线。
至此,就完事了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值