我的Android进阶之旅------>Android如何通过自定义SeekBar来实现视频播放进度条

简介: 首先来看一下效果图,如下所示:其中进度条如下:接下来说一说我的思路,上面的进度拖动条有自定义的Thumb,在Thumb正上方有一个PopupWindow窗口,窗口里面显示当前的播放时间。


首先来看一下效果图,如下所示:

其中进度条如下:


接下来说一说我的思路,上面的进度拖动条有自定义的Thumb,在Thumb正上方有一个PopupWindow窗口,窗口里面显示当前的播放时间。在SeekBar右边有一个文本框显示当前播放时间/总时间

step1、先来看一看PopupWindow的布局文件,seek_popu.xml,效果如下图所示:


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/seek_dialog_bg" >
    <!-- 展现当前播放进度时间的文本框-->
    <TextView
        android:id="@+id/dialogSeekTime"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dip"
        android:layout_marginTop="12dip"
        android:text="@string/unknow_seek_time"
        android:textColor="@color/black"
        android:textSize="12sp" />
</RelativeLayout>

step2、自定义一个SeekBar

import com.canplay.video.R;

import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.PopupWindow;
import android.widget.SeekBar;
import android.widget.TextView;

/**
 * 自定义进度拖动条控件
 */
public class MySeekBar extends SeekBar {
	/**
	 * 定义一个展现时间的PopupWindow
	 */
	private PopupWindow mPopupWindow;
	
	private View mView;
	/**
	 * 显示时间的TextView
	 */
	private TextView dialogSeekTime;
	/**
	 * 用来表示该组件在整个屏幕内的绝对坐标,其中 mPosition[0] 代表X坐标,mPosition[1] 代表Y坐标。
	 */
	private int[] mPosition;
	/**
	 * SeekBar上的Thumb的宽度,即那个托动的小黄点的宽度
	 */
	private final int mThumbWidth = 25;

	public MySeekBar(Context context) {
		this(context, null);
	}

	public MySeekBar(Context context, AttributeSet attrs) {
		super(context, attrs);
		mView = LayoutInflater.from(context).inflate(R.layout.seek_popu, null);
		dialogSeekTime = (TextView) mView.findViewById(R.id.dialogSeekTime);
		mPopupWindow = new PopupWindow(mView, mView.getWidth(), mView.getHeight(), true);
		mPosition = new int[2];
	}

	/**
	 * 获取控件的宽度
	 * 
	 * @param v
	 * @return 控件的宽度
	 */
	private int getViewWidth(View v) {
		int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
		int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
		v.measure(w, h);
		return v.getMeasuredWidth();
	}

	/**
	 * 获取控件的高度
	 * 
	 * @param v
	 * @return 控件的高度
	 */
	private int getViewHeight(View v) {
		int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
		int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
		v.measure(w, h);
		return v.getMeasuredHeight();
	}

	/**
	 * 隐藏进度拖动条的PopupWindow
	 */
	public void hideSeekDialog() {
		if (mPopupWindow != null && mPopupWindow.isShowing()) {
			mPopupWindow.dismiss();
		}
	}

	/**
	 * 显示进度拖动条的PopupWindow
	 * 
	 * @param str
	 *                  时间值
	 */
	public void showSeekDialog(String str) {
		dialogSeekTime.setText(str);
		int progress = this.getProgress();
		// 计算每个进度值所占的宽度
		int thumb_x = (int) (progress * (1.0f * (this.getWidth() - 22) / this.getMax())); //22是两边的空白部分宽度
		// 更新后的PopupWindow的Y坐标
		int middle = this.getHeight() / 2 + 120;
		if (mPopupWindow != null) {
			try {
				/*
				 * 获取在整个屏幕内的绝对坐标,注意这个值是要从屏幕顶端算起,也就是包括了通知栏的高度。
				 * 其中 mPosition[0] 代表X坐标,mPosition[1]代表Y坐标。
				 */
				this.getLocationOnScreen(mPosition);
				// 相对某个控件的位置(正左下方),在X、Y方向各有偏移
				mPopupWindow.showAsDropDown(this, (int) mPosition[0], mPosition[1]);
				/*
				 * 更新后的PopupWindow的X坐标
				 * 首先要把当前坐标值减去PopWindow的宽度的一半,再加上Thumb的宽度一半。
				 * 这样才能使PopWindow的中心点和Thumb的中心点的X坐标相等
				 */
				int x = thumb_x + mPosition[0] - getViewWidth(mView) / 2 + mThumbWidth / 2;
				// 更新popup窗口的位置
				mPopupWindow.update(x, middle, getViewWidth(mView), getViewHeight(mView));
			} catch (Exception e) {
			}
		}
	}
}

step3、将自定义的拖动条加入到布局文件中,下面是部分代码

 <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:background="@android:color/black" >
