关闭

模拟视频播放器控制界面时动画效果出现的问题

1443人阅读 评论(0) 收藏 举报
分类:

需求:

项目要实现一个类似视频播放器控制界面的效果,不过比他简单些,显示有三个部分组成:最上面进行时间和下面的两个控制按钮,点击后屏幕显示,过几秒自动消失。

首先想到的就是前几天刚看的MediaController,所以就把MediaController拿过来简单的看了下,想貌似没有它复杂,赶进度要紧,自己简单实现就好,最后效果如下:



实现思路:

自定义一个view,上面是一个显示时间的文本,设下透明度;下面用一个FrameLayout,先放纵向放两个线性布局在最底层实现下面白条效果,然后把两个文本按钮加在上层;最后在稍微加点进入和退出的动画效果(这个地方出了点粗心的问题,下面提到)。


实现:

首先是自定义view:

public class VideoControlPanel extends FrameLayout implements IVideoControlPanel, AnimationListener, OnTouchListener,
		Runnable {
	private static final int DELAYMILLIS = 3 * 1000;
	private static final int DURATION = 300;

	private boolean isShowing = false;

	private Animation topEnter, topExit, buttomEnter, buttomExit;

	private View mRoot;
	private TextView tvTitle;
	private LinearLayout llTitle, llChange, llHangup;
	private FrameLayout flOperate;

	private Handler handler = new Handler();

	public VideoControlPanel(Context context) {
		super(context);
		initControllerPanel();
	}

	public VideoControlPanel(Context context, AttributeSet attrs) {
		super(context, attrs);
		initControllerPanel();
	}

	private void initControllerPanel() {
		initOther();
		initView();
	}

	private void initView() {

		LayoutInflater inflate = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		mRoot = inflate.inflate(R.layout.media_controller, null);

		llTitle = (LinearLayout) mRoot.findViewById(R.id.llTitle);
		flOperate = (FrameLayout) mRoot.findViewById(R.id.flOperate);

		tvTitle = (TextView) mRoot.findViewById(R.id.tvTitle);
		llHangup = (LinearLayout) mRoot.findViewById(R.id.llHangup);
		llChange = (LinearLayout) mRoot.findViewById(R.id.llChange);

		setOnTouchListener(this);

		addView(mRoot);

		show();
	}

	private void initOther() {
		topEnter = new TranslateAnimation(0, 0, -100, 0);
		topEnter.setDuration(DURATION);

		topExit = new TranslateAnimation(0, 0, 0, -100);
		topExit.setDuration(DURATION);
		topExit.setAnimationListener(this);

		buttomEnter = new TranslateAnimation(0, 0, 300, 0);
		buttomEnter.setDuration(DURATION);

		buttomExit = new TranslateAnimation(0, 0, 0, 100);
		buttomExit.setDuration(DURATION);
	}

	@Override
	public void onAnimationStart(Animation animation) {

	}

	@Override
	public void onAnimationEnd(Animation animation) {
		if (mRoot != null) {
			mRoot.setVisibility(View.GONE);
		}
	}

	@Override
	public void onAnimationRepeat(Animation animation) {

	}

	@Override
	public void hide() {
		isShowing = false;
		llTitle.startAnimation(topExit);
		flOperate.startAnimation(buttomExit);
	}

	@Override
	public void show() {
		if (!isShowing) {
			mRoot.setVisibility(View.VISIBLE);
			isShowing = true;

			llTitle.startAnimation(topEnter);

			flOperate.setAnimation(buttomEnter);
		}
		handler.removeCallbacks(this);
		handler.postDelayed(this, DELAYMILLIS);
	}

	@Override
	public void run() {
		hide();
	}

	@Override
	public void setTitle(String title) {
		if (tvTitle != null) {
			tvTitle.setText(title);
		}
	}

	@Override
	public View getHangupView() {
		return llHangup;
	}

	@Override
	public View getChangeView() {
		return llChange;
	}

	@Override
	public void setOnClickListener(OnClickListener listener) {
		// super.setOnClickListener(l);
		if (llHangup != null) {
			llHangup.setOnClickListener(listener);
		}
		if (llChange != null) {
			llChange.setOnClickListener(listener);
		}
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		show();
		return false;
	}
}

