首页 > 系统 > Android > 正文

Android自定义SeekBar实现视频播放进度条

2019-12-12 03:13:09
字体:
来源:转载
供稿:网友

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

其中进度条如下:

接下来说一说我的思路,上面的进度拖动条有自定义的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();  } 

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

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表