欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android自定义双向进度条的实现代码

程序员文章站 2023-12-20 08:57:34
想整个双向的进度条,就是可以选取播放范围的。 像这样: 然而官方控件里只有单向的。不要慌,我们自己画一个。 绘制一个进度条主要是三方面。1.样式,2....

想整个双向的进度条,就是可以选取播放范围的。

像这样:

Android自定义双向进度条的实现代码

然而官方控件里只有单向的。不要慌,我们自己画一个。

绘制一个进度条主要是三方面。1.样式,2.尺寸,3.操作监听。

完整代码来一遍:

注释基本上就把原理说明了一下。

package util;

import android.content.context;
import android.graphics.canvas;
import android.graphics.color;
import android.graphics.paint;
import android.graphics.drawable.drawable;
import android.support.v4.content.contextcompat;
import android.util.attributeset;
import android.util.log;
import android.view.motionevent;
import android.view.view;

import com.example.qzd.utildemo.r;

import java.math.bigdecimal;

/**
 * 双向滑块的进度条(区域选择)
 */
public class seekrangebar extends view {
  private context _context;
  private static final int click_on_low = 1;    //手指在前滑块上滑动
  private static final int click_on_high = 2;    //手指在后滑块上滑动
  private static final int click_in_low_area = 3;  //手指点击离前滑块近
  private static final int click_in_high_area = 4; //手指点击离后滑块近
  private static final int click_out_area = 5;   //手指点击在view外
  private static final int click_invaild = 0;
  private static final int[] state_normal = {};
  private static final int[] state_pressed =
 {android.r.attr.state_pressed,android.r.attr.state_window_focused,};
  private static int mthumbmargintop = 0;  //滑动块顶部离view顶部的距离
  private static int mtextviewmargintop = 0;  //当前滑块文字距离view顶部距离
  private drawable hasscrollbarbg;    //滑动条滑动后背景图
  private drawable notscrollbarbg;    //滑动条未滑动背景图
  private drawable mthumblow;     //前滑块
  private drawable mthumbhigh;    //后滑块
  private int mscollbarwidth;   //控件宽度 = 滑动条宽度 + 滑动块宽度
  private int mscollbarheight;  //控件高度
  private int mthumbwidth;    //滑动块直径
  private double moffsetlow = 0;   //前滑块中心坐标
  private double moffsethigh = 0;  //后滑块中心坐标
  private int mdistance=0;   //总刻度是固定距离 两边各去掉半个滑块距离
  private int mflag = click_invaild;  //手指按下的类型
  private double defaultscreenlow = 0;  //默认前滑块位置百分比
  private double defaultscreenhigh = 100; //默认后滑块位置百分比
  private onseekbarchangelistener mbarchangelistener;
  private boolean editable=false;//是否处于可编辑状态
  private int minigap=5;//ab的最小间隔
  private double progresslow;//起点(百分比)
  private double progresshigh;//终点

  public seekrangebar(context context) {
    this(context, null);
  }
  public seekrangebar(context context, attributeset attrs) {
    this(context, attrs, 0);
  }
  public seekrangebar(context context, attributeset attrs, int defstyle) {
    super(context, attrs, defstyle);
    _context=context;
    //这里设置背景图及滑块图,自定义过进度条的同学应该很熟悉了
    notscrollbarbg = contextcompat.getdrawable(_context,r.mipmap.hp_wbf);
    hasscrollbarbg = contextcompat.getdrawable(_context, r.mipmap.hp_ybf);
    mthumblow = contextcompat.getdrawable(_context,r.mipmap.hp_a);
    mthumbhigh = contextcompat.getdrawable(_context,r.mipmap.hp_b);
    mthumblow.setstate(state_normal);
    mthumbhigh.setstate(state_normal);
    //设置滑动条高度
    mscollbarheight = notscrollbarbg.getintrinsicheight();
    //设置滑动块直径
    mthumbwidth = mthumblow.getintrinsicwidth();
  }