......
<!-- 进度拖动条 -->
        <RelativeLayout
            android:id="@+id/seek_bar_container"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_above="@id/control_btn_container"
            android:background="@drawable/seek_bg" >

            <com.canplay.video.view.MySeekBar
                android:id="@+id/seek_progress"
                android:layout_width="600dip"
                android:layout_height="wrap_content"
                android:layout_centerInParent="true" />

            <TextView
                android:id="@+id/currentTime"
                style="@style/seekTime"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_toRightOf="@id/seek_progress"
                android:paddingLeft="20dip"
                android:text="@string/unknow_time" />
        </RelativeLayout>
...............
</RelativeLayout>

step4、在主文件中对拖动条进行托动监听

mSeekBar = (MySeekBar) findViewById(R.id.seek_progress);
mSeekBar.setOnSeekBarChangeListener(mSeekBarListener);
/**
	 * 进度拖动条监听器
	 */
	private OnSeekBarChangeListener mSeekBarListener = new OnSeekBarChangeListener() {
		// 通知进度已经被修改
		public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
			if (isTouchSeeked) {
				mSeekBar.showSeekDialog(makeTimeString(progress));//动态展示当前播放时间
			} else {
				mSeekBar.hideSeekDialog();
			}
		}

		// 通知用户已经开始一个触摸拖动手势
		public void onStartTrackingTouch(SeekBar seekBar) {
			showControlView(3600000);
			isTouchSeeked = true;
		}

		// 通知用户触摸手势已经结束
		public void onStopTrackingTouch(SeekBar seekBar) {
			Message msg = Message.obtain();
			msg.what = PROGRESS_SEEKTO;
			msg.arg1 = seekBar.getProgress();
			mHandler.removeMessages(PROGRESS_SEEKTO);
			mHandler.sendMessageAtTime(msg, 1000);// 1秒之后开始发送更新进度的消息
			isTouchSeeked = false;
			showControlView(sDefaultTimeout);
		}
	};

其中将进度值转换为时间的方法makeTimeString(int secs)如下所示:

/**
	 * 格式化的Builder
	 */
	private StringBuilder sFormatBuilder = new StringBuilder();
	/**
	 * 格式化的Formatter
	 */
	private Formatter sFormatter = new Formatter(sFormatBuilder, Locale.getDefault());
	/**
	 * 格式化的相关属性
	 */
	private final Object[] sTimeArgs = new Object[3];

	/**
	 * 转换进度值为时间
	 * 
	 * @param secs
	 * @return
	 */
	private String makeTimeString(int secs) {
		/**
		 * %[argument_index$][flags][width]conversion 可选的
		 * argument_index 是一个十进制整数,用于表明参数在参数列表中的位置。第一个参数由 "1$"
		 * 引用,第二个参数由 "2$" 引用,依此类推。 可选 flags
		 * 是修改输出格式的字符集。有效标志集取决于转换类型。 可选 width
		 * 是一个非负十进制整数,表明要向输出中写入的最少字符数。 可选 precision
		 * 是一个非负十进制整数,通常用来限制字符数。特定行为取决于转换类型。 所需 conversion
		 * 是一个表明应该如何格式化参数的字符。给定参数的有效转换集取决于参数的数据类型。
		 */
		String durationformat = getString(R.string.durationformat);// <xliff:g
								// id="format">%1$02d:%2$02d:%3$02d</xliff:g>
		sFormatBuilder.setLength(0);
		secs = secs / 1000;
		Object[] timeArgs = sTimeArgs;
		timeArgs[0] = secs / 3600; // 秒
		timeArgs[1] = (secs % 3600) / 60; // 分
		timeArgs[2] = (secs % 3600 % 60) % 60; // 时
		return sFormatter.format(durationformat, timeArgs).toString().trim();
	}


当然,这里只是简单的介绍了下自定义进度条,而该进度条的样式都没有展现出来,样式读者可以自己定义。




PS: 2017-11-4,今天看到github上有个很棒的类似的开源库,效果如下,大家可以看看!


https://github.com/rubensousa/PreviewSeekBar


如果你使用Google Play Movies,你可能注意到了这个动画效果很棒,可以预览电影的SeekBar。 Rúben Sousa (https://medium.com/@rubensousa)实现了这种效果并开源。下面的gif图片很好的说明了其功能。如果你的app是一个播放器,你决定应该试试。








====================================================================================

  作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址http://blog.csdn.net/ouyang_peng

===================================================================================



相关文章
|
4月前
|
Android开发 UED 计算机视觉
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
本文介绍了一款受游戏“金铲铲之战”启发的Android自定义View——线条等待动画的实现过程。通过将布局分为10份,利用`onSizeChanged`测量最小长度,并借助画笔绘制动态线条,实现渐变伸缩效果。动画逻辑通过四个变量控制线条的增长与回退,最终形成流畅的等待动画。代码中详细展示了画笔初始化、线条绘制及动画更新的核心步骤,并提供完整源码供参考。此动画适用于加载场景,提升用户体验。
425 5
Android自定义view之线条等待动画(灵感来源:金铲铲之战)
|
4月前
|
Android开发
Android自定义view之利用PathEffect实现动态效果
本文介绍如何在Android自定义View中利用`PathEffect`实现动态效果。通过改变偏移量,结合`PathEffect`的子类(如`CornerPathEffect`、`DashPathEffect`、`PathDashPathEffect`等)实现路径绘制的动态变化。文章详细解析了各子类的功能与参数,并通过案例代码展示了如何使用`ComposePathEffect`组合效果,以及通过修改偏移量实现动画。最终效果为一个菱形图案沿路径运动,源码附于文末供参考。
|
4月前
|
Android开发 开发者
Android自定义view之利用drawArc方法实现动态效果
本文介绍了如何通过Android自定义View实现动态效果,重点使用`drawArc`方法完成圆弧动画。首先通过`onSizeChanged`进行测量,初始化画笔属性,设置圆弧相关参数。核心思路是不断改变圆弧扫过角度`sweepAngle`,并调用`invalidate()`刷新View以实现动态旋转效果。最后附上完整代码与效果图,帮助开发者快速理解并实践这一动画实现方式。
133 0
|
4月前
|
Android开发 数据安全/隐私保护 开发者
Android自定义view之模仿登录界面文本输入框(华为云APP)
本文介绍了一款自定义输入框的实现,包含静态效果、hint值浮动动画及功能扩展。通过组合多个控件完成界面布局,使用TranslateAnimation与AlphaAnimation实现hint文字上下浮动效果,支持密码加密解密显示、去除键盘回车空格输入、光标定位等功能。代码基于Android平台,提供完整源码与attrs配置,方便复用与定制。希望对开发者有所帮助。
|
4月前
|
XML Java Android开发
Android自定义view之网易云推荐歌单界面
本文详细介绍了如何通过自定义View实现网易云音乐推荐歌单界面的效果。首先,作者自定义了一个圆角图片控件`MellowImageView`,用于绘制圆角矩形图片。接着,通过将布局放入`HorizontalScrollView`中,实现了左右滑动功能,并使用`ViewFlipper`添加图片切换动画效果。文章提供了完整的代码示例,包括XML布局、动画文件和Java代码,最终展示了实现效果。此教程适合想了解自定义View和动画效果的开发者。
215 65
Android自定义view之网易云推荐歌单界面
|
4月前
|
XML 前端开发 Android开发
一篇文章带你走近Android自定义view
这是一篇关于Android自定义View的全面教程,涵盖从基础到进阶的知识点。文章首先讲解了自定义View的必要性及简单实现(如通过三个构造函数解决焦点问题),接着深入探讨Canvas绘图、自定义属性设置、动画实现等内容。还提供了具体案例,如跑马灯、折线图、太极图等。此外,文章详细解析了View绘制流程(measure、layout、draw)和事件分发机制。最后延伸至SurfaceView、GLSurfaceView、SVG动画等高级主题,并附带GitHub案例供实践。适合希望深入理解Android自定义View的开发者学习参考。
525 84
|
4月前
|
前端开发 Android开发 UED
讲讲Android为自定义view提供的SurfaceView
本文详细介绍了Android中自定义View时使用SurfaceView的必要性和实现方式。首先分析了在复杂绘制逻辑和高频界面更新场景下,传统View可能引发卡顿的问题,进而引出SurfaceView作为解决方案。文章通过Android官方Demo展示了SurfaceView的基本用法,包括实现`SurfaceHolder.Callback2`接口、与Activity生命周期绑定、子线程中使用`lockCanvas()`和`unlockCanvasAndPost()`方法完成绘图操作。
103 3
|
4月前
|
Android开发 开发者
Android自定义view之围棋动画(化繁为简)
本文介绍了Android自定义View的动画实现,通过两个案例拓展动态效果。第一个案例基于`drawArc`方法实现单次动画,借助布尔值控制动画流程。第二个案例以围棋动画为例,从简单的小球直线运动到双向变速运动,最终实现循环动画效果。代码结构清晰,逻辑简明,展示了如何化繁为简实现复杂动画,帮助读者拓展动态效果设计思路。文末提供完整源码,适合初学者和进阶开发者学习参考。
Android自定义view之围棋动画(化繁为简)
|
4月前
|
Java Android开发 开发者
Android自定义view之围棋动画
本文详细介绍了在Android中自定义View实现围棋动画的过程。从测量宽高、绘制棋盘背景,到创建固定棋子及动态棋子,最后通过属性动画实现棋子的移动效果。文章还讲解了如何通过自定义属性调整棋子和棋盘的颜色及动画时长,并优化视觉效果,如添加渐变色让白子更明显。最终效果既可作为围棋动画展示,也可用作加载等待动画。代码完整,适合进阶开发者学习参考。
|
4月前
|
传感器 Android开发 开发者
Android自定义view之3D正方体
本文介绍了如何通过手势滑动操作实现3D正方体的旋转效果,基于Android自定义View中的GLSurfaceView。相较于使用传感器控制,本文改用事件分发机制(onTouchEvent)处理用户手势输入,调整3D正方体的角度。代码中详细展示了TouchSurfaceView的实现,包括触控逻辑、OpenGL ES绘制3D正方体的核心过程,以及生命周期管理。适合对Android 3D图形开发感兴趣的开发者学习参考。

热门文章

最新文章