【andorid進(jìn)階篇之View-自定義tablayout】

啥話不說,先亮出效果圖:






最近公司UI設(shè)計(jì)了一個這樣的tab切換的樣式,剛一看到UI設(shè)計(jì)圖,感覺還是不錯滴。但是幾秒鐘后下意識想到,這個效果,Android自帶的 tablayout 控件應(yīng)該并實(shí)現(xiàn)不了這效果呢。頓時一臉茫然,我還要自定義一個不成!但是這個弧度怎么搞…

此時有的小伙伴可能會想,自定義view太麻煩,不如讓UI直接把這個弧度切出來,不是一共就三個tab項(xiàng)嘛,Textview,ImageView… 這樣橫著排起來,然后做適當(dāng)?shù)娘@示和隱藏不就行啦。

是的,確實(shí)是可以,但是作為一個優(yōu)秀的開發(fā)人員。我們還是要優(yōu)選自定義view滴。

思路分析

(1) 由效果圖,我們很容易分析出,不管tab項(xiàng)一共幾個,無非就這三種情況。

草圖如下:



(2) 無論是哪種情況,首先我們需要畫出一個背景矩形,這個比較簡單。

(3)下面就是曲線的畫法,草圖中也已經(jīng)標(biāo)注了,很明顯我需要2個控制點(diǎn),那就需要用到三階貝塞爾曲線啦!cubicTo

(4)畫出圖形后,點(diǎn)擊事件如何響應(yīng)處理呢? 我們在 onTouchEvent 是能獲取到點(diǎn)擊控件后x,y坐標(biāo)的,判斷x的坐標(biāo)是在哪個tab項(xiàng)的范圍內(nèi),我們就認(rèn)為點(diǎn)擊了那個tab項(xiàng),就可以了。

(5)控件的圓角的如何實(shí)現(xiàn)呢?canvas的 范圍裁切 就可以啦。




代碼實(shí)現(xiàn)

思路梳理好了,那我們就寫代碼吧:

情況一(關(guān)鍵代碼):


//最左邊的圖形

Path pathLeft =new Path();

pathLeft.lineTo(textWidth, 0);

pathLeft.cubicTo(textWidth +arcControlX, arcControlY, textWidth +arcWidth -arcControlX, viewHeight -arcControlY, textWidth +arcWidth, viewHeight);

pathLeft.lineTo(0, viewHeight);

pathLeft.lineTo(0, 0);

paint.setColor(selectColor);

canvas.drawPath(pathLeft, paint);


步驟說明:

首先我們的起始點(diǎn)坐標(biāo)是(0,0),到坐標(biāo)(textWidth, 0) 畫一條直線;

然后三階貝塞爾曲線,2個控制點(diǎn)的坐標(biāo)1(textWidth + arcControlX,arcControlY),坐標(biāo)2(textWidth + arcWidth - arcControlX,viewHeight - arcControlY),結(jié)束的坐標(biāo)(textWidth + arcWidth,viewHeight);

最后畫直線到坐標(biāo)點(diǎn)(0, viewHeight),再到最終的原點(diǎn)(0, 0)。

到這里一個封閉的路徑,就畫好啦。情況二和情況三也是同樣的道理,我也不在太廢話了。

情況二(關(guān)鍵代碼):

//中間的圖形

? ? ? ? ? ? Path pathCenter = new Path();

? ? ? ? ? ? pathCenter.moveTo(tabPosition * textWidth + tabPosition * arcWidth, 0);

? ? ? ? ? ? pathCenter.cubicTo(tabPosition * textWidth + tabPosition * arcWidth - arcControlX, arcControlY, tabPosition * textWidth + tabPosition * arcWidth - arcWidth + arcControlX, viewHeight - arcControlY, tabPosition * textWidth + tabPosition * arcWidth - arcWidth, viewHeight);

? ? ? ? ? ? pathCenter.lineTo(tabPosition * textWidth + tabPosition * arcWidth + textWidth + arcWidth, viewHeight);

? ? ? ? ? ? pathCenter.cubicTo(tabPosition * textWidth + tabPosition * arcWidth + textWidth + arcWidth - arcControlX, viewHeight - arcControlY, tabPosition * textWidth + tabPosition * arcWidth + textWidth + arcControlX, arcControlY, tabPosition * textWidth + tabPosition * arcWidth + textWidth, 0);

? ? ? ? ? ? pathCenter.lineTo(tabPosition * textWidth + tabPosition * arcWidth, 0);

? ? ? ? ? ? paint.setColor(selectColor);

? ? ? ? ? ? canvas.drawPath(pathCenter, paint);


情況三(關(guān)鍵代碼):

//最右邊的圖形

? ? ? ? ? ? Path pathRight = new Path();

? ? ? ? ? ? pathRight.moveTo(viewWidth, 0);

? ? ? ? ? ? pathRight.lineTo(viewWidth - textWidth, 0);

? ? ? ? ? ? pathRight.cubicTo(viewWidth - textWidth - arcControlX, arcControlY, viewWidth - textWidth - arcWidth + arcControlX, viewHeight - arcControlY, viewWidth - textWidth - arcWidth, viewHeight);

? ? ? ? ? ? pathRight.lineTo(viewWidth, viewHeight);

? ? ? ? ? ? pathRight.lineTo(viewWidth, 0);

? ? ? ? ? ? paint.setColor(selectColor);