  /**
   * 测量view尺寸(在ondraw()之前)
   * @param widthmeasurespec
   * @param heightmeasurespec
   */
  protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
    int width = measurespec.getsize(widthmeasurespec);
    mscollbarwidth = width;
    if(mdistance==0) {//这里滑块中心坐标初始化的时候测量一下(根据mdistance是否赋值判断),并不需要不停地去测量。后面会根据进度计算滑块位置。
      moffsetlow = mthumbwidth / 2;
      moffsethigh = width - mthumbwidth / 2;
    }
    mdistance = width - mthumbwidth;
    if(defaultscreenlow != 0) {
      moffsetlow = formatint(defaultscreenlow / 100 * (mdistance)) + mthumbwidth / 2;
    }
    if(defaultscreenhigh != 100) {
      moffsethigh = formatint(defaultscreenhigh / 100 * (mdistance)) + mthumbwidth / 2;
    }
    setmeasureddimension(width, mthumbwidth + mthumbmargintop + 2);
  }

  protected void onlayout(boolean changed, int l, int t, int r, int b) {
    super.onlayout(changed, l, t, r, b);
  }

  /**
   * 绘制进度条
   */
  protected void ondraw(canvas canvas) {
    super.ondraw(canvas);
    //设置绘制样式
    paint text_paint = new paint();
    text_paint.settextalign(paint.align.center);
    text_paint.setcolor(color.red);
    text_paint.settextsize(20);

    int top = mthumbmargintop + mthumbwidth / 2 - mscollbarheight / 2;
    int bottom = top + mscollbarheight;

    //绘制是否可操作状态的下的不同样式,仅可编辑状态下显示进度条
    if(editable) {
      //白色滑动条,两个滑块各两边部分
      notscrollbarbg.setbounds(mthumbwidth / 2, top, mscollbarwidth - mthumbwidth / 2, bottom);
      notscrollbarbg.draw(canvas);

      //红色滑动条,两个滑块中间部分
      hasscrollbarbg.setbounds((int) moffsetlow, top, (int) moffsethigh, bottom);
      hasscrollbarbg.draw(canvas);
    }

    //前滑块
    mthumblow.setbounds((int) (moffsetlow - mthumbwidth / 2), mthumbmargintop, (int) (moffsetlow + mthumbwidth / 2), mthumbwidth + mthumbmargintop);
    mthumblow.draw(canvas);

    //后滑块
    mthumbhigh.setbounds((int) (moffsethigh - mthumbwidth / 2), mthumbmargintop, (int) (moffsethigh + mthumbwidth / 2), mthumbwidth + mthumbmargintop);
    mthumbhigh.draw(canvas);

    //当前滑块刻度
    progresslow = formatint((moffsetlow - mthumbwidth / 2) * 100 / mdistance);
    progresshigh = formatint((moffsethigh - mthumbwidth / 2) * 100 / mdistance);
    canvas.drawtext((int) progresslow + "", (int) moffsetlow - 2 - 2, mtextviewmargintop, text_paint);
    canvas.drawtext((int) progresshigh + "", (int) moffsethigh - 2, mtextviewmargintop, text_paint);

    if (mbarchangelistener != null) {
      mbarchangelistener.onprogresschanged(this, progresslow, progresshigh);
    }
  }

  //手势监听
  @override
  public boolean ontouchevent(motionevent e) {
    if(!editable) {
      return false;
    }
    if (e.getaction() == motionevent.action_down) {
      mflag = getareaflag(e);
      if (mflag == click_on_low) {
        mthumblow.setstate(state_pressed);
      } else if (mflag == click_on_high) {
        mthumbhigh.setstate(state_pressed);
      } else if (mflag == click_in_low_area) {
        mthumblow.setstate(state_pressed);
        mthumbhigh.setstate(state_normal);
        //如果点击0-mthumbwidth/2坐标
        if (e.getx() < 0 || e.getx() <= mthumbwidth / 2) {
          moffsetlow = mthumbwidth / 2;
        } else if (e.getx() > mscollbarwidth - mthumbwidth / 2) {
          moffsetlow = mthumbwidth / 2 + mdistance;
        } else {
          moffsetlow = formatint(e.getx());

        }
      } else if (mflag == click_in_high_area) {
        mthumbhigh.setstate(state_pressed);
        mthumblow.setstate(state_normal);
        if (e.getx() >= mscollbarwidth - mthumbwidth / 2) {
          moffsethigh = mdistance + mthumbwidth / 2;
        } else {
          moffsethigh = formatint(e.getx());
        }
      }
      //更新滑块
      invalidate();
    } else if (e.getaction() == motionevent.action_move) {
      if (mflag == click_on_low) {
        if (e.getx() < 0 || e.getx() <= mthumbwidth / 2) {
          moffsetlow = mthumbwidth / 2;
        } else if (e.getx() >= mscollbarwidth - mthumbwidth / 2) {
          moffsetlow = mthumbwidth / 2 + mdistance;
          moffsethigh = moffsetlow;
        } else {
          moffsetlow = formatint(e.getx());
          if (moffsethigh - moffsetlow <= 0) {
            moffsethigh = (moffsetlow <= mdistance + mthumbwidth / 2) ? (moffsetlow) : (mdistance + mthumbwidth / 2);
          }
        }
      } else if (mflag == click_on_high) {
        if (e.getx() < mthumbwidth / 2) {
          moffsethigh = mthumbwidth / 2;
          moffsetlow = mthumbwidth / 2;
        } else if (e.getx() > mscollbarwidth - mthumbwidth / 2) {
          moffsethigh = mthumbwidth / 2 + mdistance;
        } else {
          moffsethigh = formatint(e.getx());
          if (moffsethigh - moffsetlow <= 0) {
            moffsetlow = (moffsethigh >= mthumbwidth / 2) ? (moffsethigh) : mthumbwidth / 2;
          }
        }
      }
      //更新滑块,每次滑块有动作都要执行此函数触发ondraw方法绘制新图片
      invalidate();
    } else if (e.getaction() == motionevent.action_up) {
      log.d("logcat","action up:"+progresshigh+"-"+progresslow);
      mthumblow.setstate(state_normal);
      mthumbhigh.setstate(state_normal);
      if(minigap>0 && progresshigh<progresslow+minigap){
        progresshigh=progresslow+minigap;
        this.defaultscreenhigh = progresshigh;
        moffsethigh = formatint(progresshigh / 100 * (mdistance)) + mthumbwidth / 2;
        invalidate();
      }
    }
    return true;
  }

  /**
   * 设置是否可编辑状态,非可编辑状态将不能对ab点进行操作
   * @param _b
   */
  public void seteditable(boolean _b){
    editable=_b;
    invalidate();
  }

  /**
   * 获取当前手指位置
   */
  public int getareaflag(motionevent e) {
    int top = mthumbmargintop;
    int bottom = mthumbwidth + mthumbmargintop;
    if (e.gety() >= top && e.gety() <= bottom && e.getx() >= (moffsetlow - mthumbwidth / 2) && e.getx() <= moffsetlow + mthumbwidth / 2) {
      return click_on_low;
    } else if (e.gety() >= top && e.gety() <= bottom && e.getx() >= (moffsethigh - mthumbwidth / 2) && e.getx() <= (moffsethigh + mthumbwidth / 2)) {
      return click_on_high;
    } else if (e.gety() >= top
      && e.gety() <= bottom
      && ((e.getx() >= 0 && e.getx() < (moffsetlow - mthumbwidth / 2)) || ((e.getx() > (moffsetlow + mthumbwidth / 2))
      && e.getx() <= ((double) moffsethigh + moffsetlow) / 2))) {
      return click_in_low_area;
    } else if (e.gety() >= top && e.gety() <= bottom && (((e.getx() > ((double) moffsethigh + moffsetlow) / 2) && e.getx() < (moffsethigh - mthumbwidth / 2)) || (e.getx() > (moffsethigh + mthumbwidth / 2) && e.getx() <= mscollbarwidth))) {
      return click_in_high_area;
    } else if (!(e.getx() >= 0 && e.getx() <= mscollbarwidth && e.gety() >= top && e.gety() <= bottom)) {
      return click_out_area;
    } else {
      return click_invaild;
    }
  }

  /**
   * 设置前滑块位置
   * @param progresslow
   */
  public void setprogresslow(double progresslow) {
    this.defaultscreenlow = progresslow;
    moffsetlow = formatint(progresslow / 100 * (mdistance)) + mthumbwidth / 2;
    invalidate();
  }

  /**
   * 设置后滑块位置
   * @param progresshigh
   */
  public void setprogresshigh(double progresshigh) {
    this.defaultscreenhigh = progresshigh;
    moffsethigh = formatint(progresshigh / 100 * (mdistance)) + mthumbwidth / 2;
    invalidate();
  }

  /**
   * 设置滑动监听
   * @param mlistener
   */
  public void setonseekbarchangelistener(onseekbarchangelistener mlistener) {
    this.mbarchangelistener = mlistener;
  }

  /**
   * 滑动监听,改变输入框的值
   */
  public interface onseekbarchangelistener {
    //滑动时
    public void onprogresschanged(seekrangebar seekbar, double progresslow, double progresshigh);
  }

  /**
   * 设置滑动结果为整数
   */
  private int formatint(double value) {
    bigdecimal bd = new bigdecimal(value);
    bigdecimal bd1 = bd.setscale(0, bigdecimal.round_half_up);
    return bd1.intvalue();
  }
}

然后就可以在程序中使用了。

布局中

<util.seekrangebar
  android:id="@+id/doubleseekbar"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:layout_centervertical="true"/>

调用

private seekrangebar doubleseekbar;//双向进度条
doubleseekbar = (seekrangebar) findviewbyid(r.id.doubleseekbar);
//监听进度范围变化
doubleseekbar.setonseekbarchangelistener(new seekrangebar.onseekbarchangelistener() {
  @override
  public void onprogresschanged(seekrangebar seekbar, double progresslow, double progresshigh) {
    log.d("logcat","低:" + progresslow + "高:" + progresshigh);
  }
});

相关github项目地址:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

上一篇:

下一篇: