项目要实现一个类似视频播放器控制界面的效果,不过比他简单些,显示有三个部分组成:最上面进行时间和下面的两个控制按钮,点击后屏幕显示,过几秒自动消失。
首先想到的就是前几天刚看的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);
这样问题就解决了。
上面的问题虽然坚决了,却不知道问题出在哪,为什么解决,今天无意中看到一篇文章(来自:wangjinyu501),又拿过来研究了下,
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.
animation | The next animation, or null. |
---|
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(); }