? ? ? ? ? ? canvas.drawPath(pathRight, paint);


tabtext的繪制(關(guān)鍵代碼):

for (int i = 0; i < tabTextList.size(); i++) {

? ? ? ? ? ? String strTabText = tabTextList.get(i);

? ? ? ? ? ? Rect rectText = new Rect();

? ? ? ? ? ? textPaint.getTextBounds(strTabText, 0, strTabText.length(), rectText);

? ? ? ? ? ? int strWidth = rectText.width();

? ? ? ? ? ? int strHeight = rectText.height();

? ? ? ? ? ? if (i == 0) {

? ? ? ? ? ? ? ? canvas.drawText(strTabText, (textWidth + arcWidth / 2) / 2 - strWidth / 2, viewHeight / 2 + strHeight / 2, textPaint);

? ? ? ? ? ? } else if (i == tabTextList.size() - 1) {

? ? ? ? ? ? ? ? canvas.drawText(strTabText, viewWidth - (textWidth + arcWidth / 2) / 2 - strWidth / 2, viewHeight / 2 + strHeight / 2, textPaint);

? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? canvas.drawText(strTabText, textWidth * i + arcWidth * (i - 1) + (textWidth + 2 * arcWidth) / 2 - strWidth / 2, viewHeight / 2 + strHeight / 2, textPaint);

? ? ? ? ? ? }

? ? ? ? }


tab點(diǎn)擊處理(關(guān)鍵代碼):

@Override

? ? public boolean onTouchEvent(MotionEvent event) {

? ? ? ? boolean isHandleClick = false;//是否處理點(diǎn)擊事件

? ? ? ? switch (event.getAction()) {

? ? ? ? ? ? case MotionEvent.ACTION_DOWN:

? ? ? ? ? ? ? ? float x = event.getX();

? ? ? ? ? ? ? ? float y = event.getY();

? ? ? ? ? ? ? ? System.out.println("YPKTabLayoutView.onTouchEvent x=" + x + " y=" + y);

? ? ? ? ? ? ? ? for (int i = 0; i < tabNumber; i++) {

? ? ? ? ? ? ? ? ? ? if (x <= ((i + 1) * textWidth + i * arcWidth + arcWidth / 2)) {//點(diǎn)擊的第一個按鈕

? ? ? ? ? ? ? ? ? ? ? ? tabPosition = i;

? ? ? ? ? ? ? ? ? ? ? ? if (onTabClickListener != null) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? onTabClickListener.tabSelectedListener(tabPosition);

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ? ? invalidate();

? ? ? ? ? ? ? ? ? ? ? ? isHandleClick = true;

? ? ? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? return isHandleClick;

? ? ? ? ? ? case MotionEvent.ACTION_MOVE:

? ? ? ? ? ? ? ? break;

? ? ? ? ? ? case MotionEvent.ACTION_UP:

? ? ? ? ? ? ? ? break;

? ? ? ? }

? ? ? ? return super.onTouchEvent(event);

? ? }


遠(yuǎn)程依賴使用

一: 添加依賴

Add it in your root build.gradle at the end of repositories:


allprojects {

repositories {

...

maven { url 'https://jitpack.io' }

}

}


Add the dependency

dependencies {

? implementation 'com.github.dacaoyuan:YPKTabDemo:1.0.2'

}


二:在xml布局中添加

<com.ypk.library.view.YPKTabLayoutView

? ? ? ? android:id="@+id/mYPKTabLayoutView"

? ? ? ? android:layout_width="match_parent"

? ? ? ? android:layout_height="wrap_content"

? ? ? ? android:layout_margin="10dp"

? ? ? ? app:view_bg_corners="0"

? ? ? ? app:arcControlX="30" />


三:代碼中

val tabTextList: MutableList<String> = ArrayList<String>()

? ? ? ? tabTextList.add("推薦學(xué)習(xí)");

? ? ? ? tabTextList.add("企業(yè)學(xué)院");

? ? ? ? tabTextList.add("我的關(guān)注");

? ? ? ? mYPKTabLayoutView.setTabTextList(tabTextList);

? ? ? ? mYPKTabLayoutView.addTabSelectedListener { tabPosition ->

? ? ? ? ? ? val makeText =

? ? ? ? ? ? ? ? Toast.makeText(

? ? ? ? ? ? ? ? ? ? this@MainActivity,

? ? ? ? ? ? ? ? ? ? "點(diǎn)擊了第" + tabPosition + "項(xiàng)",

? ? ? ? ? ? ? ? ? ? Toast.LENGTH_SHORT

? ? ? ? ? ? ? ? )

? ? ? ? ? ? makeText.setGravity(Gravity.CENTER, 0, 0);

? ? ? ? ? ? makeText.show();

? ? ? ? }


屬性說明:

屬性| 說明

- | -

tabTextColor| tab的文字顏色

tabTextSize| tab文字大?。▎挝唬簊p)

tab_view_bg| 控件背景色

select_tab_color| 選中后tab的背景色

arcControlX| 值越大,曲線弧度越大(單位:px)

view_bg_corners| 控件的圓角大?。▎挝唬篸p)

文章到這里就已經(jīng)說完啦,如果對你有些幫助的話,順手也給點(diǎn)個贊吧,十分感謝!整理這篇文章快用2個星期時間了。如果你對實(shí)現(xiàn)思路有更好的建議,歡迎留言,指正。



源碼地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容