編輯:關於Android編程
//繪制進度條的X,Y坐標 private int marginXY = 0; //進度條背景,進度條,分隔線的paint private Paint progressbarBackgroundPaint, progressbarPaint, separtatedPaint; //視頻總數 private int videoSum = 6; //畫筆的寬度 private int strokeWidth = 20; //進度條線程實例 private CustomProgressRunnable customProgressRunnable; //進度條繪制標記位 private boolean startDrawProgress = false; //進度條的背景顏色 private int background; //分隔線的顏色 private int separtatedLineBackground; //進度條的顏色 private int progressbarBackground; //分隔線的寬度 private int separtatedLineWidth; //線程池 private ExecutorService executorService;這裡有個變量需要特別注意的就是strokeWidth。這是一個指定畫筆寬度的變量。後面繪制的進度條實質上是 一條線,但是一條線太細了,所以我把這條線的寬度設寬一點,默認設置為20。這樣這條線看起來就像進度條了。 2、構造方法初始化
public CustomProgress(Context context , AttributeSet attrs, int defStyleAttr) { super (context, attrs , defStyleAttr); TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomProgress) ; videoSum = typedArray.getInt(R.styleable.CustomProgress_CP_Video_Sum, 3); background = typedArray.getColor(R.styleable.CustomProgress_CP_Background, Color.parseColor("#F1F1F1" ));//F1F1F1 separtatedLineBackground = typedArray.getColor(R.styleable.CustomProgress_CP_Separated_Line_Background, Color.parseColor("#D6D6D6" )); progressbarBackground = typedArray.getColor(R.styleable.CustomProgress_CP_Progressbar_Background, Color.parseColor("#5BD290" ));//5BD290 separtatedLineWidth = typedArray.getDimensionPixelSize(R.styleable.CustomProgress_CP_Separated_Line_Width, 5); typedArray.recycle() ; // progressbarBackgroundPaint = new Paint() ; progressbarBackgroundPaint .setAntiAlias(true) ; progressbarBackgroundPaint .setStrokeCap(Paint.Cap.ROUND) ; progressbarBackgroundPaint .setStrokeWidth(strokeWidth) ; progressbarBackgroundPaint .setColor(background) ; // progressbarPaint = new Paint() ; progressbarPaint .setAntiAlias(true) ; progressbarPaint .setStrokeWidth(strokeWidth) ; progressbarPaint .setStrokeCap(Paint.Cap.ROUND) ; progressbarPaint .setColor(progressbarBackground) ; separtatedPaint = new Paint() ; separtatedPaint .setAntiAlias(true) ; separtatedPaint .setStrokeWidth(strokeWidth) ; separtatedPaint .setColor(separtatedLineBackground) ; executorService = Executors.newCachedThreadPool(); }在這裡初始化了線程池以及三個筆刷的初始化。 setAntiAlias(true)設置抗鋸齒 setStrokeWidth(strokeWidth)設置筆刷的寬度 setStrokeCap(Paint.Cap.ROUND)設置筆刷末端為半圓形
@Override protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int size = MeasureSpec.getSize(widthMeasureSpec); int finalWidth = size + getPaddingLeft() + getPaddingRight(); int finalHeight = strokeWidth + getPaddingBottom() + getPaddingTop(); setMeasuredDimension(finalWidth , finalHeight); }在初始化完成之後,系統就會調用onMeasure方法。開始測量這個類(View)的大小。 如果在xml文件設置layout_width為match_parent或者wrap_content。int size=MeasureSpec.getSize(widthMeasureSpec)得到的值就是填充父布局的最大值。 如果輸入的是一個准確數值,int size=准確數值。再將size的值用來作為該視圖的寬度。 由進度條的寬度(strokeWidth)作為該視圖的高度。layout_height屬性在XML文件,無論你設置什麼都是無效的! 調用setMeasureDimension(finalWidth,finalHeight)方法設置視圖的最終大小。 4、重寫onDraw方法 說說我個人的繪制思路:
//開始繪制進度條的X,Y坐標 marginXY = strokeWidth / 2; //繪制背景進度 canvas.drawLine(marginXY, marginXY, getWidth() - marginXY, marginXY, progressbarBackgroundPaint);也許很多人不理解marginXY是什麼意思呢?為了方便解釋,請大家再看看下面的圖。
//開始繪制進度條 if (startDrawProgress) { //繪制當前進度 canvas.drawLine(marginXY, marginXY, getProgress(), marginXY, progressbarPaint); }startDrawProgress是一個布爾類型的標記位默認為false。目的是為了避免控件在初始化的時候系統調用onDraw方法 將進度條繪制出來(這個時候我的視頻都沒點擊播放呢,把進度條畫出來干啥?)。 在點擊視頻的時候,通過向外提供的方法。修改startDrawProgress的值為true。 就可以執行canvas.drawLine(marginXY,marginXY, getProgress(),marginXY, progressbarPaint);了 這些參數我已經在上面講的很清楚了,在這裡不再敘述。 最後就是分隔線的繪制
//分隔線的X坐標(注意,這一步必須放在“開始繪制進度條”之後,否則繪制的進度條會把分隔線覆蓋。) int separtatedLineX = 0; for (int i = 0; i < videoSum - 1; i++) { //計算分隔線的X坐標 separtatedLineX += (getWidth() - 2 * marginXY) / videoSum; //繪制分隔線 // canvas.drawLine(marginXY + separtatedLineX - (separtatedLineWidth / 2), marginXY, marginXY + separtatedLineX + (separtatedLineWidth/2), marginXY, separtatedPaint); // canvas.drawLine(separtatedLineX , marginXY, separtatedLineX + separtatedLineWidth , marginXY, separtatedPaint); canvas.drawLine(marginXY + separtatedLineX - (separtatedLineWidth / 2), marginXY, marginXY + separtatedLineX + (separtatedLineWidth / 2), marginXY, separtatedPaint); } }
private Handler handler; private Thread thread; private VideoView videoView; //視頻進度條的長度 private int perVideoLength; //當前正在播放視頻的下標(下標從0開始) private int currentVideoIndex; //線程正在運行標記位 private boolean running = false; //線程正在等待標記位 private boolean waiting = false;其中比較重要的兩個變量就是running和waiting,前者是用來標記線程是否正在運行,後者用來標記線程是否正在等待(暫停)。 2、內部類的構造方法
public CustomProgressRunnable(Handler handler, VideoView videoView, int currentVideoIndex) { this.handler = handler; this.currentVideoIndex = currentVideoIndex; this.perVideoLength = (getWidth() - 2 * marginXY) / videoSum; this.videoView = videoView; thread = new Thread(this); //設置進度條的最大進度值 setMax((currentVideoIndex + 1) * perVideoLength + marginXY); }下圖中綠色線
@Override public void run() { //當前視頻開始的位置 int currentVideoStartPosition = (currentVideoIndex * perVideoLength); while (currentVideoStartPosition < getMax()) { synchronized (this) { if (!running) { break; } if (waiting) { try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } execute(currentVideoStartPosition); } }
public void execute(int currentVideoStartPosition) { if (running && !waiting) { int videoDuration = videoView.getDuration(); if (videoDuration != -1) { //獲取比例 float proportion = (float) videoView.getCurrentPosition() / (float) videoDuration; //計算移動進度 int currentVideoPosition = (int) (proportion * perVideoLength); //計算當前最新進度 int newestVideoPosition = currentVideoPosition + currentVideoStartPosition + marginXY; // CommonTool.showLog("測試:" + newestVideoPosition + " " + videoView.getCurrentPosition() + " " + videoView.getDuration() + " " + proportion + " " + perVideoLength); handler.sendMessage(Message.obtain(handler, newestVideoPosition)); } } }
class TestHandler extends Handler { @Override public void handleMessage(Message msg) { int temp = msg.what; //每設置一次setProgress的值就會調用onDraw方法 setProgress(temp); } }
//開始線程 public void start() { running = true; executorService.execute(thread); } //掛起線程 public void suspend() { if (waiting) { return; } synchronized (this) { this.waiting = true; } } //恢復線程 public void resume() { if (!waiting) { return; } synchronized (this) { this.waiting = false; this.notifyAll(); } } //停止線程 public void stop() { if (!running) { return; } synchronized (this) { running = false; } }在這個內部類裡面我向外面提供了四種方法來操作線程。 這些方法都比較簡單。我就略過啦!
/** * 開始進度條 * * @param videoView * @param currentVideoIndex */ public void startProgress(VideoView videoView, int currentVideoIndex) { if (currentVideoIndex > videoSum) { Log.e("-------------------->", "當前視頻下標不能大於視頻總數"); return; } customProgressBarRunnable = new CustomProgressBarRunnable(new TestHandler(), videoView, currentVideoIndex); //開啟一個線程更新進度條 customProgressBarRunnable.start(); startDrawProgress = true; } public void hangUpThread() { if (customProgressBarRunnable == null) { return; } customProgressBarRunnable.suspend(); Log.e("-------------------->", "掛起線程!"); } public void recoverThread() { if (customProgressBarRunnable == null) { return; } customProgressBarRunnable.resume(); Log.e("-------------------->", "恢復線程!"); } public void stopThread() { if (customProgressBarRunnable == null) { return; } customProgressBarRunnable.stop(); Log.e("-------------------->", "停止線程!"); } /** * 關閉線程池 */ public void closeThreadPool() { executorService.shutdown(); }startProgress從名字就可以看出這是一個開始進度條的方法。當我們點擊視頻的播放按鈕的同時也要調用此方法開始繪制進度條 最後一個closeThreadPool關閉線程池,當播放視頻的頁面被銷毀的時候,通過onDestory回調方法調用關閉線程池。 其它的三個方法從Log中可以看出是掛起,恢復,停止線程等操作。
另外,在使用CustomProgressBar時,其默認形式是一個圓形進度條(因為這個類是繼承ProgressBar的)。 其中style="?android:attr/progressBarStyleHorizontal"代碼的意思是將一個圓形進度條改為水平形式。 2、首先在res的目錄下創建一個名為raw的文件夾。將准備好的小視頻放在src/main/res/raw目錄裡面。 如下圖:
/** * 播放視頻頁面 * Created by Edward on 2016/4/15. */ public class MainActivity extends Activity { //視頻視圖實例 private VideoView videoView; //視頻路徑列表 private ArrayListvideoUriList; //咋們寫的自定義控件 private CustomProgressBar progressBar; //播放視頻按鈕 private ImageView imagePlayStop; //用來顯示當前視頻下標以及視頻的總數 private TextView txtVideoNumber; //當前視頻的下標 private int currentVideoIndex = 0; //是否開啟視頻線程標記位 private boolean isOpenVideoThread = false; //獲取內部類實例 private CustomProgressBar.CustomProgressBarRunnable customProgressBarRunnable; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); videoView = (VideoView) findViewById(R.id.video_view); videoUriList = new ArrayList<>(); progressBar = (CustomProgressBar) findViewById(R.id.progressbar); imagePlayStop = (ImageView) findViewById(R.id.image_play_stop); txtVideoNumber = (TextView) findViewById(R.id.txt_video_number); initData(); setCallBackListener(); } public void initData() { int[] videosId = {R.raw.bbb, R.raw.aaa, R.raw.ccc}; //獲取raw文件夾內視頻 for (int i = 0; i < videosId.length; i++) { videoUriList.add("android.resource://" + getPackageName() + "/" + videosId[i]); } //設置視頻段數 progressBar.setVideoSum(videoUriList.size()); txtVideoNumber.setText(currentVideoIndex + 1 + "/" + videoUriList.size()); } /** * 設置回調監聽事件 */ public void setCallBackListener() { //視頻播放完畢之後回調此方法,關閉線程 videoView.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mp) { //將繪制進度條的線程暫停 progressBar.stopThread(); //將視頻下標移動下一個 ++currentVideoIndex; //如果沒有視頻 if (currentVideoIndex > videoUriList.size() - 1) { Toast.makeText(MainActivity.this, "沒有視頻了", Toast.LENGTH_LONG).show(); //更換播放視頻按鈕的圖標 imagePlayStop.setImageResource(R.mipmap.video_play); //將當前視頻的下標設為0 currentVideoIndex = 0; //將開啟線程的標記位設置false isOpenVideoThread = false; } else { //每當一個視頻播放結束之後,再播放下一個視頻。 openVideoThread(currentVideoIndex); txtVideoNumber.setText(currentVideoIndex + 1 + "/" + videoUriList.size()); } } }); videoView.setOnErrorListener(new MediaPlayer.OnErrorListener() { @Override public boolean onError(MediaPlayer mp, int what, int extra) { Toast.makeText(MainActivity.this, "視頻播放錯誤!", Toast.LENGTH_LONG).show(); return false; } }); } public void onClick(View v) { switch (v.getId()) { //播放或暫停視頻 case R.id.image_play_stop: imagePlayStop(); break; //下一個視頻 case R.id.image_netx_video: ++currentVideoIndex; if (currentVideoIndex > videoUriList.size() - 1) { Toast.makeText(MainActivity.this, "沒有視頻了", Toast.LENGTH_LONG).show(); currentVideoIndex = videoUriList.size() - 1; } else { isOpenVideoThread = false; progressBar.stopThread(); imagePlayStop.setImageResource(R.mipmap.video_stop); txtVideoNumber.setText(currentVideoIndex + 1 + "/" + videoUriList.size()); openVideoThread(currentVideoIndex); } break; //上一個視頻 case R.id.image_prev_video: --currentVideoIndex; if (currentVideoIndex < 0) { Toast.makeText(MainActivity.this, "沒有視頻了", Toast.LENGTH_LONG).show(); ++currentVideoIndex; } else { isOpenVideoThread = false; progressBar.stopThread(); imagePlayStop.setImageResource(R.mipmap.video_stop); txtVideoNumber.setText(currentVideoIndex + 1 + "/" + videoUriList.size()); openVideoThread(currentVideoIndex); } break; } } /** * 視頻播放與暫停 */ public void imagePlayStop() { txtVideoNumber.setText(currentVideoIndex + 1 + "/" + videoUriList.size()); //如果沒有開啟視頻(線程),那麼就打開 if (!isOpenVideoThread) { imagePlayStop.setImageResource(R.mipmap.video_stop); openVideoThread(currentVideoIndex); } else { customProgressBarRunnable = progressBar.getCustomProgressBarRunnable(); //當前視頻線程打開了之後,如果線程處於運行狀態,那麼就掛起,否則恢復線程。 if (!customProgressBarRunnable.isWaiting()) { //掛起進度條的線程 progressBar.hangUpThread(); //暫停視頻播放 videoView.pause(); imagePlayStop.setImageResource(R.mipmap.video_play); } else { //恢復進度條的線程 progressBar.recoverThread(); //開始視頻播放 videoView.start(); imagePlayStop.setImageResource(R.mipmap.video_stop); } } } /** * 開啟視頻線程 * * @param currentVideoIndex */ public void openVideoThread(int currentVideoIndex) { //把線程標記位設為已開啟 isOpenVideoThread = true; //設置uri videoView.setVideoURI(Uri.parse(videoUriList.get(currentVideoIndex))); //開始視頻播放 videoView.start(); //開啟進度條繪制 progressBar.startProgress(videoView, currentVideoIndex); } @Override protected void onPause() { CustomProgressBar.CustomProgressBarRunnable customProgressBarRunnable = progressBar.getCustomProgressBarRunnable(); //當APP進入“不可見”狀態的時候(例如返回桌面,鎖屏,跳轉到另一個Activity等情況),將視頻暫停 if (customProgressBarRunnable != null && !customProgressBarRunnable.isWaiting()) imagePlayStop(); super.onPause(); } /** * 在該頁面被銷毀之前,關閉線程池,停止線程 */ @Override protected void onDestroy() { progressBar.closeThreadPool(); progressBar.stopThread(); super.onDestroy(); } }
一、概述在之前一個項目中,因為涉及到數據庫,所以就接觸到了ORM框架的GreenDao。後面就去網上大量的搜索下載學習,發現很多都是官網的翻譯或者是官網DEMO的簡單入門
我們在選購安卓手機的時候,參數中最容易犯暈的就是ROM和RAM了,許多朋友都不了解是什麼意思,廠家也借助這一點來大做文章,下面我就給大家來簡單的介紹一下其中
今天要實現的功能是實現專輯倒影效果,這個功能已經屬於圖像處理方面的了,對圖像處理我不怎麼在行,等一下會介紹一個很實用的工具類,專門用來進行圖像處理的。這個工具類不是我寫的
前言尋尋覓覓終於等到你,Material Design系列BottomBar開源庫你值得擁有。從我接觸android開發遇到tabhost,到radioGroup+Vie