貝塞爾曲線做畫布和不規(guī)則布局

貝塞爾曲線也是自定義View的常用知識點(diǎn),在Android中也有API支持停撞,二階貝塞爾用的是quadTo瓷蛙,三階是cubicTo,只用嘴巴說是沒啥意思的戈毒,我們這來個(gè)小案例~畫布艰猬。
畫布就是讓人在屏幕上手指一動從而畫下他們劃過的痕跡是吧,好的我們下面開始埋市。先直接上代碼冠桃,CanvasLayout

public class CanvasLayout extends View {

    private Paint mPaint;
    private Path mPath;
    private float mPreX;
    private float mPreY;
    private Context mContext;

    public CanvasLayout(Context context) {
        this(context, null);
    }

    public CanvasLayout(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CanvasLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        mContext = context;
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(getResources().getDimension(R.dimen.dp_3));
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setAntiAlias(true);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                mPreX = event.getX();
                mPreY = event.getY();
                mPath.moveTo(mPreX, mPreY);
                return true;

            case MotionEvent.ACTION_MOVE:
                float mEndX = (mPreX + event.getX()) / 2;
                float mEndY = (mPreY + event.getY()) / 2;
                mPath.quadTo(mPreX,mPreY,mEndX,mEndY);
                mPreX = event.getX();
                mPreY = event.getY();
                invalidate();
                break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

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

在CanvasLayout構(gòu)造方法中,我們只是簡單定義Path和Paint的樣式道宅,重寫onTouchEvent方法食听。當(dāng)接收到ACTION_DOWN事件的時(shí)候利用mPath.moveTo(mPreX, mPreY);將起點(diǎn)放置在手指按下的位置。記得要返回true污茵,不然下面的ACTION_MOVE將不會接收到消息樱报,然后再ACTION_MOVE中,我們利用quadTo設(shè)置控制點(diǎn)和終點(diǎn)泞当,這里可能大家不明白迹蛤,控制點(diǎn)為啥是上一個(gè)點(diǎn)的X,Y坐標(biāo),終點(diǎn)是兩者相加的一半襟士?下面我們上個(gè)圖:


image.png

這樣控制點(diǎn)為兩者的一半盗飒,就一目了然了,但是這樣會有A到a0陋桂,a1到C的線段沒有畫出來逆趣,但是這不是問題,他的距離也就幾像素章喉,可以忽略不計(jì)的汗贫。整體效果如下:


1551235120304(1).gif

但是項(xiàng)目中我們會碰到這樣的界面:
1551236072494.gif

變形狀的布局是一個(gè)地圖身坐,紅色背景我們可以做其他的,我在項(xiàng)目就遇到了落包,前面是高德地圖的MapView部蛇,后面要打開我們的AR相機(jī),也就是一個(gè)AR導(dǎo)航項(xiàng)目咐蝇,這個(gè)怎么做呢涯鲁?我就直接上代碼了,下面再介紹有序。
準(zhǔn)備BesselLayout抹腿,做上面的可變形的layout,

public class BesselLayout extends RelativeLayout {
    private Paint paint;
    private int screeWidthOrHeight;
    private int heigth;
    private int width;
    private int mapHeight=150;
    private int beiserHeight=0;
    private RelativeLayout mRe;

    public BesselLayout(Context context) {
        this(context,null);
    }

    public BesselLayout(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public BesselLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        paint = new Paint();
        paint.setAntiAlias(true);
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        View view = inflate(context, R.layout.map_layout, this);
        mRe = view.findViewById(R.id.rl);
        beiserHeight = 150;
    }

    public RelativeLayout getmRe() {
        return mRe;
    }

    @Override
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        canvas.save();
        Path path=new Path();
        path.moveTo(0,0);
        path.lineTo(width,0);
        path.lineTo(width,mapHeight);
        path.quadTo(width/2,-beiserHeight,0,mapHeight);
        path.close();
        canvas.drawPath(path,paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        Path path2=new Path();
        path2.moveTo(0,0);
        path2.lineTo(width,0);
        path2.lineTo(width,mapHeight);
        path2.quadTo(width/2,-beiserHeight,0,mapHeight);
        path2.close();
        canvas.drawPath(path2,paint);

        canvas.restore();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        getScreeWidth();
    }

    public void kaiguan(float start, float end){
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setDuration(300);
        valueAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                beiserHeight=0;
            }

        });
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float mAnimatedValue = (float) valueAnimator.getAnimatedValue();
                beiserHeight= (int) (mAnimatedValue*150);
                mapHeight=(int)(mAnimatedValue*150);
                if(mapHeight<=0){
                    mapHeight=0;
                }
                if(beiserHeight<=0){
                    beiserHeight = 0;
                }
                invalidate();
            }
        });
        valueAnimator.start();
    }

    public void getScreeWidth(){
        //2旭寿、通過Resources獲取
        DisplayMetrics dm = getResources().getDisplayMetrics();
        heigth = dm.heightPixels;
        width = dm.widthPixels;
        screeWidthOrHeight = heigth > width ? width : heigth;
    }
}

在構(gòu)造方法里面設(shè)置Paint警绩,和關(guān)閉GPU加速,然后添加個(gè)布局進(jìn)來通過

View view = inflate(context, R.layout.map_layout, this);

dispatchDraw里面我們畫兩個(gè)不規(guī)則矩形盅称,就是采用貝塞爾曲線肩祥,畫的一個(gè)底部向上凹陷的矩形,畫兩個(gè)一模一樣缩膝,中間用paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));隔開混狠,這樣這個(gè)不規(guī)則矩形區(qū)域就是透明的了,如圖:


