恐惧自我受苦的人,已经正因自我的恐惧在受苦。
我们平时的进度条一般是下面这样子的
今天上的一个效果是这样的(初始化)
动起来后是这样的
给与用户一种新的体验吧,贴下项目结构
自定义控件:ProgressLayout
配套的监听事件:ProgressLayoutListener
适配器:RecylerListAdapter
对象类:Track(实际生产应该是 Json对象之类的)
OK,废话不多说,上代码
public class Track {
private int trackId;
private String songName;
private String singerName;
private int durationInSec;
private boolean isPlaying = false;
public Track(int trackId, String songName, String singerName, int durationInSec) {
this.trackId = trackId;
this.songName = songName;
this.singerName = singerName;
this.durationInSec = durationInSec;
}
public String getSongName() {
return songName;
}
public void setSongName(String songName) {
this.songName = songName;
}
public String getSingerName() {
return singerName;
}
public void setSingerName(String singerName) {
this.singerName = singerName;
}
public int getDurationInSec() {
return durationInSec;
}
public void setDurationInSec(int durationInSec) {
this.durationInSec = durationInSec;
}
public int getTrackId() {
return trackId;
}
public void setTrackId(int trackId) {
this.trackId = trackId;
}
public boolean isPlaying() {
return isPlaying;
}
public void setIsPlaying(boolean isPlaying) {
this.isPlaying = isPlaying;
}
}
分析:各字段的get,set方法,用于给对象赋值。
RecylerListAdapter
public class RecylerListAdapter extends RecyclerView.Adapter<RecylerListAdapter.ViewHolder> {
/**
* 数据源集合
*/
private List<Track> trackList;
/**
* 当前播放的对象。
*/
private Track currentTrack;
/**
* 持续时间
*/
private int currentDuration = 0;
/**
* 是否正在播放
*/
private boolean isPlaying = false;
private static final int SECOND_MS = 1000;
/**
* recyclerview中调用的Handle
*/
private Handler mHandler = new Handler();
/**
* 计算秒数
*
*/
private final Runnable mRunnable = new Runnable() {
@Override public void run() {
currentDuration += 1;
mHandler.postDelayed(mRunnable, SECOND_MS);
}
};
/**
* 传参
*/
public void setTrackList(List<Track> trackList) {
this.trackList = trackList;
notifyDataSetChanged();
}
/**
* 创建holder
*/
@Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View view =
LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_item, viewGroup, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
/**
* 绑定对象
*/
@Override public void onBindViewHolder(final ViewHolder viewHolder, final int i) {
final Track track = trackList.get(i);
viewHolder.textViewDuration.setText(calculateSongDuration(track.getDurationInSec()));
viewHolder.textViewSong.setText(track.getSongName());
viewHolder.textViewSinger.setText(track.getSingerName());
viewHolder.imageViewAction.setBackgroundResource(R.drawable.play);
viewHolder.progressLayout.setMaxProgress(track.getDurationInSec());
if (currentTrack != null && currentTrack == track) {
viewHolder.imageViewAction.setBackgroundResource(
isPlaying ? R.drawable.pause : R.drawable.play);
viewHolder.progressLayout.setCurrentProgress(currentDuration);
if (isPlaying) viewHolder.progressLayout.start();
} else {
viewHolder.progressLayout.cancel();
}
viewHolder.imageViewAction.setOnClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
if (track != currentTrack) {
currentTrack = track;
mHandler.removeCallbacks(mRunnable);
currentDuration = 0;
}
if (!viewHolder.progressLayout.isPlaying()) {
isPlaying = true;
viewHolder.progressLayout.start();
mHandler.postDelayed(mRunnable, 0);
viewHolder.imageViewAction.setBackgroundResource(R.drawable.pause);
notifyDataSetChanged();
} else {
isPlaying = false;
viewHolder.progressLayout.stop();
mHandler.removeCallbacks(mRunnable);
viewHolder.imageViewAction.setBackgroundResource(R.drawable.play);
notifyDataSetChanged();
}
}
});
/*
* 播放按钮的监听事件
* */
viewHolder.progressLayout.setProgressLayoutListener(new ProgressLayoutListener() {
@Override public void onProgressCompleted() {
viewHolder.imageViewAction.setBackgroundResource(R.drawable.play);
}
@Override public void onProgressChanged(int seconds) {
viewHolder.textViewDuration.setText(calculateSongDuration(seconds));
}
});
}
/**
* List大小
*/
@Override public int getItemCount() {
return trackList.size();
}
/**
* 换成分钟
*/
private String calculateSongDuration(int seconds) {
return new StringBuilder(String.valueOf(seconds / 60))
.append(":")
.append(String.valueOf(seconds % 60))
.toString();
}
/**
* ViewHolder对象
*/
public static class ViewHolder extends RecyclerView.ViewHolder {
@Bind(R.id.imageviewAction) ImageView imageViewAction;
@Bind(R.id.progressLayout) ProgressLayout progressLayout;
@Bind(R.id.textviewSong) TextView textViewSong;
@Bind(R.id.textviewSinger) TextView textViewSinger;
@Bind(R.id.textviewDuration) TextView textViewDuration;
public ViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
ProgressLayoutListener接口
public interface ProgressLayoutListener {
void onProgressCompleted();
void onProgressChanged(int seconds);
}
ProgressLayout
public class ProgressLayout extends View implements Animatable {
private static final int COLOR_EMPTY_DEFAULT = 0x00000000;
private static final int COLOR_LOADED_DEFAULT = 0x11FFFFFF;
private static final int PROGRESS_SECOND_MS = 1000;
private static Paint paintProgressLoaded;
private static Paint paintProgressEmpty;
private boolean isPlaying = false;
private boolean isAutoProgress;
private int mHeight;
private int mWidth;
private int maxProgress;
private int currentProgress = 0;
private Handler handlerProgress;
private ProgressLayoutListener progressLayoutListener;
public ProgressLayout(Context context) {
this(context, null);
}
public ProgressLayout(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public ProgressLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ProgressLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
@Override public boolean isRunning() {
return isPlaying;
}
@Override public void start() {
if (isAutoProgress) {
isPlaying = true;
handlerProgress.removeCallbacksAndMessages(null);
handlerProgress.postDelayed(mRunnableProgress, 0);
}
}
@Override public void stop() {
isPlaying = false;
handlerProgress.removeCallbacks(mRunnableProgress);
postInvalidate();
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
mWidth = MeasureSpec.getSize(widthMeasureSpec);
mHeight = MeasureSpec.getSize(heightMeasureSpec);
}
@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawRect(0, 0, mWidth, mHeight, paintProgressEmpty);
canvas.drawRect(0, 0, calculatePositionIndex(currentProgress), mHeight, paintProgressLoaded);
}
private void init(Context context, AttributeSet attrs) {
setWillNotDraw(false);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.progressLayout);
isAutoProgress = a.getBoolean(R.styleable.progressLayout_autoProgress, true);
maxProgress = a.getInt(R.styleable.progressLayout_maxProgress, 0);
maxProgress = maxProgress * 10;
int loadedColor = a.getColor(R.styleable.progressLayout_loadedColor, COLOR_LOADED_DEFAULT);
int emptyColor = a.getColor(R.styleable.progressLayout_emptyColor, COLOR_EMPTY_DEFAULT);
a.recycle();
paintProgressEmpty = new Paint();
paintProgressEmpty.setColor(emptyColor);
paintProgressEmpty.setStyle(Paint.Style.FILL);
paintProgressEmpty.setAntiAlias(true);
paintProgressLoaded = new Paint();
paintProgressLoaded.setColor(loadedColor);
paintProgressLoaded.setStyle(Paint.Style.FILL);
paintProgressLoaded.setAntiAlias(true);
handlerProgress = new Handler();
}
private int calculatePositionIndex(int currentProgress) {
return (currentProgress * mWidth) / maxProgress;
}
public boolean isPlaying() {
return isPlaying;
}
public void cancel() {
isPlaying = false;
currentProgress = 0;
handlerProgress.removeCallbacks(mRunnableProgress);
postInvalidate();
}
public void setCurrentProgress(int currentProgress) {
this.currentProgress = currentProgress * 10;
postInvalidate();
}
public void setMaxProgress(int maxProgress) {
this.maxProgress = maxProgress * 10;
postInvalidate();
}
public void setAutoProgress(boolean isAutoProgress) {
this.isAutoProgress = isAutoProgress;
}
public void setProgressLayoutListener(ProgressLayoutListener progressLayoutListener) {
this.progressLayoutListener = progressLayoutListener;
}
private final Runnable mRunnableProgress = new Runnable() {
@Override public void run() {
if (isPlaying) {
if (currentProgress == maxProgress) {
if (progressLayoutListener != null) {
progressLayoutListener.onProgressCompleted();
}
currentProgress = 0;
setCurrentProgress(currentProgress);
stop();
} else {
postInvalidate();
currentProgress += 1;
if (progressLayoutListener != null) {
progressLayoutListener.onProgressChanged(currentProgress / 10);
}
handlerProgress.postDelayed(mRunnableProgress, PROGRESS_SECOND_MS / 10);
}
}
}
};
}
分析:
postInvalidate是刷新界面,使用postInvalidate则比较简单,不需要handler,直接在线程中调用postInvalidate即可。
invalidate是刷新页面,实例化一个Handler对象,并重写handleMessage方法调用invalidate()实现界面刷新;而在线程中通过sendMessage发送界面更新消息。
在init()方法内对我们的布局进行初始化,并new 一个Handle实例对之后的UI变化进行操作
具体内容可以看源码:
源码地址:http://yunpan.cn/cHHj3TwvaNhc5 访问密码 67e6
版权声明:本文为博主原创文章,未经博主允许不得转载。
改写控件之《自定义View,让你整个Layout像横向温度计一般》
原文地址:http://blog.csdn.net/ddwhan0123/article/details/48682507