編輯:關於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 ArrayList videoUriList;
//咋們寫的自定義控件
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();
}
}

Android數據庫框架GreenDao封裝使用,易理解、易擴展
一、概述在之前一個項目中,因為涉及到數據庫,所以就接觸到了ORM框架的GreenDao。後面就去網上大量的搜索下載學習,發現很多都是官網的翻譯或者是官網DEMO的簡單入門
安卓(Android)手機ROM和RAM的區別
我們在選購安卓手機的時候,參數中最容易犯暈的就是ROM和RAM了,許多朋友都不了解是什麼意思,廠家也借助這一點來大做文章,下面我就給大家來簡單的介紹一下其中
Android應用開發之簡易、大氣音樂播放器實現專輯倒影效果
今天要實現的功能是實現專輯倒影效果,這個功能已經屬於圖像處理方面的了,對圖像處理我不怎麼在行,等一下會介紹一個很實用的工具類,專門用來進行圖像處理的。這個工具類不是我寫的
開源項目:BottomBar
前言尋尋覓覓終於等到你,Material Design系列BottomBar開源庫你值得擁有。從我接觸android開發遇到tabhost,到radioGroup+Vie