interface IVideoControlPanel {

	/**
	 * 隐藏操作面板
	 */
	public void hide();

	/**
	 * 显示操作面板
	 */
	public void show();

	/**
	 * 设置标题
	 * 
	 * @param title
	 */
	public void setTitle(String title);

	/**
	 * 获取挂断的View
	 * 
	 * @return
	 */
	public View getHangupView();

	/**
	 * 获取切换的View
	 * 
	 * @return
	 */
	public View getChangeView();
}
代码比较简单,所以也就没写注释。先加载xml中定义的布局,然后处理touch事件,最后用一个handle处理界面的消失。下面是布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:id="@+id/llTitle"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:background="#88FFFFFF"
        android:gravity="center"
        android:orientation="horizontal"
        android:padding="8dip" >

        <TextView
            android:id="@+id/tvTitle"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_horizontal"
            android:text="通话时间:15分27秒"
            android:textSize="14sp" />
    </LinearLayout>

    <FrameLayout
        android:id="@+id/flOperate"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:orientation="horizontal" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="40dp"
                android:src="#00FFFFFF" />

            <ImageView
                android:layout_width="match_parent"
                android:layout_height="40dp"
                android:src="#88FFFFFF" />
        </LinearLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="12dp" >

            <LinearLayout
                android:id="@+id/llHangup"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_marginLeft="42dp"
                android:orientation="vertical" >

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:drawablePadding="4dp"
                    android:drawableTop="@drawable/ic_hangup"
                    android:gravity="center"
                    android:text="挂断" />
            </LinearLayout>

            <LinearLayout
                android:id="@+id/llChange"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_marginRight="42dp"
                android:orientation="vertical" >

                <TextView
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:drawablePadding="4dp"
                    android:drawableTop="@drawable/ic_change"
                    android:gravity="center"
                    android:text="切换" />
            </LinearLayout>
        </RelativeLayout>
    </FrameLayout>

</RelativeLayout>
这个布局的实现思路如上所述。然后就可以使用了,在需要的地方做如下引用:
<com.ttdevs.customcontrolpanel.VideoControlPanel
        android:id="@+id/vcpPanel"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignParentTop="true"
        android:background="@drawable/bg" />
这样就完成了所需效果。

遇到的问题:

在弄动画的时候,出了点问题,折腾了好久,现象为:底部的按钮出现的动画只有程序第一次启动的时候有效,之后就再没效果了。由于对动画不熟悉,也不知道怪什么,开始是像上面那么写的,最后实在没办法做如下修改,即每次都创建buttomEnter,竟然解决问题了:

	@Override
	public void show() {
		if (!isShowing) {
			mRoot.setVisibility(View.VISIBLE);
			isShowing = true;

			llTitle.startAnimation(topEnter);

			buttomEnter = new TranslateAnimation(0, 0, 300, 0);
			buttomEnter.setDuration(DURATION);
			flOperate.setAnimation(buttomEnter);
		}
		handler.removeCallbacks(this);
		handler.postDelayed(this, DELAYMILLIS);
	}

虽然效果实现了,但是一直纠结这个动画问题出在哪里。今天整理的时候无意中发现竟然是自己调用有问题导致的:

flOperate.setAnimation(buttomEnter);应该改成:flOperate.startAnimation(buttomEnter);

这样问题就解决了。


最新修改:2013-12-09

上面的问题虽然坚决了,却不知道问题出在哪,为什么解决,今天无意中看到一篇文章(来自:wangjinyu501),又拿过来研究了下,

public void setAnimation (Animation animation)

Added in API level 1

