需求:
项目要实现一个类似视频播放器控制界面的效果,不过比他简单些,显示有三个部分组成:最上面进行时间和下面的两个控制按钮,点击后屏幕显示,过几秒自动消失。
首先想到的就是前几天刚看的MediaController,所以就把MediaController拿过来简单的看了下,想貌似没有它复杂,赶进度要紧,自己简单实现就好,最后效果如下:
实现思路:
自定义一个view,上面是一个显示时间的文本,设下透明度;下面用一个FrameLayout,先放纵向放两个线性布局在最底层实现下面白条效果,然后把两个文本按钮加在上层;最后在稍微加点进入和退出的动画效果(这个地方出了点粗心的问题,下面提到)。
实现:
首先是自定义view:
public class VideoControlPanel extends FrameLayout implements IVideoControlPanel, AnimationListener, OnTouchListener,代码比较简单,所以也就没写注释。先加载xml中定义的布局,然后处理touch事件,最后用一个handle处理界面的消失。下面是布局文件:
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 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 1Sets 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. |
---|
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();
}