作者:android超級兵原文:https://juejin.cn/post/6995153251221897252
在滑動的過程中,漸變文字會隨着 ViewPager 的滑動而變化
1. 繪製文字與BaseLine思考先來看看最初版代碼:
publicclassGradualChangeTvextendsAppCompatTextView{publicPaintmPaint=newPaint();publicfinalStringtext="android超級兵";publicGradualChangeTv(Contextcontext){this(context,null);}publicGradualChangeTv(Contextcontext,AttributeSetattrs){this(context,attrs,0);}publicGradualChangeTv(Contextcontext,AttributeSetattrs,intdefStyleAttr){super(context,attrs,defStyleAttr);mPaint.setColor(Color.RED);//抗鋸齒mPaint.setAntiAlias(true);}@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);/**繪製文字*參數一:繪製文字*參數二:x軸開始位置*參數三:y軸開始位置*參數四:畫筆*/canvas.drawText(text,0,0,mPaint);}}就是簡單的繪製了一行字
來看看效果順便也看看布局:

先來回顧一下屏幕的坐標系

再來看看文字的坐標系

(圖片來自於網絡)
再來思考一下文字是為什麼不顯示的:

虛線為 BaseLine
如果此時我把字體放大到100,看一看我能不能看到文字

再一次證明了文字是從BaseLine線開始繪製
2. 文字居中可以用兩條輔助線,水平線與垂直線.然後在來看文字是否居中
代碼 (底部會給出完整代碼,這裡看思路即可)
@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);//獲取當前控件的寬高intviewWidth=getWidth()/2;intviewHeight=getHeight()/2;/**繪製文字*參數一:繪製文字*參數二:x軸開始位置*參數三:y軸開始位置*參數四:畫筆*/canvas.drawText(text,viewWidth,viewHeight,mPaint);//繪製居中線drawCenterLine(canvas,viewWidth,viewHeight);}privatevoiddrawCenterLine(Canvascanvas,intviewWidth,intviewHeight){//垂直線canvas.drawLine(viewWidth,0,viewWidth,getHeight(),mPaint);//水平線canvas.drawLine(0,viewHeight,getWidth(),viewHeight,mPaint);}效果圖:

可以看出,還是上面說的那個問題,文字繪製是基於 baseLine 線來繪製的.
文字居中思路:
這裡的descent和ascent可以參考上面文字繪製圖
相關代碼:
@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);//文字寬度floattextWidth=mPaint.measureText(text);//文字高度floattextHeight=mPaint.descent()+mPaint.ascent();//獲取當前控件的寬高intviewWidth=getWidth()/2;intviewHeight=getHeight()/2;canvas.drawText(text,viewWidth-textWidth/2,viewHeight-textHeight/2,mPaint);//繪製居中線drawCenterLine(canvas,viewWidth,viewHeight);}效果圖:

裁剪(clipRect)參數分析:
canvas.save() 和 canvas.restore() 可以理解為將當前繪製的東西當作一個新的圖層

來看看效果圖:
代碼注釋很清晰;就不過多解釋了
4. 從左到右漸變文字眾所周知,在android中,是不能夠將文字繪製一般的
思路分析:

