效果如圖:
點擊錄音進度條開始,再次點擊暫停,會在暫停的地方有一個白色的斷點。
先說說大概的思路,進度條的xml文件是ProgressBar和RecyclerView的疊加,RecyclerView是水平布局的,我們先自定義item的寬度,并且每個item的默認是invisible的,點擊暫停的時候我們會記錄相應的position,只有當item的position和記錄暫停的position相等時,item才會顯示就會出現白色的斷點。
布局文件
<ProgressBar
android:id="@+id/progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="6dp"
android:max="300"
android:layout_centerVertical="true"
android:progress="0"
android:progressDrawable="@drawable/progressbar_bg"
/>
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="6dp"
android:id="@+id/recy"
android:layout_centerVertical="true"
>
這里是布局文件中最重要的部分。
代碼邏輯
先獲得屏幕的寬度,然后根據我們自己設定的每個item(白色的斷點)的寬度來計算出有多少個item。
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
// 獲取到屏幕的像素寬度
dwidth = metrics.widthPixels;
// yinzi是暫停的那個點的長度
// partCount就是設備寬度下斷點的個數
partCount = dwidth / blockWidth;
接下來就是設置RecyclerView的其他參數
LinearLayoutManager manager = new LinearLayoutManager(this,LinearLayoutManager.HORIZONTAL,false);
mRecyclerView.setLayoutManager(manager);
List<String> datas = new ArrayList<>();
for (int i = 0; i < partCount; i++) {
datas.add("");
}
seekAdapter = new SeekAdapter(datas, positions);
mRecyclerView.setAdapter(seekAdapter);
我們的邏輯都是從點擊開始錄音按鈕開始的。
switch (view.getId()) {
// 點擊開始錄音,進度條開始啟動;再次點擊打暫停斷點
case R.id.record:
// 點擊開始錄音按鈕顯示其他按鈕
showImgs();
// 點擊后退錄音狀態取反
if (!mIsRecording) {
// 正在錄音狀態
mStartTime = System.currentTimeMillis();
startProgress();
mIsRecording = true;
mRecord.setSelected(true);
Toast.makeText(this, "開始錄音", Toast.LENGTH_SHORT).show();
} else {
mRecord.setSelected(false);
recodeStop();
mIsRecording = false;
timer.cancel();
Toast.makeText(this, "暫停錄音", Toast.LENGTH_SHORT).show();
}
break;
startProgress()進度條開始顯示進度,邏輯都比較簡單,直接看代碼就可以了。
private void startProgress() {
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
totalTime++;
second++;
if (second >= 60) {
second = 0;
minute++;
}
handler.sendEmptyMessage(1);
}
};
timer = new Timer();
timer.schedule(timerTask, 1000, 1000);
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
setTime();
mRecordTimer.setText("0" + minute + ":" + setTime());
// 點擊開始進度條開始走
mProgressBar.setProgress(totalTime);
}
};
private String setTime() {
String time = "";
if (second < 10) {
time = "0" + second;
} else {
time = second + "";
}
return time;
}
再次點擊錄制按鈕就會暫停錄制,顯示白色的斷點,主要就是下面兩個方法,在點擊暫停按鈕后刷新adapter的數據還要注意一下播放的狀態。
private void recodeStop() {
// 將暫停的位置添加進集合,可以用來顯示斷點
positions.add(trans(totalTime));
if (seekAdapter != null)
seekAdapter.notifyDataSetChanged();
// 停止錄音
mEndTime = System.currentTimeMillis();
timer.cancel();
}
// 將錄制的時間時長轉化為斷點位置
private int trans(int totalTime) {
// 先計算出錄制時間占最大錄制時間的百分比,然后通過這個百分比求出暫停斷點在所有在暫停斷點的位置
float v = totalTime / maxRecordTime;
float v1 = v * partCount;
int p = (int) v1;
return p;
}
positions集合是用來存儲斷點位置的,但是我們是用totalTime即錄制的總時長來更新進度的,所以使用trans()方法轉化為item在adapter中的position。先計算出totalTime(目前錄制時長)占總時長(我這里是300秒)的百分比v,然后通過這個v計算出目前暫停斷點item在adapter中的position,因為我們已經確定了總的item的個數。一定要注意點擊暫停的時候我們一定要timer.cancel(),要不然下次再次點擊錄制按鈕時間就會加快一倍(每次暫停后再次恢復錄制都會比前一次快一倍,有興趣的同學可以去找一下Timer相關的資料)。
接下來就是RecyclerView的Adapter中的邏輯判斷了。
public class SeekAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
List<String> datas;
List<Integer> positions;
public SeekAdapter(List<String> datas,List<Integer> positions) {
this.datas = datas;
this.positions = positions;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.record_point_item, parent, false);
return new VIewH(view);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
VIewH holder = (VIewH) viewHolder;
for (int i = 0; i < positions.size(); i++) {
int a = positions.get(i);
if (position == a) {
holder.mImageView.setVisibility(View.VISIBLE);
}
}
}
@Override
public int getItemCount() {
return datas.size();
}
public static class VIewH extends RecyclerView.ViewHolder {
ImageView mImageView;
public VIewH(View itemView) {
super(itemView);
mImageView = (ImageView)itemView.findViewById(R.id.record_point_iv);
}
}
}
最主要的就是當position == a的時候將item設置為VISIBLE即可。
item的布局文件:
<ImageView
android:id="@+id/record_point_iv"
android:layout_width="8px"
android:layout_height="6dp"
android:background="@color/white"
android:visibility="invisible"/>
整體來說難度不大,但是一般很少能想到用RecyclerView來做斷點,要感謝田老師提供的解決思路。這個案例精妙的地方在于根據錄制時長和總時長的比率來得到item在adapter中的position,然后通過position來顯示斷點。
源碼下載:https://git.oschina.net/robin_mvc_coder/RecyclerProgressbar.git