image.png

綠色區(qū)域就是變透明的區(qū)域疾层,粉紅色框框就是原本矩形的大小将饺,但是我們的底部用的是貝塞爾曲線畫的,當(dāng)控制點(diǎn)在上方的時(shí)候就會向上凹陷了痛黎。
至于怎么讓這個(gè)變直線或變曲線以及動畫予弧,就只要控制貝塞爾的控制點(diǎn)了,但是控制點(diǎn)的高度最低也是A舅逸、B點(diǎn)的的高度桌肴,不然曲線會變成向下的了。

然后再Activity的布局里面使用

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff3355"
    tools:context=".property.Main30Activity">
    <com.easy.customeasytablayout.customviews.property.BesselLayout
        android:id="@+id/my_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginTop="@dimen/dp_300">

    </com.easy.customeasytablayout.customviews.property.BesselLayout>
    <Button
        android:id="@+id/m_btn_kaiguan"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="開關(guān)"/>
</RelativeLayout>
public class Main30Activity extends AppCompatActivity {

    private BesselLayout myLayout;
    private RelativeLayout.LayoutParams myLayoutLayoutParams;
    private int mTopMargin;
    private boolean mOpen = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main30);
        myLayout = findViewById(R.id.my_layout);
        findViewById(R.id.m_btn_kaiguan).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(mOpen){
                    mOpen = false;
                    kaiguan(0,1);
                    myLayout.kaiguan(-1,1);
                }else{
                    mOpen = true;
                    kaiguan(1,0);
                    myLayout.kaiguan(1,-1);
                }

            }
        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        myLayoutLayoutParams = (RelativeLayout.LayoutParams) myLayout.getLayoutParams();
        mTopMargin = myLayoutLayoutParams.topMargin;
    }

    public void kaiguan(final float start, final float end) {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(start, end);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setDuration(300);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator valueAnimator) {
                float mAnimatedValue = (float) valueAnimator.getAnimatedValue();
                myLayoutLayoutParams = (RelativeLayout.LayoutParams) myLayout.getLayoutParams();
                int topMargin = (int) (mTopMargin*mAnimatedValue);
                myLayoutLayoutParams.setMargins(0, topMargin, 0, 0);
                myLayout.setLayoutParams(myLayoutLayoutParams);
            }
        });
        valueAnimator.start();
    }
}

其實(shí)貝塞爾曲線的作用很大琉历,要多多熟悉他哦坠七。
最后還是那句:不喜勿碰。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末旗笔,一起剝皮案震驚了整個(gè)濱河市彪置,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蝇恶,老刑警劉巖拳魁,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異撮弧,居然都是意外死亡潘懊,警方通過查閱死者的電腦和手機(jī)姚糊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來授舟,“玉大人救恨,你說我怎么就攤上這事∈褪鳎” “怎么了肠槽?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長奢啥。 經(jīng)常有香客問我秸仙,道長,這世上最難降的妖魔是什么桩盲? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任寂纪,我火速辦了婚禮,結(jié)果婚禮上赌结,老公的妹妹穿的比我還像新娘弊攘。我一直安慰自己,他們只是感情好姑曙,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著迈倍,像睡著了一般伤靠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上啼染,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天宴合,我揣著相機(jī)與錄音,去河邊找鬼迹鹅。 笑死卦洽,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的斜棚。 我是一名探鬼主播阀蒂,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼弟蚀!你這毒婦竟也來了蚤霞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤义钉,失蹤者是張志新(化名)和其女友劉穎昧绣,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捶闸,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡夜畴,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年拖刃,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贪绘。...
    茶點(diǎn)故事閱讀 40,680評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡兑牡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出兔簇,到底是詐尸還是另有隱情发绢,我是刑警寧澤,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布垄琐,位于F島的核電站边酒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏狸窘。R本人自食惡果不足惜墩朦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望翻擒。 院中可真熱鬧氓涣,春花似錦、人聲如沸陋气。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巩趁。三九已至痒玩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間议慰,已是汗流浹背蠢古。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留别凹,地道東北人草讶。 一個(gè)月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像炉菲,于是被迫代替她去往敵國和親堕战。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,691評論 2 361

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

  • 記錄下項(xiàng)目中使用貝塞爾曲線實(shí)現(xiàn)可點(diǎn)擊查看價(jià)格功能 1.XML使用方式 android:id="@+id/cus...
    劇透下閱讀 922評論 0 5
  • 談?wù)勜惾麪柷€ 最近在做項(xiàng)目的時(shí)候颁督,需要用到一個(gè)動畫践啄,非常簡單的動畫,簡單到就是直接對一個(gè)View做平移… 然而雖...
    雨潤聽潮閱讀 6,007評論 1 16
  • 貝塞爾曲線開發(fā)的藝術(shù) 一句話概括貝塞爾曲線:將任意一條曲線轉(zhuǎn)化為精確的數(shù)學(xué)公式沉御。 很多繪圖工具中的鋼筆工具屿讽,就是典...
    eclipse_xu閱讀 27,735評論 38 370
  • 系列文章之 Android中自定義View(一)系列文章之 Android中自定義View(二)系列文章之 And...
    YoungerDev閱讀 4,405評論 3 11
  • Fluentd是由Ruby和C編寫的,需要ruby進(jìn)行,然而安裝td-agent是fluentd 的易安裝版本伐谈,不...
    HelloWorld_26閱讀 2,927評論 2 0