Sets the next animation to play for this view. If you want the animation to play immediately, use startAnimation(android.view.animation.Animation) instead. This method provides allows fine-grained control over the start time and invalidation, but you must make sure that 1) the animation has a start time set, and 2) the view's parent (which controls animations on its children) will be invalidated when the animation is supposed to start.

Parameters
animation The next animation, or null.
其中提到,想立即启动动画可以使用startAnimation(Animation)。setAnimation允许细粒度的控制(动画的)启动时间和失效,但是这样必须满足两个条件1)设置了动画启动时间,2)执行动画的组件的父视图在动画启动之前需要刷新界面(从wangjinyu501引用来的)。分析下,第一次动画出问题应该是没有设置动画的启动时间,因此在setAnimation()之前加上:

buttomEnter.setStartTime(Animation.START_ON_FIRST_FRAME);
这样即可。那为什么每次都创建就可以播放动画呢,有兴趣可以看看setStartTime的源码或者打印下buttomEnter.hasStarted()的值。


去掉了布局文件后代码:

public class VideoControlPanel extends FrameLayout implements IVideoControlPanel, AnimationListener, OnTouchListener,
		Runnable {
	private static final int DELAYMILLIS = 3 * 1000; // 显示延时
	private static final int DURATION = 300; // 动画持续

	private boolean isShowing = false;

	private Animation topEnter, topExit, buttomEnter, buttomExit;
	private View mRoot;
	private TextView tvTitle;
	private RelativeLayout rlParent, rlOperate;
	private LinearLayout llTitle, llChange, llHangup, llOperateBg;
	private FrameLayout flOperate;

	private Handler handler = new Handler();

	public VideoControlPanel(Context context) {
		super(context);
		initControllerPanel();
	}

	public VideoControlPanel(Context context, AttributeSet attrs) {
		super(context, attrs);
		initControllerPanel();
	}

	private void initControllerPanel() {
		initOther();
		initView();
	}

	private void initView() {
		mRoot = initCustomView();

		setOnTouchListener(this);

		addView(mRoot);

		show();
	}

	private View initCustomView() {
		rlParent = new RelativeLayout(getContext());
		rlParent.setLayoutParams(new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

		// 上部分
		llTitle = new LinearLayout(getContext());
		llTitle.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, 60));
		llTitle.setOrientation(LinearLayout.VERTICAL);
		llTitle.setBackgroundColor(Color.parseColor("#90FFFFFF")); // TODO
		llTitle.setGravity(Gravity.CENTER);
		tvTitle = new TextView(getContext());
		tvTitle.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
		tvTitle.setGravity(Gravity.CENTER);
		tvTitle.setTextSize(18);
		llTitle.addView(tvTitle);

		RelativeLayout.LayoutParams rlpTop = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
				RelativeLayout.LayoutParams.WRAP_CONTENT);
		rlpTop.addRule(RelativeLayout.ALIGN_PARENT_TOP);
		rlParent.addView(llTitle, rlpTop);

		// 下部分
		flOperate = new FrameLayout(getContext());
		flOperate.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
				FrameLayout.LayoutParams.WRAP_CONTENT));

		// 下部分背景
		llOperateBg = new LinearLayout(getContext());
		llOperateBg.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
				LinearLayout.LayoutParams.WRAP_CONTENT));
		llOperateBg.setOrientation(LinearLayout.VERTICAL);
		addBackgroundView("#00FFFFFF");
		addBackgroundView("#90FFFFFF");
		flOperate.addView(llOperateBg);

		// 下部分按钮
		rlOperate = new RelativeLayout(getContext());
		rlOperate.setLayoutParams(new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
				RelativeLayout.LayoutParams.MATCH_PARENT));

		// 下部分按钮挂断
		llHangup = getButtomLayout("挂断", R.drawable.ic_hangup);
		addBottomView(true, llHangup);

		// 下部分按钮切换
		llChange = getButtomLayout("切换", R.drawable.ic_change);
		addBottomView(false, llChange);

		flOperate.addView(rlOperate);

		RelativeLayout.LayoutParams rlpBottom = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT,
				LayoutParams.WRAP_CONTENT);
		rlpBottom.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		rlParent.addView(flOperate, rlpBottom);
		return rlParent;
	}

	private void addBackgroundView(String color) {
		ImageView ivBg = new ImageView(getContext());
		ivBg.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 80));
		ivBg.setBackgroundColor(Color.parseColor(color));
		llOperateBg.addView(ivBg);
	}

	private LinearLayout getButtomLayout(String text, int drawId) {
		LinearLayout llChange = new LinearLayout(getContext());
		llChange.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
				LinearLayout.LayoutParams.WRAP_CONTENT));
		llChange.setOrientation(LinearLayout.VERTICAL);
		TextView tvChange = new TextView(getContext());
		tvChange.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
		tvChange.setText(text);
		tvChange.setTextSize(18);
		tvChange.setTextColor(Color.parseColor("#FF393939"));
		tvChange.setGravity(Gravity.CENTER);
		tvChange.setCompoundDrawablePadding(2);
		tvChange.setCompoundDrawablesWithIntrinsicBounds(null, getResources().getDrawable(drawId), null, null);
		llChange.addView(tvChange);
		return llChange;
	}

	private void addBottomView(boolean isLeft, LinearLayout view) {
		RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT);
		lp.addRule(RelativeLayout.CENTER_VERTICAL);
		if (isLeft) {
			lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
			lp.leftMargin = 88;
		} else {
			lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
			lp.rightMargin = 88;
		}
		rlOperate.addView(view, lp);
	}

	private void initOther() {
		topEnter = new TranslateAnimation(0, 0, -100, 0);
		topEnter.setDuration(DURATION);

		topExit = new TranslateAnimation(0, 0, 0, -100);
		topExit.setDuration(DURATION);
		topExit.setAnimationListener(this);

		buttomEnter = new TranslateAnimation(0, 0, 300, 0);
		buttomEnter.setDuration(DURATION);

		buttomExit = new TranslateAnimation(0, 0, 0, 100);
		buttomExit.setDuration(DURATION);
	}

	@Override
	public void onAnimationStart(Animation animation) {
		// do nothing
	}

	@Override
	public void onAnimationEnd(Animation animation) {
		if (mRoot != null) {
			mRoot.setVisibility(View.GONE);
		}
	}

	@Override
	public void onAnimationRepeat(Animation animation) {
		// do nothing
	}

	@Override
	public void hide() {
		isShowing = false;
		llTitle.startAnimation(topExit);
		flOperate.startAnimation(buttomExit);
	}

	@Override
	public void show() {
		if (!isShowing) {
			mRoot.setVisibility(View.VISIBLE);
			isShowing = true;

			llTitle.startAnimation(topEnter);
			
			flOperate.startAnimation(buttomEnter);
		}
		handler.removeCallbacks(this);
		handler.postDelayed(this, DELAYMILLIS);
	}

	@Override
	public void run() {
		hide();
	}

	@Override
	public void setTitle(String title) {
		if (tvTitle != null) {
			tvTitle.setText(title);
		}
	}

	@Override
	public View getHangupView() {
		return llHangup;
	}

	@Override
	public View getChangeView() {
		return llChange;
	}

	@Override
	public boolean onTouch(View v, MotionEvent event) {
		show();
		return false;
	}
}

