編輯:關於Android編程
最近項目中需要完成以下這個需求
UI給我了五張圖片,我感覺太浪費了,自定義view完全可以做而且適配起來更加的方便
項目效果
擴展
在實現這個過程之前,我們需要了解path的一系列的原理(如果你了解path的用法直接跳過)
這三個方法都如字面意思一樣,非常簡單,這裡就簡單是敘述一下,不再過多講解。
setPath 是 PathMeasure 與 Path 關聯的重要方法,效果和 構造函數 中兩個參數的作用是一樣的。
isClZ喎?/kf/ware/vc/" target="_blank" class="keylink">vc2VkINPD09rF0LbPIFBhdGggyse38bHVus+jrLWrysfI57n7xOPU2rnYwaogUGF0aCC1xMqxuvLJ6NbDIGZvcmNlQ2xvc2VkIM6qIHRydWUgtcS7sKOs1eK49re9t6i1xLe1u9jWtdTy0ru2qM6qdHJ1ZaGjPGJyIC8+DQpnZXRMZW5ndGgg08PT2rvxyKEgUGF0aCC1xNfcs6S2yDwvcD4NCmdldFNlZ21lbnQNCjxwcmUgY2xhc3M9"brush:java;">
//返回值(boolean) 判斷截取是否成功 true 表示截取成功,結果存入dst中,false 截取失敗,不會改變dst中內容
//startD 開始截取位置距離 Path 起點的長度 取值范圍: 0 <= startD < stopD <= Path總長度
//stopD 結束截取位置距離 Path 起點的長度 取值范圍: 0 <= startD < stopD <= Path總長度
//dst 截取的 Path 將會添加到 dst 中 注意: 是添加,而不是替換
//startWithMoveTo 起始點是否使用 moveTo 用於保證截取的 Path 第一個點位置不變
//如果 startD、stopD 的數值不在取值范圍 [0, getLength] 內,或者 startD == stopD 則返回值為 false,不會改變 dst 內容。
//如果在安卓4.4或者之前的版本,在默認開啟硬件加速的情況下,更改 dst 的內容後可能繪制會出現問題,請關閉硬件加速或者給 dst 添加一個單個操作,例如: dst.rLineTo(0, 0)
boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)
getPosTan
/*這個方法是用於得到路徑上某一長度的位置以及該位置的正切值: 參數 作用 備注 返回值(boolean) 判斷獲取是否成功 true表示成功,數據會存入 pos 和 tan 中, false 表示失敗,pos 和 tan 不會改變 distance 距離 Path 起點的長度 取值范圍: 0 <= distance <= getLength pos 該點的坐標值 坐標值: (x==[0], y==[1]) tan 該點的正切值 正切值: (x==[0], y==[1]) */ boolean getPosTan (float distance, float[] pos, float[] tan)getMatrix
這個方法是用於得到路徑上某一長度的位置以及該位置的正切值的矩陣:
/* 返回值(boolean) 判斷獲取是否成功 true表示成功,數據會存入matrix中,false 失敗,matrix內容不會改變 distance 距離 Path 起點的長度 取值范圍: 0 <= distance <= getLength matrix 根據 falgs 封裝好的matrix 會根據 flags 的設置而存入不同的內容 flags 規定哪些內容會存入到matrix中 可選擇 POSITION_MATRIX_FLAG(位置) ANGENT_MATRIX_FLAG(正切) */ boolean getMatrix (float distance, Matrix matrix, int flags)
可以明顯的看出這個view的5個園的圓心都在一個大的圓上
Path pathCircle = new Path(); pathCircle.addCircle(with / 2, hight / 2, hight / 2 - pading - radius, Path.Direction.CW);
float[] position = new float[2]; for (int index = 0; index < 5; index++) { if (currentPosition == index) { paint.setColor(Color.RED); } else { paint.setColor(Color.BLUE); } float allLength = pathMeasure.getLength(); distance = (allLength / 5) * (index + 1); pathMeasure.getPosTan(distance, position, tan); canvas.drawCircle(position[0], position[1], radius, paint); }
其實在path添加大圓的時候我們只能控制path路徑的軌跡方向,並不能指定其開始位置,而且現在我們寫死了很多變量:顏色,圓環數等*
解決辦法:那我們用arc(圓弧)去畫指定其實位置;通過指定要屬性實現動態添加屬性;
Path pathCircle = new Path(); RectF rectF = new RectF(pading + radius, pading + radius, with - pading - radius, hight - pading - radius); pathCircle.arcTo(rectF, -90, 359);
// 寬 private int with; // 高 private int hight; // 間距 private int pading; // 小圓環半徑 private int radius; // 圓環寬度 private int paintWith; // 圓環數 private int pie; // 當前選中圓環 private int currentPosition; // 正常顏色 private int normalColor; // 選中顏色 private int clickColor; // 畫筆 private Paint paint; public ProgressCircleView(Context context, AttributeSet attrs) { super(context, attrs); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ProgressCircleOldView); pading = a.getDimensionPixelOffset(R.styleable.ProgressCircleOldView_pading, 0); radius = a.getDimensionPixelOffset(R.styleable.ProgressCircleOldView_radius, 10); paintWith = a.getDimensionPixelOffset(R.styleable.ProgressCircleOldView_paintWith, 4); pie = a.getInt(R.styleable.ProgressCircleOldView_pie, 5); currentPosition = a.getInt(R.styleable.ProgressCircleOldView_currentPosition, 0); normalColor = a.getColor(R.styleable.ProgressCircleOldView_normalColor, Color.BLUE); clickColor = a.getColor(R.styleable.ProgressCircleOldView_clickColor, Color.RED); a.recycle(); initPaint(); }
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); float[] position = new float[2]; float[] tan = new float[2]; float distance; Path pathCircle = new Path(); RectF rectF = new RectF(pading + radius, pading + radius, with - pading - radius, hight - pading - radius); pathCircle.arcTo(rectF, -90, 359); PathMeasure pathMeasure = new PathMeasure(pathCircle, false); for (int index = 0; index < pie; index++) { if (currentPosition == index) { paint.setColor(clickColor); } else { paint.setColor(normalColor); } float allLength = pathMeasure.getLength(); distance = (allLength / pie) * (index); pathMeasure.getPosTan(distance, position, tan); canvas.drawCircle(position[0], position[1], radius, paint); } }
到這裡我們基本已經完成了這個需求了但是估計大家還是沒有講PathMeasure沒有很好的理解,所以就有了下面的擴展
上面的效果在很多場景中我們都能用到,不如加載、經度顯示等;其實通過動畫我們也可以實現,但是自定義view也是可以的,而且它的效率更高,
靈活性更加好,功能也可以做的更加強大,主要是你實現起來還很簡單哦!
其實上面的矩形和圓軌跡都是走的同樣的邏輯,不過是path添加了不同的圖形,所以你可以自由發揮哦,所以就拿上面的圓形進度為例子來講解了
Path path = new Path(); path.addCircle(600, 400, 100, Path.Direction.CCW);
// 按照比例獲取 progress = progress < 1 ? progress + 0.0005 : 0; Matrix matrix = new Matrix(); paint.setColor(Color.YELLOW); measure.getPosTan((int) (measure.getLength() * progress), position, tan);
Path path1 = new Path(); path1.moveTo(position[0] - 20, position[1] + 20); path1.lineTo(position[0], position[1]); path1.lineTo(position[0] + 20, position[1] + 20); // 是否閉合,閉合就是三角形了 path1.close();
Path path2 = new Path(); float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); matrix.setRotate(degrees + 90, position[0], position[1]); path2.addPath(path1, matrix);
// 進度線 measure.getSegment(-1000, (int) (measure.getLength() * progress), path2, true); paint.setColor(Color.BLUE); canvas.drawPath(path2, paint);
/** * 繪制panth上每一個點的位置 * 帶箭頭的進度框 * * @param canvas */ @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void PaintMatr(Canvas canvas) { paint.setStrokeWidth(10); paint.setStyle(Paint.Style.STROKE); Path path = new Path(); path.addCircle(600, 400, 100, Path.Direction.CCW); PathMeasure measure = new PathMeasure(path, false); // 按照比例獲取 progress = progress < 1 ? progress + 0.0005 : 0; Matrix matrix = new Matrix(); paint.setColor(Color.YELLOW); measure.getPosTan((int) (measure.getLength() * progress), position, tan); canvas.drawPath(path, paint); // 箭頭 paint.setColor(Color.RED); Path path1 = new Path(); path1.moveTo(position[0] - 20, position[1] + 20); path1.lineTo(position[0], position[1]); path1.lineTo(position[0] + 20, position[1] + 20); // 是否閉合,閉合就是三角形了 path1.close(); Path path2 = new Path(); float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI); matrix.setRotate(degrees + 90, position[0], position[1]); path2.addPath(path1, matrix); // 進度線 measure.getSegment(-1000, (int) (measure.getLength() * progress), path2, true); paint.setColor(Color.BLUE); canvas.drawPath(path2, paint); invalidate(); }
到這裡你也是path就完事了 no no no其實path還能結合SVG( 是一種矢量圖,內部用的是 xml 格式化存儲方式存儲這操作和數據,你完全可以將 SVG 看作是 Path 的各項操作簡化書寫後的存儲格式)
SVG 是一種矢量圖,內部用的是 xml 格式化存儲方式存儲這操作和數據,你完全可以將 SVG 看作是 Path 的各項操作簡化書寫後的存儲格式
他們結合能創找出很多意想不到的東西,有興趣的同學可以自己去研究一下
SVG解析成Path的解析庫
github開源庫
項目源碼地址*戳我
帶卡片滑動結合地址*戳我
一、前言自從去年中微信添加搶紅包的功能,微信的電商之旅算是正式開始正式火爆起來。但是作為Android開發者來說,我們在搶紅包的同時意識到了很多問題,就是手動去搶紅包的速
開源地址:https://github.com/SimonVT/android-menudrawer 簡介:menudrawer是跟sliderMenu差不多的一種框架,
web開發概述 靜態web資源:內容是靜態的,不同的人在不同的時間來訪問時都是相同的內容。HTML、CSS、JS動態web資源:內容是由程序生成的,
搜芽的移動開發這幾天進度相對來說非常的快。但是美中不足的就是網絡圖片的加載問題。我有兩套方案: 1)沿用迅雷動漫的圖片加載。迅雷動漫也是用的一個開源的庫。但是不知道是我使