Android自定义双向进度条的实现代码
程序员文章站
2023-12-20 08:57:34
想整个双向的进度条,就是可以选取播放范围的。
像这样:
然而官方控件里只有单向的。不要慌,我们自己画一个。
绘制一个进度条主要是三方面。1.样式,2....
想整个双向的进度条,就是可以选取播放范围的。
像这样:
然而官方控件里只有单向的。不要慌,我们自己画一个。
绘制一个进度条主要是三方面。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项目地址:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。