interface IVideoControlPanel {

	/**
	 * 隐藏操作面板
	 */
	public void hide();

	/**
	 * 显示操作面板
	 */
	public void show();

	/**
	 * 设置标题
	 * 
	 * @param title
	 */
	public void setTitle(String title);

	/**
	 * 获取挂断的View
	 * 
	 * @return
	 */
	public View getHangupView();

	/**
	 * 获取切换的View
	 * 
	 * @return
	 */
	public View getChangeView();
}




0
0
查看评论

Android播放器菜单的显示隐藏动画效果

Android播放器菜单的显示隐藏动画效果
  • humorousz
  • humorousz
  • 2016-12-18 20:11
  • 725

EasyNVR H5无插件直播方案前端构建之:播放界面添加实时云台控制界面

如何在播放器上加一个云台控制界面问题:对于实时直播的视频播放, 由于播放页面客观样式要求(一个播放器占据了整个页面),因此很难找出很合理的空间来放置其他功能按钮的位置(比如配合实时的云平台控制界面); EasyNVR配置中设有ONVIF探测功能;因此需要在视频播放界面添加了云台控制界面来展示出...
  • Black_3717
  • Black_3717
  • 2017-08-29 17:06
  • 276

模拟视频播放器练习

模拟视频播放器练习
  • mumu42
  • mumu42
  • 2017-10-11 15:38
  • 56