在來看看現在代碼是什麼樣子的:
//用來記錄當前進度【0-1】floatprogress=0.3f;@OverrideprotectedvoidonDraw(Canvascanvas){super.onDraw(canvas);//文字寬度floattextWidth=mPaint.measureText(text);//文字高度floattextHeight=mPaint.descent()+mPaint.ascent();//獲取當前控件的寬高的一半intviewWidth=getWidth()/2;intviewHeight=getHeight()/2;//繪製底層drawBottom(canvas,viewWidth,viewHeight,textWidth,textHeight);//繪製上層【顏色漸變的】drawUp(canvas,viewWidth,viewHeight,textWidth,textHeight);//繪製居中線drawCenterLine(canvas,viewWidth,viewHeight);}//繪製上層【漸變的】privatevoiddrawUp(Canvascanvas,intviewWidth,intviewHeight,floattextWidth,floattextHeight){mPaint.setColor(Color.BLACK);canvas.save();//繪製文字X軸的位置floatleft=viewWidth-textWidth/2;//繪製文字Y軸的位置floatright=viewHeight-textHeight/2;//裁剪canvas.clipRect((int)left,0,(int)left+textWidth*progress,getHeight());/**繪製文字*參數一:繪製文字*參數二:x軸開始位置*參數三:y軸開始位置*參數四:畫筆*/canvas.drawText(text,left,right,mPaint);canvas.restore();}//繪製下層不動的privatevoiddrawBottom(Canvascanvas,intviewWidth,intviewHeight,floattextWidth,floattextHeight){mPaint.setColor(Color.RED);//文字顏色canvas.save();//文字開始位置floatleft=viewWidth-textWidth/2;/**繪製文字*參數一:繪製文字*參數二:x軸開始位置*參數三:y軸開始位置*參數四:畫筆*/canvas.drawText(text,left,viewHeight-textHeight/2,mPaint);canvas.restore();}這裡重點解釋一下上層[需要裁剪的]參數:
//裁剪canvas.clipRect((int)left,0,(int)left+textWidth*progress,getHeight());
通過手勢滑動來控制:
這段代碼並沒有實質性作用,只是來看看效果:
@SuppressLint("ClickableViewAccessibility")@OverridepublicbooleanonTouchEvent(MotionEventevent){if(event.getAction()==MotionEvent.ACTION_MOVE){progress=event.getX()/getWidth();invalidate();}returntrue;}效果圖:
思路和從左到右繪製是一樣的直接看關鍵代碼:
privatevoiddrawRightToLeft(Canvascanvas,intviewWidth,intviewHeight,floattextWidth,floattextHeight){mPaint.setColor(Color.GREEN);/**這裡 left和right能夠在此抽取出來,不過這樣寫很易懂,有需求自己弄吧!!!*/canvas.save();//繪製文字X軸的位置【文字開始的位置】floattextX=viewWidth-textWidth/2;//繪製文字Y軸的位置floattextY=viewHeight-textHeight/2;//文字結束的位置floatend=viewWidth+mPaint.measureText(text)/2;canvas.clipRect(end,0,textX+textWidth*(1-progress),getHeight());canvas.drawText(text,textX,textY,mPaint);canvas.restore();}@SuppressLint("ClickableViewAccessibility")@OverridepublicbooleanonTouchEvent(MotionEventevent){if(event.getAction()==MotionEvent.ACTION_MOVE){if(type==GradualChangeTextView.GRADUAL_CHANGE_RIGHT){//從右到左滑動progress=1-event.getX()/getWidth();}elseif(type==GradualChangeTextView.GRADUAL_CHANGE_LEFT){//從左到右滑動progress=event.getX()/getWidth();}invalidate();}returntrue;}效果圖:
最後在添加兩個按鈕來完全測試一下代碼有沒有問題:
完完全全沒有問題!
6. 最終實現效果(漸變滑動)先來看看布局:
布局簡單的很,就是文字和ViewPager
大致看看ViewPager代碼:
//text1..text4是控件idvaltextList=listOf(text1,text2,text3,text4)vallist=listOf(HomeFragment(),MyFragment(),TestFragment(),SettingFragment())valviewPagerAdapter=ViewPagerAdapter(supportFragmentManager,list)viewPager.adapter=viewPagerAdapter//默認選擇第一頁viewPager.currentItem=1//默認選中textList[viewPager.currentItem].percent=1f這段代碼,只要學過就懂,不細說了!
重中之重來了:
viewPager.addOnPageChangeListener(object:ViewPager.OnPageChangeListener{overridefunonPageScrolled(position:Int,positionOffset:Float,positionOffsetPixels:Int,){if(positionOffset>0){valleft=textList[position]valright=textList[position+1]//從右到左滑動left.setSlidingPosition(GradualChangeTextView.GRADUAL_CHANGE_RIGHT)//從左到右滑動right.setSlidingPosition(GradualChangeTextView.GRADUAL_CHANGE_LEFT)//當前頁面取反[從右到左]left.percent=1-positionOffset//下一個頁面正常[從左到右]right.percent=positionOffset}}overridefunonPageSelected(position:Int){}overridefunonPageScrollStateChanged(state:Int){//當ViewPage結束的時候,重新設置一下狀態[不設置的話滑動太快,會導致'殘影']textList.forEach{if(it.tag==textList[viewPager.currentItem].tag){it.percent=1f}else{it.percent=0f}}}})來看看效果:
先來看看沒有優化的效果:
可以看到,在繪製的過程中,因為是兩層,那麼就繪製了2次,
優化思路: 當黑色[上層]從左到右滑動的時候,紅色[下層]跟隨着從左到右裁剪
來看看下層繪製的代碼:
//繪製下層不動的privatevoiddrawBottom(Canvascanvas,intviewWidth,intviewHeight,floattextWidth,floattextHeight){mPaint.setColor(Color.RED);canvas.save();//繪製文字X軸的位置[文字開始的位置]floattextX=viewWidth-textWidth/2;//繪製文字Y軸的位置floattextY=viewHeight-textHeight/2;//跟隨者上層裁剪canvas.clipRect((int)textX+textWidth*progress,0,textWidth+viewWidth,getHeight());/**繪製文字*參數一:繪製文字*參數二:x軸開始位置*參數三:y軸開始位置*參數四:畫筆*/canvas.drawText(text,textX,textY,mPaint);canvas.restore();}效果圖:
完整代碼:https://gitee.com/lanyangyangzzz/android_ui
-- End --
推薦閱讀
使用 Compose 搞一個貪吃蛇遊戲
天道酬勤,進字節了
妙啊!Jetpack Compose 實現波浪加載
加好友進交流群,技術乾貨聊不停

