码迷,mamicode.com
首页 > 其他好文 > 详细

改写控件之《自定义View,让你整个Layout像横向温度计一般》

时间:2015-09-23 19:30:35      阅读:165      评论:0      收藏:0      [点我收藏+]

标签:控件   布局   android   体验   

转载请注明出处:王亟亟的大牛之路

恐惧自我受苦的人,已经正因自我的恐惧在受苦。

我们平时的进度条一般是下面这样子的

技术分享

今天上的一个效果是这样的(初始化)
技术分享

动起来后是这样的

技术分享

给与用户一种新的体验吧,贴下项目结构

技术分享

自定义控件: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像横向温度计一般》

标签:控件   布局   android   体验   

原文地址:http://blog.csdn.net/ddwhan0123/article/details/48682507

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!