本文主要是介绍关于MediaController的自定义,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
上一篇博客介绍了videoview和自定义该类,这篇想介绍一下MediaController,本人接触android也就几个月,其中还有一些问题,希望和大家一起进步。
自定义其实就是将android的MediaController源码摘出来,然后对其进行改编定义成自己的类。
摘出来和修改如下:
import java.util.Formatter;
import java.util.Locale;import com.android.internal.policy.PolicyManager;import android.content.Context;
import android.graphics.PixelFormat;
import android.media.AudioManager;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
//import android.view.View.OnLayoutChangeListener;
//import android.view.View.OnTouchListener;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
//import android.widget.FrameLayout.LayoutParams;
//import android.widget.MediaController.MediaPlayerControl;
import android.widget.SeekBar.OnSeekBarChangeListener;public class MediaController extends FrameLayout {private MediaPlayerControl mPlayer;private Context mContext;private View mAnchor;private View mRoot;private WindowManager mWindowManager;private Window mWindow;private View mDecor;private WindowManager.LayoutParams mDecorLayoutParams;private ProgressBar mProgress;private TextView mEndTime, mCurrentTime;private boolean mShowing;private boolean mDragging;private static final int sDefaultTimeout = 3000;private static final int FADE_OUT = 1;private static final int SHOW_PROGRESS = 2;private boolean mUseFastForward;private boolean mFromXml;private boolean mListenersSet;private View.OnClickListener mNextListener, mPrevListener;StringBuilder mFormatBuilder;Formatter mFormatter;private ImageButton mPauseButton;private ImageButton mFfwdButton;private ImageButton mRewButton;private ImageButton mNextButton;private ImageButton mPrevButton;public MediaController(Context context, AttributeSet attrs) {super(context, attrs);mRoot = this;mContext = context;mUseFastForward = true;mFromXml = true;}@Overridepublic void onFinishInflate() {if (mRoot != null)initControllerView(mRoot);}public MediaController(Context context, boolean useFastForward) {super(context);mContext = context;mUseFastForward = useFastForward;initFloatingWindowLayout();initFloatingWindow();}public MediaController(Context context) {this(context, true);}private void initFloatingWindow() {mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);mWindow = PolicyManager.makeNewWindow(mContext);mWindow.setWindowManager(mWindowManager, null, null);mWindow.requestFeature(Window.FEATURE_NO_TITLE);mDecor = mWindow.getDecorView();mDecor.setOnTouchListener(mTouchListener);mWindow.setContentView(this);mWindow.setBackgroundDrawableResource(android.R.color.transparent);mWindow.setVolumeControlStream(AudioManager.STREAM_MUSIC);setFocusable(true);setFocusableInTouchMode(true);setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);requestFocus();}private void initFloatingWindowLayout() {mDecorLayoutParams = new WindowManager.LayoutParams();WindowManager.LayoutParams p = mDecorLayoutParams;p.gravity = Gravity.TOP;p.height = LayoutParams.WRAP_CONTENT;p.x = 0;p.format = PixelFormat.TRANSLUCENT;p.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;p.flags |= WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM| WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;p.token = null;p.windowAnimations = 0; // android.R.style.DropDownAnimationDown;}private void updateFloatingWindowLayout() {int [] anchorPos = new int[2];mAnchor.getLocationOnScreen(anchorPos);WindowManager.LayoutParams p = mDecorLayoutParams;p.width = mAnchor.getWidth();p.y = anchorPos[1] + mAnchor.getHeight();}// This is called whenever mAnchor's layout bound changesprivate OnLayoutChangeListener mLayoutChangeListener =new OnLayoutChangeListener() {public void onLayoutChange(View v, int left, int top, int right,int bottom, int oldLeft, int oldTop, int oldRight,int oldBottom) {updateFloatingWindowLayout();if (mShowing) {mWindowManager.updateViewLayout(mDecor, mDecorLayoutParams);}}};private OnTouchListener mTouchListener = new OnTouchListener() {public boolean onTouch(View v, MotionEvent event) {if (event.getAction() == MotionEvent.ACTION_DOWN) {if (mShowing) {hide();}}return false;}};public void setMediaPlayer(MediaPlayerControl player) {mPlayer = player;updatePausePlay();}/*** Set the view that acts as the anchor for the control view.* This can for example be a VideoView, or your Activity's main view.* @param view The view to which to anchor the controller when it is visible.*/public void setAnchorView(View view) {if (mAnchor != null) {mAnchor.removeOnLayoutChangeListener(mLayoutChangeListener);}mAnchor = view;if (mAnchor != null) {mAnchor.addOnLayoutChangeListener(mLayoutChangeListener);}FrameLayout.LayoutParams frameParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT);removeAllViews();View v = makeControllerView();addView(v, frameParams);}/*** Create the view that holds the widgets that control playback.* Derived classes can override this to create their own.* @return The controller view.* @hide This doesn't work as advertised*/protected View makeControllerView() {LayoutInflater inflate = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);mRoot = inflate.inflate(R.layout.media_controller, null);initControllerView(mRoot);return mRoot;}private void initControllerView(View v) {mPauseButton = (ImageButton) v.findViewById(R.id.pause);if (mPauseButton != null) {mPauseButton.requestFocus();mPauseButton.setOnClickListener(mPauseListener);}mFfwdButton = (ImageButton) v.findViewById(R.id.ffwd);if (mFfwdButton != null) {Log.v("MediaController", "into the mFfwdButton.setOnClickListener");mFfwdButton.setOnClickListener(mFfwdListener);if (!mFromXml) {mFfwdButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);}}mRewButton = (ImageButton) v.findViewById(R.id.rew);if (mRewButton != null) {Log.v("MediaController", "into the mRewButton.setOnClickListener");mRewButton.setOnClickListener(mRewListener);if (!mFromXml) {mRewButton.setVisibility(mUseFastForward ? View.VISIBLE : View.GONE);}}// By default these are hidden. They will be enabled when setPrevNextListeners() is called mNextButton = (ImageButton) v.findViewById(R.id.next);if (mNextButton != null && !mFromXml && !mListenersSet) {mNextButton.setVisibility(View.GONE);}mPrevButton = (ImageButton) v.findViewById(R.id.prev);if (mPrevButton != null && !mFromXml && !mListenersSet) {mPrevButton.setVisibility(View.GONE);}mProgress = (ProgressBar) v.findViewById(R.id.mediacontroller_progress);if (mProgress != null) {if (mProgress instanceof SeekBar) {SeekBar seeker = (SeekBar) mProgress;seeker.setOnSeekBarChangeListener(mSeekListener);}mProgress.setMax(1000);}mEndTime = (TextView) v.findViewById(R.id.time);mCurrentTime = (TextView) v.findViewById(R.id.time_current);mFormatBuilder = new StringBuilder();mFormatter = new Formatter(mFormatBuilder, Locale.getDefault());installPrevNextListeners();}/*** Show the controller on screen. It will go away* automatically after 3 seconds of inactivity.*/public void show() {show(sDefaultTimeout);}/*** Disable pause or seek buttons if the stream cannot be paused or seeked.* This requires the control interface to be a MediaPlayerControlExt*/private void disableUnsupportedButtons() {try {if (mPauseButton != null && !mPlayer.canPause()) {mPauseButton.setEnabled(false);}if (mRewButton != null && !mPlayer.canSeekBackward()) {mRewButton.setEnabled(false);}if (mFfwdButton != null && !mPlayer.canSeekForward()) {mFfwdButton.setEnabled(false);}} catch (IncompatibleClassChangeError ex) {}}/*** Show the controller on screen. It will go away* automatically after 'timeout' milliseconds of inactivity.* @param timeout The timeout in milliseconds. Use 0 to show* the controller until hide() is called.*/public void show(int timeout) {if (!mShowing && mAnchor != null) {setProgress();if (mPauseButton != null) {mPauseButton.requestFocus();}disableUnsupportedButtons();updateFloatingWindowLayout();mWindowManager.addView(mDecor, mDecorLayoutParams);mShowing = true;}updatePausePlay();mHandler.sendEmptyMessage(SHOW_PROGRESS);Message msg = mHandler.obtainMessage(FADE_OUT);if (timeout != 0) {mHandler.removeMessages(FADE_OUT);mHandler.sendMessageDelayed(msg, timeout);}}public boolean isShowing() {return mShowing;}/*** Remove the controller from the screen.*/public void hide() {if (mAnchor == null)return;if (mShowing) {try {mHandler.removeMessages(SHOW_PROGRESS);mWindowManager.removeView(mDecor);} catch (IllegalArgumentException ex) {Log.w("MediaController", "already removed");}mShowing = false;}}private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {int pos;switch (msg.what) {case FADE_OUT:hide();break;case SHOW_PROGRESS:pos = setProgress();if (!mDragging && mShowing && mPlayer.isPlaying()) {msg = obtainMessage(SHOW_PROGRESS);sendMessageDelayed(msg, 1000 - (pos % 1000));}break;}}};private String stringForTime(int timeMs) {int totalSeconds = timeMs / 1000;int seconds = totalSeconds % 60;int minutes = (totalSeconds / 60) % 60;int hours = totalSeconds / 3600;mFormatBuilder.setLength(0);if (hours > 0) {return mFormatter.format("%d:%02d:%02d", hours, minutes, seconds).toString();} else {return mFormatter.format("%02d:%02d", minutes, seconds).toString();}}private int setProgress() {if (mPlayer == null || mDragging) {return 0;}int position = mPlayer.getCurrentPosition();int duration = mPlayer.getDuration();if (mProgress != null) {if (duration > 0) {// use long to avoid overflowlong pos = 1000L * position / duration;mProgress.setProgress( (int) pos);}int percent = mPlayer.getBufferPercentage();mProgress.setSecondaryProgress(percent * 10);}if (mEndTime != null)mEndTime.setText(stringForTime(duration));if (mCurrentTime != null)mCurrentTime.setText(stringForTime(position));return position;}@Overridepublic boolean onTouchEvent(MotionEvent event) {show(sDefaultTimeout);return true;}@Overridepublic boolean onTrackballEvent(MotionEvent ev) {show(sDefaultTimeout);return false;}@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {int keyCode = event.getKeyCode();final boolean uniqueDown = event.getRepeatCount() == 0&& event.getAction() == KeyEvent.ACTION_DOWN;if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK|| keyCode == KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE|| keyCode == KeyEvent.KEYCODE_SPACE) {if (uniqueDown) {doPauseResume();show(sDefaultTimeout);if (mPauseButton != null) {mPauseButton.requestFocus();}}return true;} else if (keyCode == KeyEvent.KEYCODE_MEDIA_PLAY) {if (uniqueDown && !mPlayer.isPlaying()) {mPlayer.start();updatePausePlay();show(sDefaultTimeout);}return true;} else if (keyCode == KeyEvent.KEYCODE_MEDIA_STOP|| keyCode == KeyEvent.KEYCODE_MEDIA_PAUSE) {if (uniqueDown && mPlayer.isPlaying()) {mPlayer.pause();updatePausePlay();show(sDefaultTimeout);}return true;} else if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN|| keyCode == KeyEvent.KEYCODE_VOLUME_UP|| keyCode == KeyEvent.KEYCODE_VOLUME_MUTE|| keyCode == KeyEvent.KEYCODE_CAMERA) {// don't show the controls for volume adjustmentreturn super.dispatchKeyEvent(event);} else if (keyCode == KeyEvent.KEYCODE_BACK || keyCode == KeyEvent.KEYCODE_MENU) {if (uniqueDown) {hide();}return true;}show(sDefaultTimeout);return super.dispatchKeyEvent(event);}private View.OnClickListener mPauseListener = new View.OnClickListener() {public void onClick(View v) {Log.v("MediaController", "into the mPauseListener");doPauseResume();show(sDefaultTimeout);}};private void updatePausePlay() {if (mRoot == null || mPauseButton == null)return;if (mPlayer.isPlaying()) {mPauseButton.setImageResource(R.drawable.media_pause);} else {mPauseButton.setImageResource(R.drawable.media_play);}}private void doPauseResume() {if (mPlayer.isPlaying()) {mPlayer.pause();} else {mPlayer.start();}updatePausePlay();}private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {public void onStartTrackingTouch(SeekBar bar) {show(3600000);mDragging = true;mHandler.removeMessages(SHOW_PROGRESS);}public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {if (!fromuser) {// We're not interested in programmatically generated changes to// the progress bar's position.return;}long duration = mPlayer.getDuration();long newposition = (duration * progress) / 1000L;mPlayer.seekTo( (int) newposition);if (mCurrentTime != null)mCurrentTime.setText(stringForTime( (int) newposition));}public void onStopTrackingTouch(SeekBar bar) {mDragging = false;setProgress();updatePausePlay();show(sDefaultTimeout);mHandler.sendEmptyMessage(SHOW_PROGRESS);}};@Overridepublic void setEnabled(boolean enabled) {if (mPauseButton != null) {mPauseButton.setEnabled(enabled);}if (mFfwdButton != null) {mFfwdButton.setEnabled(enabled);}if (mRewButton != null) {mRewButton.setEnabled(enabled);}if (mNextButton != null) {mNextButton.setEnabled(enabled && mNextListener != null);}if (mPrevButton != null) {mPrevButton.setEnabled(enabled && mPrevListener != null);}if (mProgress != null) {mProgress.setEnabled(enabled);}disableUnsupportedButtons();super.setEnabled(enabled);}@Overridepublic void onInitializeAccessibilityEvent(AccessibilityEvent event) {super.onInitializeAccessibilityEvent(event);event.setClassName(MediaController.class.getName());}@Overridepublic void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {super.onInitializeAccessibilityNodeInfo(info);info.setClassName(MediaController.class.getName());}private View.OnClickListener mRewListener = new View.OnClickListener() {public void onClick(View v) {Log.v("MediaController", "into the mRew button");int pos = mPlayer.getCurrentPosition();pos -= 5000; // millisecondsmPlayer.seekTo(pos);setProgress();show(sDefaultTimeout);}};private View.OnClickListener mFfwdListener = new View.OnClickListener() {public void onClick(View v) {Log.v("MediaController", "into the mFfw button");int pos = mPlayer.getCurrentPosition();pos += 15000; // millisecondsmPlayer.seekTo(pos);setProgress();show(sDefaultTimeout);}};private void installPrevNextListeners() {if (mNextButton != null) {mNextButton.setOnClickListener(mNextListener);mNextButton.setEnabled(mNextListener != null);}if (mPrevButton != null) {mPrevButton.setOnClickListener(mPrevListener);mPrevButton.setEnabled(mPrevListener != null);}}public void setPrevNextListeners(View.OnClickListener next, View.OnClickListener prev) {mNextListener = next;mPrevListener = prev;mListenersSet = true;if (mRoot != null) {installPrevNextListeners();if (mNextButton != null && !mFromXml) {mNextButton.setVisibility(View.VISIBLE);}if (mPrevButton != null && !mFromXml) {mPrevButton.setVisibility(View.VISIBLE);}}}public interface MediaPlayerControl {void start();void pause();int getDuration();int getCurrentPosition();void seekTo(int pos);boolean isPlaying();int getBufferPercentage();boolean canPause();boolean canSeekBackward();boolean canSeekForward();}
}
直接应用源代码会提示很多错误,比如资源找不到等,其中有一个layout.media_controller文件,布局如下
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="#CC000000"android:orientation="vertical"><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:gravity="center"android:paddingTop="4dip"android:orientation="horizontal"><ImageButton android:id="@+id/prev" style="@style/MediaButton.Previous" /><ImageButton android:id="@+id/rew" style="@style/MediaButton.Rew" /><ImageButton android:id="@+id/pause" style="@style/MediaButton.Play" /><ImageButton android:id="@+id/ffwd" style="@style/MediaButton.Ffwd" /><ImageButton android:id="@+id/next" style="@style/MediaButton.Next" /> </LinearLayout><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"><TextView android:id="@+id/time_current"android:textSize="14sp"android:textStyle="bold"android:paddingTop="4dip"android:paddingStart="4dip"android:layout_gravity="center_horizontal"android:layout_width="wrap_content"android:layout_height="wrap_content"android:paddingEnd="4dip"android:textColor="@color/dim_foreground_dark" /><SeekBarandroid:id="@+id/mediacontroller_progress"style="?android:attr/progressBarStyleHorizontal"android:layout_width="0dip"android:layout_weight="1"android:layout_height="32dip"android:layout_alignParentStart="true"android:layout_alignParentEnd="true" /><TextView android:id="@+id/time"android:textSize="14sp"android:textStyle="bold"android:paddingTop="4dip"android:paddingEnd="4dip"android:layout_gravity="center_horizontal"android:layout_width="wrap_content"android:layout_height="wrap_content"android:paddingStart="4dip"android:textColor="@color/dim_foreground_dark" /></LinearLayout></LinearLayout>
其中有一些style风格如下:
<style name="MediaButton"><item name="android:background">@null</item><item name="android:layout_width">71dip</item><item name="android:layout_height">52dip</item></style><style name="MediaButton.Previous"><item name="android:src">@android:drawable/ic_media_previous</item></style><style name="MediaButton.Next"><item name="android:src">@android:drawable/ic_media_next</item></style><style name="MediaButton.Play"><item name="android:src">@android:drawable/ic_media_play</item></style><style name="MediaButton.Ffwd"><item name="android:src">@android:drawable/ic_media_ff</item></style><style name="MediaButton.Rew"><item name="android:src">@android:drawable/ic_media_rew</item></style><style name="MediaButton.Pause"><item name="android:src">@android:drawable/ic_media_pause</item></style>
可以根据其中的UI更换成自己的风格。
最棘手的是下面几段代码:
mWindowManager = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);mWindow = PolicyManager.makeNewWindow(mContext);mWindow.setWindowManager(mWindowManager, null, null);mWindow.requestFeature(Window.FEATURE_NO_TITLE);mDecor = mWindow.getDecorView();mDecor.setOnTouchListener(mTouchListener);mWindow.setContentView(this);mWindow.setBackgroundDrawableResource(android.R.color.transparent);
PolicyManager.makeNewWindow是一个内部类,用于新建一个window窗口,该类的包为:
import com.android.internal.policy.PolicyManager;
这个包为android的core层使用的,应用程序无法调用。由于对这些函数不胜了解,到现在我任然没有搞懂这段代码用什么替换,如果有哪位大神知道,可以告知我,本人不胜感激。
我这地方用了另一种方法搞定编译,PolicyManager.makeNewWindow这个函数android内部是有的,位于system/framwork/framwork.odex中,但是在eclipse中的android的jar包中找不到这个类,所以一种方法是将这个类的jar包直接加入到工程当中,经过网络搜索,捣鼓一通以后重新得到了一个包含com.android.internal的jar包。
下载地址:
http://download.csdn.net/detail/taoxugang2012/6509377
由于时间倡促,技术不够强硬,这个东西暂时就这么做,严格上来说,应该用应用层的接口方法来替换该部分代码。
到此,有了自定义的videoview和自定义MediaController,那么一个播放器也就差不多了,比起自己重零做起,android的这两个类自然有它的好处,使用的人可以根据这个类修改其中的UI界面,对其它部分进行优化。
现在就这么做吧,等我技术知识储备好了,再重新写一个更完美的。
这篇关于关于MediaController的自定义的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!