Qt Creator 之mplayer播放器图形界面设计

利用mplayer为播放核心,编写自己的播放器界面(1) 前几天学习了mplayer的编译、 初步使用和方法学习 有需要的可以看我发过的子~xphyym 这个礼拜写了个播放器的界面,很粗糙! 理想界面为暴风影音那样~~...
  • fzf151
  • fzf151
  • 2012-07-12 18:59
  • 1621

常用数字及模拟视频接口

常用模拟接口: 1.VGA接口:以前我们最常用的接口,基本所有的计算机和显示器上都有这个接口,VGA(Video Graphics Array)即视频图形阵列,由于在数字显示器上显示需要经过数模转换,必然存在损耗,因此已经逐渐过渡到一些主流的数字接口。 2.TV接口:TV接口又称RF射频...
  • ldzgeneral
  • ldzgeneral
  • 2016-05-13 16:44
  • 1200

Android怎样实现控制第三方音乐播放器暂停、播放

1.需求 怎么控制第三方音乐播放器暂停 播放呢  2.解决思路 写一个服务,当第三方播放器打开时 ,开启这个服务,音乐暂停;关闭服务,音乐继续 3.开启和关闭服务 startService(new Intent(MainActivity.this,StartService.cla...
  • Evahuangchen
  • Evahuangchen
  • 2016-11-24 16:22
  • 2710

C#使界面产生动画效果

using System.Runtime.InteropServices;//动画效果命名空间 //动画效果类型选项 public const Int32 AW_HOR_POSITIVE = 0X00000001;//与NEGATIVE public const Int3...
  • sz_bdqn
  • sz_bdqn
  • 2008-07-25 11:52
  • 2086

一个实用的JS动画弹出层效果

动画弹出层 .list{ position:relative;; background:#eee; border:1px #ccc solid; margin:10px; height:30px; width:100px; cursor :pointer ; } .listS...
  • as375256234
  • as375256234
  • 2012-05-22 15:15
  • 1940

【视频处理】模拟视频与数字视频的区别

模拟视频与数字视频的区别 模拟视频是指每一帧图像是实时获取的自然景物的真实图像信号。我们在日常生活中看到的电视、电影都属于模拟视频的范畴。模拟视频信号具有成本低和还原性好等优点,视频画面往往会给人一种身临其境的感觉。 但它的最大缺点是不论被记录的图像信号有多好,经过长时间的存放之后,...
  • LG1259156776
  • LG1259156776
  • 2016-07-28 22:10
  • 1638

使用C#实现WinForm窗体的动画效果

using System.Runtime.InteropServices;  public class Win32  {   public const Int32 AW_HOR_POSITIVE = 0x00000001; // 从左到右打开窗口   public const Int32...
  • u012927285
  • u012927285
  • 2015-05-18 11:28
  • 800
    个人资料
    • 访问:284462次
    • 积分:3735
    • 等级:
    • 排名:第10256名
    • 原创:87篇
    • 转载:0篇
    • 译文:2篇
    • 评论:147条
    最新评论
    屏蔽广告