转自:http://blog.csdn.net/huweigoodboy/article/details/42156209
看到天天动听的播放界面,可以从底部划出来,被其效果惊艳到,于是想自己动手模仿。
效果:1,在Content未展开的状态(隐藏):
1>点击Handler控件,弹出Content。
2>拖动Handler,Content会从底部逐渐出来。
2,在Content展开的状态:
拖动Content,content位置随着手指的滑动而产生位置变化。
最开始想到的是用SlidingDraw,因为有几分相似,SlidingDraw有一个handle,拖着handle让content跟着移动。
区别在于在我们要设计的控件handle是不动的,监听到handle的touch事件,只能让content去移动。
要自定义一个控件,首先一定要清楚一下几个方法,onMeasure(),用于测量view的大小,onLayout()用于放置view的位置,onDraw()用于绘制控件,onDispatchDraw()一般在这里绘制子控件。这里我们把handle放在最底部,content位于handle下面,也就是屏幕外面。
实现思路:
1,Content关闭状态,touch事件的监听应该在Handler上,也就是说父控件不能去拦截,在onInterceptTouchEvent()中返回false,handler控件去消费onTouch事件。这里用了一个手势辅助类GestureDetector,其实我们要用到只是滑动手势和点击,所以这里不用这个辅助类也可以自己实现。
2,Content展开状态,touch事件的监听应该放在ParentView上,也就是说监听事件是全屏的。(一开始我犯了一个错误,将监听事件放到content上,也就是content去监听滑动,然后移动自己,这样会由于移动content自身后,监听位置更新不及时而发生抖动现象)。
3,当拖动content到屏幕中间而放手,应该去调整content的位置,播放从当前位置到展开位置或者关闭位置的动画。
由于代码比较简单,关于自定义控件的事件监听以及重要的几个方法这里不多啰嗦了。
SlidingPanel.java
- package com.example.huwei.slidingpaneldemo.ui;
- import android.content.Context;
- import android.graphics.Canvas;
- import android.graphics.Rect;
- import android.os.Handler;
- import android.util.AttributeSet;
- import android.util.Log;
- import android.view.GestureDetector;
- import android.view.Gravity;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.animation.AccelerateInterpolator;
- import android.view.animation.Animation;
- import android.view.animation.TranslateAnimation;
- import android.widget.LinearLayout;
- import android.widget.Scroller;
- import com.example.huwei.slidingpaneldemo.R;
- /**
- * Created by huwei on 14-12-19.
- */
- public class SlidingPanel extends LinearLayout implements GestureDetector.OnGestureListener {
- private static final String TAG = "SlidingPanel";
- private static final String GTAG = "GTAG"; //手势TAG
- private View mHandle;
- private View mContent;
- private int mContentRangeTop; //content在父布局的移动范围
- private int mContentRangeBottom;
- private int mDownY; //ACTION_DOWN时y的坐标
- private boolean mExpanded=false; //是否展开
- private static final int ANIMATION_DURATION = 1000; // 从底部到上面需要1s
- private GestureDetector mGestureDetector; //检测手势辅助类
- public SlidingPanel(Context context) {
- this(context, null);
- }
- public SlidingPanel(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
- public SlidingPanel(Context context, AttributeSet attrs, int defStyleAttr) {
- super(context, attrs, defStyleAttr);
- mGestureDetector = new GestureDetector(context, this);
- setOrientation(LinearLayout.VERTICAL);
- }
- @Override
- protected void onFinishInflate() {
- mHandle = findViewById(R.id.handle);
- if (mHandle == null) {
- throw new IllegalArgumentException("The handle attribute is required and must refer "
- + "to a valid child.");
- }
- mContent = findViewById(R.id.content);
- if (mContent == null) {
- throw new IllegalArgumentException("The content attribute is required and must refer "
- + "to a valid child.");
- }
- Log.i(TAG, "***********handle:" + mHandle + "************content:" + mContent + "**********");
- mHandle.setClickable(true);
- mHandle.setOnTouchListener(touchListener);
- }
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
- int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
- View handle = mHandle;
- measureChild(handle, widthMeasureSpec, heightMeasureSpec);
- int height = (int) heightSpecSize;
- measureChild(mContent, MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
- setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
- }
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {
- super.onLayout(changed, l, t, r, b);
- //获取自身的宽高
- final int width = r - l;
- final int height = b - t;
- mContentRangeTop=0;
- mContentRangeBottom = b - t;
- final View handle = mHandle;
- int childHeight = handle.getMeasuredHeight();
- int childWidth = handle.getMeasuredWidth();
- handle.layout(0, height - childHeight, childWidth, height);
- final View content = mContent;
- if (!mExpanded) {
- mContent.layout(0, mContentRangeBottom, content.getMeasuredWidth(), mContentRangeBottom + content.getMeasuredHeight());
- } else {
- mContent.layout(0, mContentRangeTop, content.getMeasuredWidth(), mContentRangeTop + content.getMeasuredHeight());
- }
- }
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- long drawingTime = getDrawingTime();
- final View handle = mHandle;
- drawChild(canvas, handle, drawingTime);
- final View content = mContent;
- drawChild(canvas, content, drawingTime);
- }
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev) {
- return mExpanded;
- }
- @Override
- public boolean onTouchEvent(MotionEvent event) {
- switch (event.getAction()){
- case MotionEvent.ACTION_DOWN:
- mDownY= (int)event.getY();
- break;
- case MotionEvent.ACTION_MOVE:
- int nowY=(int)event.getY();
- moveContent(nowY-mDownY);
- break;
- case MotionEvent.ACTION_UP:
- adjustContentView();
- break;
- }
- return mExpanded;
- }
- /**
- * 停止滑动
- *
- * @param
- */
- private void stopTracking() {
- //判断content是展开还是收缩
- updateExpanded();
- }
- /**
- * 更新mExpanded状态
- */
- private void updateExpanded(){
- if (mContent.getTop() <= mContentRangeTop) {
- mExpanded = true;
- } else {
- mExpanded = false;
- }
- }
- /**
- * move content到指定位置
- *
- * @param position
- */
- private void moveContent(int position) {
- Log.i(GTAG, "*********move Content:position" + position + "**********");
- //不能移出上边界
- if (position < mContentRangeTop) {
- position = mContentRangeTop;
- } else if (position > mContentRangeBottom) {
- position = mContentRangeBottom;
- }
- final View content=mContent;
- final int top = (int) mContent.getY();
- final int deltaY = position - top;
- content.layout(0,position,content.getWidth(),position+content.getHeight());
- }
- //移动Content
- private void doAnimation(int nowY, final int targetY) {
- AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();
- //BounceInterpolator bounceInterpolator=new BounceInterpolator();
- TranslateAnimation animation = new TranslateAnimation(0, 0, 0, targetY - nowY);
- animation.setDuration(ANIMATION_DURATION * Math.abs(targetY - nowY) / mContentRangeBottom);
- animation.setInterpolator(accelerateInterpolator);
- animation.setAnimationListener(new Animation.AnimationListener() {
- @Override
- public void onAnimationStart(Animation animation) {
- }
- @Override
- public void onAnimationEnd(Animation animation) {
- //Log.i(TAG,"onAnimationEnd");
- mContent.clearAnimation();
- mContent.layout(0, targetY, mContent.getMeasuredWidth(), targetY + mContent.getMeasuredHeight());
- stopTracking();
- }
- @Override
- public void onAnimationRepeat(Animation animation) {
- }
- });
- mContent.startAnimation(animation);
- }
- /**
- * 点击时打开Content*
- */
- private void open() {
- doAnimation(mContentRangeBottom, 0);
- stopTracking();
- }
- /**
- * ACTION_UP时 contentView不是停靠在屏幕边缘(在屏幕中间)时,调整contentView的位置*
- */
- private void adjustContentView(){
- final int top = mContent.getTop();
- //切割父容器,分成3等份
- final int perRange=(mContentRangeBottom-mContentRangeTop)/3;
- if(mExpanded){
- //小于1/3
- if(top<perRange+mContentRangeTop){
- doAnimation(top,0);
- }else{
- doAnimation(top, mContentRangeBottom);
- }
- }else{
- //小于2/3
- if(top<mContentRangeTop+perRange*2){
- doAnimation(top,0);
- }else{
- doAnimation(top,mContentRangeBottom);
- }
- }
- }
- /**
- *按下时触发*
- * @param e
- * @return
- */
- @Override
- public boolean onDown(MotionEvent e) {
- Log.i(GTAG, "onDown:"+e.getY());
- return false;
- }
- @Override
- public void onShowPress(MotionEvent e) {
- Log.i(GTAG, "onShowPress");
- }
- /**
- * 轻轻点击*
- * @param e
- * @return
- */
- @Override
- public boolean onSingleTapUp(MotionEvent e) {
- Log.i(GTAG, "onSingleTapUp");
- if (!mExpanded) {
- open();
- }
- return false;
- }
- /**
- * 滚动时出发*
- * @param e1
- * @param e2
- * @param distanceX
- * @param distanceY
- * @return
- */
- @Override
- public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
- int touchY = (int) e2.getY();
- Log.i(GTAG, "onScroll:"+mContent.getY());
- moveContent(touchY - mDownY + (mExpanded ? mContentRangeTop : mContentRangeBottom));
- return false;
- }
- /**
- * 长按*
- * @param e
- */
- @Override
- public void onLongPress(MotionEvent e) {
- Log.i(GTAG, "onLongPress");
- }
- /**
- * 瞬时滑动*
- * @param e1
- * @param e2
- * @param velocityX
- * @param velocityY
- * @return
- */
- @Override
- public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
- Log.i(GTAG, "onFling");
- return false;
- }
- OnTouchListener touchListener = new OnTouchListener() {
- @Override
- public boolean onTouch(View v, MotionEvent event) {
- final View handle = mHandle;
- if (event.getAction() == MotionEvent.ACTION_DOWN) {
- final int top = handle.getTop();
- mDownY = (int) event.getY();
- Log.i(GTAG,"mDownY:"+mDownY);
- } else if (event.getAction() == MotionEvent.ACTION_UP) {
- adjustContentView();
- }
- Log.i(GTAG, "onTouch:" + event.getAction() + " y:" + event.getY());
- return mGestureDetector.onTouchEvent(event);
- }
- };
- }
xml
- <com.example.huwei.slidingpaneldemo.ui.SlidingPanel xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- <LinearLayout
- android:id="@+id/handle"
- android:orientation="horizontal"
- android:layout_width="fill_parent"
- android:layout_height="68dp"
- android:background="@drawable/handle_seletor">
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
- <LinearLayout
- android:orientation="vertical"
- android:id="@+id/content"
- android:background="#78ADFF2F"
- android:layout_width="match_parent"
- android:layout_height="match_parent" >
- <Button
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
- </LinearLayout>
- </com.example.huwei.slidingpaneldemo.ui.SlidingPanel>