最近项目中用到这种效果,UX 圣诞度假,只能自力更生,开一波车了,记录下爬坑过程
先来个效果图
中间用到这个函数
public void quadTo(float x1, float y1, float x2, float y2){
...
}
这个参数x1,y1,是贝塞尔曲线的控制点,也就是起始位置的切线交点,弧形并没有经过它,这里我们需要三点确定圆弧,这里的贝塞尔曲线为二阶,为抛物线 f(x) = a * x * x+b * x+c,已知经过三点A(0,0),B(0,AB),C(AB/2,mArcImageViewTopHeight),可以求出函数表达式,再求其一阶导函数,带入A,B 点,得到AD BD 一次函数的斜率,分别带入A,B点,求出其切线方程,然后取得交点,一番折腾终于见到答案了,为 2 * mArcImageViewTopHeight,
既然是二倍,那用几何推理证明下,如何证明?
还是回到正题吧,这是利用贝塞尔曲线画圆弧,当然这里面还需要证明 二阶贝塞尔曲线 为抛物线,这样推理就严密了,闲了再补充吧,
还有一种是画真正的圆弧,而不是贝塞尔曲线,那么我们需要计算圆弧所在圆的外接正方形的四点坐标,不过感觉这两个画出来的效果 差不多吧,看需求实现吧
CD,AC 为我们已知的高度
不妨 设 OA = OD = R ,AC = d CD = h
三角形OCA 勾股定理 得方程
R * R - (R - d) * (R - d) = h * h
R = (d * d + h * h) / (2 * h)
此时,圆的外接正方形四个点 就有了 可以愉快的画真正的圆弧了
这里用的渐变色,如果是背景图片 需要用到BitmapShader
至于用到的android api 网上介绍很多就不展开了 这里用的是贝塞尔曲线实现的圆弧
全部代码
package com.example.zhangyan.myapplication;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Path;
import android.graphics.Shader;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import com.zhang.test.R;
public class ArcBackgroundView extends View {
private int mWidth;
private int mHeight;
private int mArcImageViewTopHeight;
private Path mPath;
private PathShape mPathShape;
private int mArcBackgroundViewEndColor;
private int mArcBackgroundViewStartColor;
private int[] mColors;
private LinearGradient mLinearGradient;
private ShapeDrawable mShapeDrawable;
public ArcBackgroundView(Context context) {
this(context, null);
}
public ArcBackgroundView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public ArcBackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public ArcBackgroundView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs, context);
}
private void init(AttributeSet attrs, Context context) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ArcBackgroundView);
mArcBackgroundViewEndColor = typedArray.getColor(R.styleable.ArcBackgroundView_ArcBackgroundViewEndColor, mArcBackgroundViewEndColor);
mArcBackgroundViewStartColor = typedArray.getColor(R.styleable.ArcBackgroundView_ArcBackgroundViewStartColor, mArcBackgroundViewStartColor);
mArcImageViewTopHeight = typedArray.getDimensionPixelOffset(R.styleable.ArcBackgroundView_ArcBackgroundViewTopHeight, 0);
mPath = new Path();
typedArray.recycle();
mColors = new int[]{mArcBackgroundViewStartColor, mArcBackgroundViewEndColor};
mShapeDrawable = new ShapeDrawable();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPath.reset();
mPath.moveTo(0, 0);
mPath.quadTo(mWidth/2, mArcImageViewTopHeight * 2,mWidth, 0);
mPath.lineTo(mWidth,mHeight);
mPath.lineTo(0,mHeight);
mPath.close();
if (mPathShape == null) {
mPathShape = new PathShape(mPath, mWidth,mHeight);
mLinearGradient = new LinearGradient(0, 0, 0, mHeight, mColors, null, Shader.TileMode.CLAMP);
}
mShapeDrawable.setShape(mPathShape);
mShapeDrawable.setBounds(0,0,mWidth,mHeight);
mShapeDrawable.getPaint().setShader(mLinearGradient);
mShapeDrawable.draw(canvas);
}
}
自定义属性
<declare-styleable name="ArcBackgroundView">
<attr name="ArcBackgroundViewStartColor" format="color" />
<attr name="ArcBackgroundViewTopHeight" format="dimension" />
<attr name="ArcBackgroundViewEndColor" format="color" />
</declare-styleable>
使用
<com.example.zhangyan.myapplication.ArcBackgroundView
android:layout_width="match_parent"
android:layout_marginTop="90dp"
app:ArcBackgroundViewEndColor="#f0f"
app:ArcBackgroundViewStartColor="#ff0"
app:ArcBackgroundViewTopHeight="80dp"
android:layout_height="400dp" />