Android自定義View實(shí)現(xiàn)繪制虛線(xiàn)的方法詳解
前言
說(shuō)實(shí)話(huà)當(dāng)?shù)谝淮慰吹竭@個(gè)需求的時(shí)候,第一反應(yīng)就是Canvas只有drawLine方法,并沒(méi)有drawDashLine方法??!這咋整啊,難道要我自己做個(gè)遍歷不斷的drawLine?不到1秒,我就放棄這個(gè)想法了,因?yàn)樘珢盒牧?。方法肯定是有的,只不過(guò)我不知道而已。
繪制方法
最簡(jiǎn)單的方法是利用ShapeDrawable,比如說(shuō)你想用虛線(xiàn)要隔開(kāi)兩個(gè)控件,就可以在這兩個(gè)控件中加個(gè)View,然后給它個(gè)虛線(xiàn)背景。
嗯,理論上就是這樣子的,實(shí)現(xiàn)上也很簡(jiǎn)單。
<!-- drawable 文件 --> <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="line"> <stroke android:width="1dp" android:color="@color/dash_line" android:dashGap="2dp" android:dashWidth="3dp"/> </shape>
<!-- 布局文件 --> <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:gravity="center" tools:context="hope.example.dashlinedemo.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="分享給微信好友"/> <View android:layout_width="match_parent" android:layout_height="2dp" android:background="@drawable/dash_line" /> <TextView android:layout_width="match_parent" android:layout_height="40dp" android:gravity="center" android:text="分享至朋友圈" /> </LinearLayout>
寫(xiě)完之后,從Android Studio的預(yù)覽功能上就可以看到效果了。

是不是很簡(jiǎn)單呢?慢著,先別高興得太早了,真機(jī)上跑一下看看效果先。

這什么鬼,明明是虛線(xiàn)才對(duì)的啊,Studio上的預(yù)覽也可以看出代碼沒(méi)問(wèn)題的。其實(shí),這是因?yàn)槲覀儸F(xiàn)在的手機(jī)默認(rèn)都是開(kāi)啟了硬件加速的,而dashGap不支持硬件加速,我們只需要修改下View的參數(shù),把硬件加速關(guān)了就好了。
<View android:layout_width="match_parent" android:layout_height="2dp" android:background="@drawable/dash_line" android:layerType="software"/>
使用ShapeDrawable實(shí)現(xiàn)虛線(xiàn)的方式雖然簡(jiǎn)單,但是簡(jiǎn)單就意味著不靈活。比如說(shuō)要求虛線(xiàn)是根據(jù)用戶(hù)操作來(lái)判斷要不要添加的,這種情況下就不如使用Canvas來(lái)實(shí)現(xiàn)方便了。
前面說(shuō)了,Canvas只有drawLine方法,沒(méi)有drawDashLine方法。但是你要知道,畫(huà)什么雖然是Canvas決定的,但是怎么畫(huà)卻是由畫(huà)筆Paint決定的。接下來(lái)看看這神奇的畫(huà)筆怎么幫我們把直線(xiàn)畫(huà)成虛的吧。
Paint有setPathEffect(PathEffect effect)這么一個(gè)方法,PathEffect一共有五個(gè)子類(lèi):ComposePathEffect, CornerPathEffect, DashPathEffect, DiscretePathEffect, PathDashPathEffect, SumPathEffect, 其中的DashPathEffect就是我們需要的虛線(xiàn)效果,其它的幾種效果大家可以自己試一試。
DashPathEffect的創(chuàng)建需要兩個(gè)參數(shù),一個(gè)float數(shù)組,代表實(shí)線(xiàn)與空白處的長(zhǎng)度。比如說(shuō)我給的參數(shù)是new float[] {10, 5} ,那么虛線(xiàn)的一個(gè)單位就是10像素的實(shí)線(xiàn)加上5像素的空白,然后以它為單位去不斷重復(fù)畫(huà),從而形成一條虛線(xiàn)。而如果設(shè)定的參數(shù)是new float[] {10, 5,20, 10},那么虛線(xiàn)的一個(gè)單位就是由10像素實(shí)線(xiàn),5像素空白,20像素實(shí)線(xiàn),10像素空白組成的虛線(xiàn)段。
另一個(gè)參數(shù)代表偏移,一般我們?cè)O(shè)置為0即可,如果要實(shí)現(xiàn)虛線(xiàn)的動(dòng)畫(huà)效果的話(huà),可以不斷改變這個(gè)值,從而讓虛線(xiàn)動(dòng)起來(lái)。
接著實(shí)現(xiàn)這個(gè)自定義View,并將它代替上面xml文件中的虛線(xiàn)(View)即可。
public class DashLineView extends View {
private Paint mPaint;
public DashLineView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(getResources().getColor(R.color.dash_line));
mPaint.setStrokeWidth(3);
mPaint.setPathEffect(new DashPathEffect(new float[] {5, 5}, 0));
}
@Override
protected void onDraw(Canvas canvas) {
int centerY = getHeight() / 2;
canvas.drawLine(0, centerY, getWidth(), centerY, mPaint);
}
}
代碼跑起來(lái)之后又發(fā)現(xiàn)虛線(xiàn)變成了直線(xiàn),根本就沒(méi)有效果。有了上面使用ShapeDrawable的經(jīng)驗(yàn)之后,我們完全有理由猜測(cè)這是由于硬件加速引起的,到官方文檔硬件加速上可以看到確實(shí)是因?yàn)橛布铀賹?dǎo)致的。
(PS:其實(shí)真實(shí)情況是我先自定義View后才知道ShapeDrawable沒(méi)有虛線(xiàn)效果可能也是因?yàn)橛布铀僖鸬摹#?/p>

