android一种不规则布局的实现方式

一.背景
正准备出去抽根烟的你突然被产品经理叫住了,“快来,快来,我告诉你一个好消息,前几天我不是说要给 app 的菜单入口这一截 UI 美化一下嘛,我昨天突发灵感,已经想好了一个很有特色的样式,这下我们的 UI 一定和别人的不一样,我们要让产品富有品牌表现力”。
在这里插入图片描述
这看起来是挺有特别的,不过感觉有违 android UI 排列常理呀,形状倒是没什么问题,关键是这无缝衔接就有点奇怪了,这形状最简单的就是直接切几个梯形图当背景就实现了,但是想让控件边缘两两完美契合就得让两个控件重叠一部分,那点击时不就会错乱了,那肯定不行。

二.思路分析
实际布局的宽是以较长边决定的,例如上图中,第二个控件和第一个控件有一部分是重叠的,这是为了让控件两两之间看上去是完美契合的。这里就要考虑一个点击区域问题了,上图中红圈部分实际上全部是第二个控件,但是当用户点击 1 的那个梯形右下角区域,应该让第一个控件响应,点击 2 的那个梯形左上角,应该让第二个控件响应。
不仅要控制点击和契合图形,最好还要能支持设置这个梯形控件是正梯形还是倒梯形(下边更宽还是上边更宽),能设置宽度数值,能设置左边或是右边是否垂直;所以总结如下:
1.支持设置宽度数值,支持设置两边是否垂直,这样用固定图片就不太合适了,干脆通过自定义 View 来实现;
2.实现两控件间完美契合,这可以在自定义 View 时让截好的梯形以外的部分透明显示,然后让右边的控件往左 margin 负的上边和下边宽度相差的一半就正好契合了。
3.支持代码动态创建布局,设置背景图片,设置文字。

三、具体实现
第一步先实现这个自定义形状的控件,大概需要这几个参数,上宽,下宽,高度,左边是否垂直,右边是否垂直

TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PaintView, defStyleAttr, 0);
topWidth = ta.getDimensionPixelSize(R.styleable.PaintView_topWidth, 100);
bottomWidth = ta.getDimensionPixelSize(R.styleable.PaintView_bottomWidth, 100);
mHeight = ta.getDimensionPixelSize(R.styleable.PaintView_height, 100);
leftRect = ta.getBoolean(R.styleable.PaintView_leftRect, false);
rightRect = ta.getBoolean(R.styleable.PaintView_rightRect, false);

然后规划梯形路径

paint.setStyle(Paint.Style.FILL_AND_STROKE); //设置边框
paint.setStrokeWidth(strokWidth);
paint.setStrokeJoin(Paint.Join.ROUND); //设置圆角
mDrawPath = new Path();

int offset = strokWidth / 2;
if (topWidth > bottomWidth) {
   
    mDrawPath.moveTo(offset, offset);
    mDrawPath.rLineTo(topWidth, 0);
    mDrawPath.lineTo(bottomWidth + (topWidth - bottomWidth) / (rightRect ? 1 : 2) + offset, mHeight + offset);
    mDrawPath.lineTo((leftRect ? 0 : (topWidth - bottomWidth) / 2) + offset, mHeight + offset);
    mDrawPath.close();
} else {
   
    mDrawPath.moveTo((leftRect ? 0 : (bottomWidth - topWidth) / 2) + offset, offset);
    mDrawPath.lineTo((bottomWidth - topWidth) / (rightRect ? 1 : 2) + topWidth + offset, offset);
    mDrawPath.lineTo(bottomWidth + offset, mHeight + offset);
    mDrawPath.lineTo(offset, mHeight + offset);
    mDrawPath.close();
}

路径规划好后就开始测量绘制了

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
   
    setMeasuredDimension(Math.max(topWidth, bottomWidth) + strokWidth, mHeight + strokWidth);
}

@Override
protected void onDraw(Canvas canvas) {
   
    super.onDraw(canvas);
    canvas.drawPath(mDrawPath, paint);
}

下面是布局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.lcp.customeview.widget.PaintView
        android:id="@+id/ldder1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginLeft="20dp"
        android:layout_marginBottom="20dp"
        android:focusableInTouchMode="true"
        android:focusable="true"
        app:bottomWidth="80dp"
        app:height="100dp"
        app:pcolor="#d78f8f"
        app:topWidth="50dp"
        app:leftRect="true"/>

    <com.lcp.customeview.widget.PaintView
        android:id="@+id/ldder2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:focusableInTouchMode="true"
        android:focusable="true"
        android:layout_marginLeft="-14dp"
        android:layout_toRightOf="@id/ldder1"
        app:bottomWidth="50dp"
        app:height="100dp"
        app:pcolor="#bc9747"
        app:topWidth="80dp" />

    <com.lcp.customeview.widget.PaintView
        android:id="@+id/ldder3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:layout_marginBottom="20dp"
        android:focusableInTouchMode="true"
        android:focusable="true"
        android:layout_marginLeft="-14dp"
        android:layout_toRightOf="@id/ldder2"
        app:bottomWidth="80dp"
        app:height="100dp"
        app:pcolor
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值