編輯:關於Android編程
手機直播一般都會通過移動屏幕來調節音量的大小,本篇只實現了圖例,並不能改變音量。
先看效果:
需要的素材:小喇叭圖片,點擊這裡獲取
如果你將這哥們的十幾篇帖子都看完了的話,這個View實際上是非常簡單的
用動態圖來介紹:
這裡用文字翻譯下:
將小喇叭畫到中心位置 圍繞著喇叭畫一個圓圈,淺色的 畫一個圓弧,深色的 根據觸摸的位置來改變圓弧的大小分解之後,發現並沒有什麼難度(可能本身就沒有什麼難度),下面來看每一步的操作,最後會將整個View的代碼貼出來
相關代碼片(不要復制,只是看的)
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //坐標移動到中心 canvas.translate(mViewWidth / 2, mViewHeight / 2); //拿到小喇叭圖片 Bitmap voice = BitmapFactory.decodeResource(getResources(), R.mipmap.voice); //獲取圖片的寬高 int bWidth = voice.getWidth(); int bHeight = voice.getHeight(); //移動坐標到中心位置 canvas.drawBitmap(voice, -bWidth / 2, -bHeight / 2, outerCirclePaint); }
分析
核心:將圖片放到中心位置:
將圖片向上移動高度的一半,向左移動寬度的一半,就可以移動到中心如圖所示:
相關代碼片(不要復制,只是看的)
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); initPaint(); //坐標移動到中心 canvas.translate(mViewWidth / 2, mViewHeight / 2); //底層圓圈 if (cirPath == null) cirPath = new Path(); if (rectF == null) { //半徑選取為圖片寬度一半的1.3倍,可以調節 r = (int) (bWidth / 2 * 1.3f); rectF = new RectF(-r, -r, r, r); } cirPath .addArc(rectF, 0, 360); //畫底層淺色的圓圈 canvas.drawPath(cirPath, outerCirclePaint); }
分析:
這步沒有什麼難度,只是繪制一個圓圈
相關代碼片(不要復制,只是看的)
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //坐標移動到中心 canvas.translate(mViewWidth / 2, mViewHeight / 2); //音量大小 if (innerCirclePath == null) innerCirclePath = new Path(); //繪制外層深色的音量弧形 drawVoicePath(innerCirclePath); //畫音量強度 canvas.drawPath(voicePath, voicePaint); } private void drawVoicePath(Path path) { if (voiceRectf == null) { //與底層圓圈保持一致 voiceRectf = new RectF(-r, -r, r, r); voicePath = new Path(); } voicePath.reset(); //道聽途說使用359.9可以測量的更准 path.addArc(voiceRectf, -90, 359.9f); //通過PathMeasure來繪制部分的圓圈,表現出來就是繪制了弧形 PathMeasure measure = new PathMeasure(path, false); //獲取圓的總長度 float length = measure.getLength(); //根據音量大小來繪制部分的弧形 measure.getSegment(0, voiceNumber * length, voicePath, true); }
分析
圓弧從上往下繪制,所以:addArc(RectF oval, float startAngle, float sweepAngle)中的第二個參數startAngle為-90;
圓各部分的對應的Angle:
另外,通過可變參數voiceNumber來控制圓弧的大小,用於之後的修改。
相關代碼片(不要復制,只是看的)
@Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //記錄起始位置的坐標和音量大小 startY = (int) event.getY(); oldVoiceNumber = voiceNumber; break; case MotionEvent.ACTION_MOVE: //記錄當前位置的坐標 moveY = (int) event.getY(); //與起始位置進行比較來確定音量的大小 changedVoiceNumber(); break; case MotionEvent.ACTION_UP: //手指離開屏幕,重置數據 resetData(); break; } //繪制圖形 invalidate(); //這裡需要改View處理事件,所以放回true; return true; }
最後一步的代碼比較多,這裡沒有貼完,詳細代碼在最後都會貼出來。
分析:
既然要通過滑動來改變深色圓弧的大小,那麼可定是在onTouchEvent()中來進行相關的操作。
1. 通過滑動,圖形改變,需要重新繪制,所以調用invalidate()
2. 滑動事件需要改View來處理,所以返回值為true
3. 核心部分是通過ACTION_DOWN,ACTION_MOVE,ACTION_UP的相關操作來實現的
核心部分的大致流程圖是這樣的,如果與後面的代碼有出入,以代碼為准:
這裡可以復制粘貼了
/** * Created by Kevin on 2016/8/31. */ public class VoiceView extends View { //控件的寬高 private int mViewWidth; private int mViewHeight; //小喇叭 private Bitmap voice; //小喇叭的寬度 private int bWidth; //小喇叭的高度 private int bHeight; //表示音量大小 private float voiceNumber = 0.5f; //調節之前音量的大小 private float oldVoiceNumber; //開始時的坐標 private int startY; //移動後的坐標 private int moveY; //圓圈的半徑 private int r; //音量從0-->1所需要移動的距離 private int voiceChangedY; //音量的畫筆 private Paint voicePaint; //頂層圓圈的畫筆 private Paint outerCirclePaint; //音量的Path private Path voicePath; //頂層圓圈的的Path private Path cirPath; //音量圓圈的Path private Path innerCirclePath; //頂層的RectF private RectF rectF; //音量的RectF private RectF voiceRectf; public VoiceView(Context context) { super(context); initBitmap(); } public VoiceView(Context context, AttributeSet attrs) { super(context, attrs); initBitmap(); } public VoiceView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initBitmap(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); initPaint(); //坐標移動到中心 canvas.translate(mViewWidth / 2, mViewHeight / 2); //外層圓圈 if (cirPath == null) cirPath = new Path(); //音量大小 if (innerCirclePath == null) innerCirclePath = new Path(); //繪制底層淺色圓圈 drawCirclePath(cirPath); //繪制外層深色的音量弧形 drawVoicePath(innerCirclePath); //移動坐標到中心位置 canvas.drawBitmap(voice, -bWidth / 2, -bHeight / 2, outerCirclePaint); //畫頂層淺色的圓圈 canvas.drawPath(cirPath, outerCirclePaint); //畫音量強度 canvas.drawPath(voicePath, voicePaint); } /** * 通過觸摸來改變音量的大小 * * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //記錄起始位置的坐標和音量大小 startY = (int) event.getY(); oldVoiceNumber = voiceNumber; break; case MotionEvent.ACTION_MOVE: //記錄當前位置的坐標 moveY = (int) event.getY(); //與起始位置進行比較來確定音量的大小 changedVoiceNumber(); break; case MotionEvent.ACTION_UP: //手指離開屏幕,重置數據 resetData(); break; } //繪制圖形 invalidate(); //這裡需要改View處理事件,所以放回true; return true; } /** * 根據起始位置和當前位置來確定音量的大小 * * 一般情況下:音量大小的改變量 = (當前位置 - 起始位置) / 一個固定的長度 * 注釋:這裡選取的“一個固定的長度”為高度的一半 * * 極端情況:如果音量調節到1或者0,仍然以最開始的位置作為起始位置,感覺會很奇怪(可以將resetData()中的的代碼屏蔽來感覺一下); * 處理:當為0或者1時,重置數據 */ private void changedVoiceNumber() { int changeY = moveY - startY; float changedVoice = changeY / (voiceChangedY * 1.0f); if (changedVoice > 0) { //音量增加 if (voiceNumber >= 1) { voiceNumber = 1; resetData(); return; } else { float afterChange = oldVoiceNumber + changedVoice; if (afterChange >= 1) { voiceNumber = 1; resetData(); } else { voiceNumber = afterChange; } } } else if (changedVoice < 0) { //音量減少 if (voiceNumber <= 0) { voiceNumber = 0; resetData(); return; } else { float afterChange = oldVoiceNumber + changedVoice; if (afterChange <= 0) { voiceNumber = 0; resetData(); } else { voiceNumber = afterChange; } } } else if (changedVoice == 0) { //音量不變 } // String print = String.format("startY-->%d,moveY-->%d,voiceNumber-->%f,changeVoice-->%f", startY, moveY, voiceNumber, changedVoice); // Log.e("ddd", print); } /** * 當音量達到0或者1時,重置數據 */ private void resetData() { startY = moveY; oldVoiceNumber = voiceNumber; } /** * 繪制底層層圓圈 * * @param path */ private void drawCirclePath(Path path) { if (rectF == null) { //半徑選取為圖片寬度一半的1.3倍,可以調節 r = (int) (bWidth / 2 * 1.3f); rectF = new RectF(-r, -r, r, r); } path.addArc(rectF, 0, 360); } /** * 繪制音量 * * @param path */ private void drawVoicePath(Path path) { if (voiceRectf == null) { //與底層圓圈保持一致 voiceRectf = new RectF(-r, -r, r, r); voicePath = new Path(); } voicePath.reset(); //道聽途說使用359.9可以測量的更准 path.addArc(voiceRectf, -90, 359.9f); //通過PathMeasure來繪制部分的圓圈,表現出來就是繪制了弧形 PathMeasure measure = new PathMeasure(path, false); //獲取圓的總長度 float length = measure.getLength(); //根據音量大小來繪制部分的弧形 measure.getSegment(0, voiceNumber * length, voicePath, true); } /** * 小喇叭 */ private void initBitmap() { voice = BitmapFactory.decodeResource(getResources(), R.mipmap.voice); bWidth = voice.getWidth(); bHeight = voice.getHeight(); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mViewWidth = w; mViewHeight = h; //音量從0-->1所需要移動的距離 voiceChangedY = h / 2; } /** * 初始化畫筆 */ private void initPaint() { int stroke = 20; if (outerCirclePaint == null) { outerCirclePaint = new Paint(); outerCirclePaint.setStrokeWidth(stroke); outerCirclePaint.setStyle(Paint.Style.STROKE); outerCirclePaint.setColor(0x8089cff0); outerCirclePaint.setAntiAlias(true); } if (voicePaint == null) { voicePaint = new Paint(); voicePaint.setStrokeWidth(stroke); voicePaint.setStyle(Paint.Style.STROKE); voicePaint.setColor(0xff1d8ffe); voicePaint.setAntiAlias(true); } } }
該View的繪制難度並不大,涉及了不少基礎,當做練習是一個很好的素材。
關於Ditscc分布式編譯環境的搭建,網上也有不少文章,但是基本上都過時了。所以看了很多文章,走了不少彎路,最後總算梳理清楚了一條正確的環境搭建的步驟,而且可以實現zer
package com.example.jreduch08.DataBaseHelpp;import android.content.Contex
前言本文的中文注釋代碼demo更新在我的github上。SDWebImage是一個十分有名的Objective-C第三方開源框架,作用是: Asynchronous im
Android提供alert、prompt、pick-list,單選、多選,progress、time-picker和date-picker對話框,並提供自定義的dial