重新修改onDraw方法。重新運(yùn)行之后,虛線(xiàn)就正常出現(xiàn)了。
@Override
protected void onDraw(Canvas canvas) {
int centerY = getHeight() / 2;
setLayerType(LAYER_TYPE_SOFTWARE, null);
canvas.drawLine(0, centerY, getWidth(), centerY, mPaint);
}
使用shapeDrawable和drawLine方法都能實(shí)現(xiàn)虛線(xiàn)的效果,但它們的缺點(diǎn)也很明顯,就是不支持硬件加速。上面的代碼因?yàn)楹?jiǎn)單,所以不會(huì)引起卡頓的問(wèn)題,但是如果你的自定義View很復(fù)雜,比如說(shuō)用戶(hù)在畫(huà)矩形框的時(shí)候,如果用戶(hù)拉出來(lái)的圖形是一個(gè)正方形,那我們就畫(huà)一條對(duì)角虛線(xiàn)來(lái)提醒用戶(hù)。這種情況下不支持硬件加速的弊端可能就會(huì)顯現(xiàn)出來(lái)了。好在我們還有其它方法來(lái)實(shí)現(xiàn)虛線(xiàn)的繪制。setPathEffect方法只是不支持直線(xiàn)而已,那我們就找找有沒(méi)有另外的方式來(lái)畫(huà)直線(xiàn),drawPath就可以繪制各種路徑,當(dāng)然也可以幫我們繪制直線(xiàn),雖然說(shuō)有點(diǎn)大材小用了。
重新來(lái)看看代碼怎么寫(xiě)。
public class DashLineView extends View {
private Paint mPaint;
private Path mPath;
public DashLineView(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(getResources().getColor(R.color.dash_line));
// 需要加上這句,否則畫(huà)不出東西
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(3);
mPaint.setPathEffect(new DashPathEffect(new float[] {15, 5}, 0));
mPath = new Path();
}
@Override
protected void onDraw(Canvas canvas) {
int centerY = getHeight() / 2;
mPath.reset();
mPath.moveTo(0, centerY);
mPath.lineTo(getWidth(), centerY);
canvas.drawPath(mPath, mPaint);
}
}
把setLayerType(LAYER_TYPE_SOFTWARE, null);這一句代碼去掉之后也還能畫(huà)出虛線(xiàn),證明是支持硬件加速的。
至此,我們已經(jīng)完美的實(shí)現(xiàn)了虛線(xiàn)的繪制,但本篇文章還沒(méi)完呢!我們繪制出來(lái)的虛線(xiàn)是一個(gè)實(shí)心矩形,那如果我們要求這條是由實(shí)心圓形組成的呢?又或者是其它圖形組成的呢?這時(shí)候我們就可以用PathDashPathEffect來(lái)實(shí)現(xiàn)了,看清楚點(diǎn),是PathDashPathEffect而不是DashPathEffect!PathDashPathEffect允許我們添加一個(gè)路徑來(lái)定義虛線(xiàn)的樣式。我們把setPathEffect()改一下,看看效果。
Path path = new Path(); path.addCircle(0, 0, 3, Path.Direction.CW); mPaint.setPathEffect(new PathDashPathEffect(path, 15, 0, PathDashPathEffect.Style.ROTATE));
其實(shí)圓形組成的虛線(xiàn)也挺好看的嘛。繪制虛線(xiàn)至此就講完了,但如果要我們給這條虛線(xiàn)加一些效果呢?比如說(shuō)虛線(xiàn)由中間是完全不透明的,而兩邊則會(huì)慢慢變透明。是不是想打人了,又不是不能用,那么多要求干嘛!沒(méi)辦法,硬著頭皮來(lái)吧。Paint還有另外一個(gè)方法,setShader(Shader shader) ,Shader我們可以理解為著色器,它有一個(gè)子類(lèi)是LinearGradient,利用它可以幫助我們實(shí)現(xiàn)線(xiàn)性變色??纯创a先。
為了讓效果突出一點(diǎn),把虛線(xiàn)的顏色調(diào)為黑色。
// 其它方法還是不變
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 前四個(gè)參數(shù)沒(méi)啥好講的,就是起點(diǎn)和終點(diǎn)而已。
// color數(shù)組的意思是從透明 -> 黑 -> 黑 -> 透明。
// float數(shù)組與color數(shù)組對(duì)應(yīng):
// 0 -> 0.3 (透明 -> 黑)
// 0.3 - 0.7 (黑 -> 黑,即不變色)
// 0.7 -> 1 (黑 -> 透明)
mPaint.setShader(new LinearGradient(0, 0, getWidth(), 0,
new int[] {Color.TRANSPARENT, Color.BLACK, Color.BLACK, Color.TRANSPARENT},
new float[] {0, 0.3f, 0.7f, 1f}, Shader.TileMode.CLAMP));
}

