自定義View實戰
PS:上一篇從0開始學自定義View有博友給我留言說要看實戰,今天我特意寫了幾個例子,供大家參考,所畫的圖案加上動畫看著確實讓人舒服,喜歡的博友可以直接拿到自己的專案中去使用,由於我這個寫的是demo,程式碼格式寫的有些亂,所以,要自己封裝一下才可以使用,當然你如果真的不想封裝,可以直接使用,也可以給我留言,我封裝好放在github上供大家參考,也會做成依賴讓大家直接新增即可
先上圖再分析
可以看出圖中有三種樣式
- 第一種是普通的一個label,使用場景:商品過期,促銷等展示。
- 第二種是圓形進度條, 使用場景:下載檔案進度,載入視訊進度,耗電量進度.....
- 第三種是條形進度條, 使用場景:滑動調值,手機音效大小...
上面的三種,均是demo,考慮使用場景並不完善,比如說第三種條形進度條還可以加上刻度,滑動到兩邊需要判斷越界等。那就先拿第三個來吧
條形進度條-可拖動
分析:我們想要做一個類似的控制元件,需要考慮的問題不只是眼睛看的到的,看不到的就好比我只能點選小紅球才可以滑動,我點選其他區域是不能有任何操作的,這個時候就要判斷手指down的時候是否落在了小球上。
- 線條 : 漸變顏色,線帽格式,長度,寬度設定,父佈局寬高格式設定格式設定,子view寬高格式設定
- 球 :顏色,起始位置和終止位置要在線上,尺寸
在做之前我們先一個一個知識點解析,首先是線的漸變顏色,單獨拿出
/** * 設定進度圓環顏色(支援漸變色) * * @param colorArray 漸變色集合 */ private int[] mColorArray; // 圓環漸變色 public void setProgColor(@ColorRes int[] colorArray) { if (colorArray == null || colorArray.length < 2) return; mColorArray = new int[colorArray.length]; for (int index = 0; index < colorArray.length; index++) mColorArray[index] = ContextCompat.getColor(getContext(), colorArray[index]); paint.setShader(new LinearGradient(0, 0, getMeasuredWidth(),0 , mColorArray, new float[]{0,.3F,.6F,.9F}, Shader.TileMode.MIRROR)); invalidate(); }
我們可以看到我封裝成了一個方法,通過paint.setShader進行著色,方法傳入的是LinearGradient物件,我們看原始碼,解釋引數
/** * Create a shader that draws a linear gradient along a line. * * @param x0 The x-coordinate for the start of the gradient line * @param y0 The y-coordinate for the start of the gradient line * @param x1 The x-coordinate for the end of the gradient line * @param y1 The y-coordinate for the end of the gradient line * @param colors The colors to be distributed along the gradient line * @param positions May be null. The relative positions [0..1] of * each corresponding color in the colors array. If this is null, * the the colors are distributed evenly along the gradient line. * @param tile The Shader tiling mode */ public LinearGradient(float x0, float y0, float x1, float y1, @NonNull @ColorInt int colors[], @Nullable float positions[], @NonNull TileMode tile) { if (colors.length < 2) { throw new IllegalArgumentException("needs >= 2 number of colors"); } if (positions != null && colors.length != positions.length) { throw new IllegalArgumentException("color and position arrays must be of equal length"); } mType = TYPE_COLORS_AND_POSITIONS; mX0 = x0; mY0 = y0; mX1 = x1; mY1 = y1; mColors = colors.clone(); mPositions = positions != null ? positions.clone() : null; mTileMode = tile; }
引數
- x0,y0著色的起始位置
- x1,y1終止位置
- colors區域內著色的顏色集
- positions區域內部劃分模組,逐一著色,如:區域1-100劃分為4塊,第一塊1-25紅色,26-50藍色..。系統會預設在兩種顏色不一樣的情況下進行顏色過度渲染,達到漸變的效果,所以我們不用擔心出現紅藍劃分明顯的情況。
- TileMode 模式選擇
- CLAMP:當圖片小於繪製尺寸時要進行邊界拉伸來填充
- REPEAT:當圖片小於繪製尺寸時重複平鋪
- MIRROR:當圖片小於繪製尺寸時映象平鋪
好了,下面我們就先畫線和紅點,如果有看不懂的博友,可以先看上一篇從0開始學自定義View。
public void init(){ paint = new Paint(); paint.setStrokeWidth(20);//畫筆寬度 paint.setStyle(Paint.Style.FILL);//填充型別 paint.setAntiAlias(true); paint.setStrokeCap(Paint.Cap.ROUND);//線帽,半圓 paintCircle2 = new Paint(); paintCircle2.setColor(Color.RED); paintCircle2.setStrokeWidth(5); paintCircle2.setStyle(Paint.Style.FILL); paintCircle2.setAntiAlias(true); }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); heigth = MeasureSpec.getSize(heightMeasureSpec); Log.e("heigth -- width ",heigth+" -- "+width); circleX=width/2;//初始化紅點座標位置 circleY=20; }
int[] colors={R.color.colorPrimary,R.color.colorAccent,R.color.color_environment_serious,R.color.color_environment_mild}; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); setProgColor(colors); //繪製一條線,線帽為半圓 canvas.drawLine(50,20,width-50,20,paint); canvas.drawCircle(circleX,circleY,15,paintCircle2); }
到這裡,線和點就已經做好了,只是靜態的,下面是如何拖動,就要在onTouchEvent方法中去寫了,程式碼都已經添加了註釋 Math.abs(dhx)<50&&Math.abs(dhy)<50 是證明down的座標點和原始球的座標點相差範圍在50內,如果小球在螢幕200,200的位置,而我們手指down的點在800,800,那麼相差如此巨大,肯定不是我們想要的結果,所以,我們就認為down的座標減去球的座標差值最小(50內)才是我們想要的結果,這個時候我們再設定小球move的座標(讓小球跟隨手指移動)。
/** * 判斷所按壓的座標和紅點座標的關係 * 如果手指按在了紅點的下方,那麼down-紅點y座標的絕對值如果等於或者小於半徑,也就是說目前按壓的就是紅點, * * */ private boolean clickCircle(int downHeigth,int downWidth){ int dhy = downHeigth - circleY; int dhx = downWidth - circleX; //證明按壓的是原點 if(Math.abs(dhx)<50&&Math.abs(dhy)<50){ return true; } return false; } @Override public boolean dispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); } boolean isFlag=false; @Override public boolean onTouchEvent(MotionEvent event) { int rawX = (int) event.getX(); int rawY = (int) event.getY();//獲取到檢視座標,想對於外部viewgroup來說。 Log.e("raw",rawX+" --- "+rawY); Log.e("rawwww",circleX+" --- "+circleY); switch (event.getAction()){ case MotionEvent.ACTION_DOWN: //如果是手指down在了紅點上,move時可重繪 if(clickCircle(rawY,rawX)) { circleX = rawX; isFlag=true; }else{ isFlag=false; } break; case MotionEvent.ACTION_MOVE: if(isFlag){ circleX = rawX; invalidate(); } break; case MotionEvent.ACTION_UP: isFlag=false;//抬起手指,狀態恢復 break; } return true;//自己消費 }
下一篇是圓形進