需求:
项目要实现一个类似视频播放器控制界面的效果,不过比他简单些,显示有三个部分组成:最上面进行时间和下面的两个控制按钮,点击后屏幕显示,过几秒自动消失。
首先想到的就是前几天刚看的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 voidsetAnimation(Animationanimation)
Sets the next animation to play for this view. If you want the animation to play immediately, usestartAnimation(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. |
---|
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();
}