效果還是可以的嘛,當(dāng)然不是應(yīng)用在圖片上的這種場(chǎng)景。好了,這次是真的講完了。雖然畫(huà)虛線(xiàn)不難,但還是有一些值得注意的地方的(硬件加速),然后再細(xì)細(xì)玩一玩,雖然簡(jiǎn)單,還是可以玩出一些花樣來(lái)的。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)各位Android開(kāi)發(fā)者們能帶來(lái)一定的幫助,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- Android 自定義View的使用介紹
- Android自定義View實(shí)現(xiàn)搜索框(SearchView)功能
- Android開(kāi)發(fā)使用自定義View將圓角矩形繪制在Canvas上的方法
- Android自定義View設(shè)定到FrameLayout布局中實(shí)現(xiàn)多組件顯示的方法 分享
- Android自定義View實(shí)現(xiàn)廣告信息上下滾動(dòng)效果
- Android自定義View之自定義評(píng)價(jià)打分控件RatingBar實(shí)現(xiàn)自定義星星大小和間距
- Android自定義View實(shí)現(xiàn)loading動(dòng)畫(huà)加載效果
- Android自定義View實(shí)現(xiàn)漸變色進(jìn)度條
- Android 使用Kotlin自定義View的方法教程
- Android?自定義view中根據(jù)狀態(tài)修改drawable圖片
相關(guān)文章
Android實(shí)現(xiàn)對(duì)圖片放大、平移和旋轉(zhuǎn)的功能
現(xiàn)在很多App在查看一張圖片的原圖時(shí),都會(huì)支持圖片的手勢(shì)縮放,手勢(shì)平移以及圖片旋轉(zhuǎn)的操作。那么今天小編就來(lái)教大家去簡(jiǎn)單的實(shí)現(xiàn)圖片的放大、平移、旋轉(zhuǎn)的操作,有需要的可以參考借鑒。2016-08-08
Android遞歸方式刪除某文件夾下的所有文件(.mp3文件等等)
以刪除為例,當(dāng)然,對(duì)于遍歷某文件夾下的所有文件均可用這個(gè)方法。如搜索.mp3文件等,具體實(shí)現(xiàn)如下,感興趣的朋友可以參考下哈2013-06-06
基于Flutter實(shí)現(xiàn)愛(ài)心三連動(dòng)畫(huà)效果
Animation是一個(gè)抽象類(lèi),它并不參與屏幕的繪制,而是在設(shè)定的時(shí)間范圍內(nèi)對(duì)一段區(qū)間值進(jìn)行插值。本文將利用Animation制作一個(gè)愛(ài)心三連動(dòng)畫(huà)效果,感興趣的可以學(xué)習(xí)一下2022-03-03

