Android仿天天动态上拉播放界面控件

简介:

转自: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

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. package com.example.huwei.slidingpaneldemo.ui;  
  2.   
  3.   
  4. import android.content.Context;  
  5. import android.graphics.Canvas;  
  6. import android.graphics.Rect;  
  7. import android.os.Handler;  
  8. import android.util.AttributeSet;  
  9. import android.util.Log;  
  10. import android.view.GestureDetector;  
  11. import android.view.Gravity;  
  12. import android.view.MotionEvent;  
  13. import android.view.View;  
  14. import android.view.animation.AccelerateInterpolator;  
  15. import android.view.animation.Animation;  
  16. import android.view.animation.TranslateAnimation;  
  17. import android.widget.LinearLayout;  
  18. import android.widget.Scroller;  
  19.   
  20.   
  21.   
  22.   
  23. import com.example.huwei.slidingpaneldemo.R;  
  24.   
  25.   
  26.   
  27.   
  28.   
  29.   
  30.   
  31.   
  32. /** 
  33.  * Created by huwei on 14-12-19. 
  34.  */  
  35. public class SlidingPanel extends LinearLayout implements GestureDetector.OnGestureListener {  
  36.     private static final String TAG = "SlidingPanel";  
  37.     private static final String GTAG = "GTAG";    //手势TAG  
  38.   
  39.   
  40.     private View mHandle;  
  41.     private View mContent;  
  42.   
  43.   
  44.     private int mContentRangeTop;   //content在父布局的移动范围  
  45.     private int mContentRangeBottom;  
  46.   
  47.   
  48.     private int mDownY;     //ACTION_DOWN时y的坐标  
  49.     private boolean mExpanded=false;  //是否展开  
  50.   
  51.   
  52.   
  53.   
  54.     private static final int ANIMATION_DURATION = 1000;   //  从底部到上面需要1s  
  55.   
  56.   
  57.     private GestureDetector mGestureDetector;   //检测手势辅助类  
  58.   
  59.   
  60.   
  61.   
  62.   
  63.   
  64.     public SlidingPanel(Context context) {  
  65.         this(context, null);  
  66.     }  
  67.   
  68.   
  69.     public SlidingPanel(Context context, AttributeSet attrs) {  
  70.         this(context, attrs, 0);  
  71.     }  
  72.   
  73.   
  74.     public SlidingPanel(Context context, AttributeSet attrs, int defStyleAttr) {  
  75.         super(context, attrs, defStyleAttr);  
  76.   
  77.   
  78.         mGestureDetector = new GestureDetector(context, this);  
  79.         setOrientation(LinearLayout.VERTICAL);  
  80.     }  
  81.   
  82.   
  83.   
  84.   
  85.     @Override  
  86.     protected void onFinishInflate() {  
  87.         mHandle = findViewById(R.id.handle);  
  88.   
  89.   
  90.         if (mHandle == null) {  
  91.             throw new IllegalArgumentException("The handle attribute is required and must refer "  
  92.                     + "to a valid child.");  
  93.         }  
  94.   
  95.   
  96.         mContent =  findViewById(R.id.content);  
  97.   
  98.   
  99.         if (mContent == null) {  
  100.             throw new IllegalArgumentException("The content attribute is required and must refer "  
  101.                     + "to a valid child.");  
  102.         }  
  103.   
  104.   
  105.         Log.i(TAG, "***********handle:" + mHandle + "************content:" + mContent + "**********");  
  106.           
  107.         mHandle.setClickable(true);  
  108.         mHandle.setOnTouchListener(touchListener);  
  109.     }  
  110.   
  111.   
  112.   
  113.   
  114.     @Override  
  115.     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
  116.         super.onMeasure(widthMeasureSpec, heightMeasureSpec);  
  117.         int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);  
  118.   
  119.   
  120.         int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);  
  121.   
  122.   
  123.         View handle = mHandle;  
  124.   
  125.   
  126.         measureChild(handle, widthMeasureSpec, heightMeasureSpec);  
  127.   
  128.   
  129.         int height = (int) heightSpecSize;  
  130.         measureChild(mContent, MeasureSpec.makeMeasureSpec(widthSpecSize, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));  
  131.   
  132.   
  133.         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);  
  134.     }  
  135.   
  136.   
  137.     @Override  
  138.     protected void onLayout(boolean changed, int l, int t, int r, int b) {  
  139.         super.onLayout(changed, l, t, r, b);  
  140.   
  141.   
  142.   
  143.   
  144.         //获取自身的宽高  
  145.         final int width = r - l;  
  146.         final int height = b - t;  
  147.   
  148.   
  149.         mContentRangeTop=0;  
  150.         mContentRangeBottom = b - t;  
  151.   
  152.   
  153.         final View handle = mHandle;  
  154.         int childHeight = handle.getMeasuredHeight();  
  155.         int childWidth = handle.getMeasuredWidth();  
  156.   
  157.   
  158.         handle.layout(0, height - childHeight, childWidth, height);  
  159.   
  160.   
  161.         final View content = mContent;  
  162.         if (!mExpanded) {  
  163.             mContent.layout(0, mContentRangeBottom, content.getMeasuredWidth(), mContentRangeBottom + content.getMeasuredHeight());  
  164.         } else {  
  165.             mContent.layout(0, mContentRangeTop, content.getMeasuredWidth(), mContentRangeTop + content.getMeasuredHeight());  
  166.         }  
  167.     }  
  168.   
  169.   
  170.     @Override  
  171.     protected void dispatchDraw(Canvas canvas) {  
  172.         super.dispatchDraw(canvas);  
  173.   
  174.   
  175.         long drawingTime = getDrawingTime();  
  176.   
  177.   
  178.         final View handle = mHandle;  
  179.         drawChild(canvas, handle, drawingTime);  
  180.   
  181.   
  182.         final View content = mContent;  
  183.         drawChild(canvas, content, drawingTime);  
  184.     }  
  185.   
  186.   
  187.   
  188.   
  189.     @Override  
  190.     public boolean onInterceptTouchEvent(MotionEvent ev) {  
  191.         return mExpanded;  
  192.     }  
  193.   
  194.   
  195.     @Override  
  196.     public boolean onTouchEvent(MotionEvent event) {  
  197.         switch (event.getAction()){  
  198.             case MotionEvent.ACTION_DOWN:  
  199.                 mDownY= (int)event.getY();  
  200.                 break;  
  201.             case MotionEvent.ACTION_MOVE:  
  202.                 int nowY=(int)event.getY();  
  203.                   
  204.                 moveContent(nowY-mDownY);  
  205.                 break;  
  206.             case MotionEvent.ACTION_UP:  
  207.                 adjustContentView();  
  208.                 break;  
  209.         }  
  210.         return  mExpanded;  
  211.     }  
  212.    
  213.   
  214.   
  215.     /** 
  216.      * 停止滑动 
  217.      * 
  218.      * @param 
  219.      */  
  220.     private void stopTracking() {  
  221.         //判断content是展开还是收缩  
  222.         updateExpanded();  
  223.     }  
  224.   
  225.   
  226.     /** 
  227.      * 更新mExpanded状态 
  228.      */  
  229.     private void updateExpanded(){  
  230.         if (mContent.getTop() <= mContentRangeTop) {  
  231.             mExpanded = true;  
  232.         } else {  
  233.             mExpanded = false;  
  234.         }  
  235.     }  
  236.   
  237.   
  238.     /** 
  239.      * move content到指定位置 
  240.      * 
  241.      * @param position 
  242.      */  
  243.     private void moveContent(int position) {  
  244.   
  245.   
  246.   
  247.   
  248.         Log.i(GTAG, "*********move Content:position" + position + "**********");  
  249.         //不能移出上边界  
  250.         if (position < mContentRangeTop) {  
  251.             position = mContentRangeTop;  
  252.         } else if (position > mContentRangeBottom) {  
  253.             position = mContentRangeBottom;  
  254.         }  
  255.   
  256.   
  257.         final View content=mContent;  
  258.         final int top = (int) mContent.getY();  
  259.         final int deltaY = position - top;  
  260.   
  261.   
  262.   
  263.   
  264.   
  265.   
  266.         content.layout(0,position,content.getWidth(),position+content.getHeight());  
  267.     }  
  268.   
  269.   
  270.     //移动Content  
  271.     private void doAnimation(int nowY, final int targetY) {  
  272.         AccelerateInterpolator accelerateInterpolator = new AccelerateInterpolator();  
  273.         //BounceInterpolator bounceInterpolator=new BounceInterpolator();  
  274.         TranslateAnimation animation = new TranslateAnimation(000, targetY - nowY);  
  275.         animation.setDuration(ANIMATION_DURATION * Math.abs(targetY - nowY) / mContentRangeBottom);  
  276.         animation.setInterpolator(accelerateInterpolator);  
  277.         animation.setAnimationListener(new Animation.AnimationListener() {  
  278.             @Override  
  279.             public void onAnimationStart(Animation animation) {  
  280.   
  281.   
  282.   
  283.   
  284.             }  
  285.   
  286.   
  287.             @Override  
  288.             public void onAnimationEnd(Animation animation) {  
  289.                 //Log.i(TAG,"onAnimationEnd");  
  290.                 mContent.clearAnimation();  
  291.                 mContent.layout(0, targetY, mContent.getMeasuredWidth(), targetY + mContent.getMeasuredHeight());  
  292.   
  293.   
  294.                 stopTracking();  
  295.             }  
  296.   
  297.   
  298.             @Override  
  299.             public void onAnimationRepeat(Animation animation) {  
  300.   
  301.   
  302.             }  
  303.         });  
  304.   
  305.   
  306.         mContent.startAnimation(animation);  
  307.   
  308.   
  309.     }  
  310.   
  311.   
  312.     /** 
  313.      * 点击时打开Content* 
  314.      */  
  315.     private void open() {  
  316.         doAnimation(mContentRangeBottom, 0);  
  317.         stopTracking();  
  318.     }  
  319.   
  320.   
  321.   
  322.   
  323.     /** 
  324.      * ACTION_UP时 contentView不是停靠在屏幕边缘(在屏幕中间)时,调整contentView的位置* 
  325.      */  
  326.     private void adjustContentView(){  
  327.         final int top = mContent.getTop();  
  328.   
  329.   
  330.         //切割父容器,分成3等份  
  331.         final int perRange=(mContentRangeBottom-mContentRangeTop)/3;  
  332.         if(mExpanded){  
  333.             //小于1/3  
  334.             if(top<perRange+mContentRangeTop){  
  335.                 doAnimation(top,0);  
  336.             }else{  
  337.                 doAnimation(top, mContentRangeBottom);  
  338.             }  
  339.         }else{  
  340.             //小于2/3  
  341.             if(top<mContentRangeTop+perRange*2){  
  342.                 doAnimation(top,0);  
  343.             }else{  
  344.                 doAnimation(top,mContentRangeBottom);  
  345.             }  
  346.         }  
  347.           
  348.     }  
  349.   
  350.   
  351.     /** 
  352.      *按下时触发*  
  353.      * @param e 
  354.      * @return 
  355.      */  
  356.     @Override  
  357.     public boolean onDown(MotionEvent e) {  
  358.         Log.i(GTAG, "onDown:"+e.getY());  
  359.         return false;  
  360.     }  
  361.   
  362.   
  363.     @Override  
  364.     public void onShowPress(MotionEvent e) {  
  365.         Log.i(GTAG, "onShowPress");  
  366.     }  
  367.   
  368.   
  369.     /** 
  370.      * 轻轻点击*  
  371.      * @param e 
  372.      * @return 
  373.      */  
  374.     @Override  
  375.     public boolean onSingleTapUp(MotionEvent e) {  
  376.         Log.i(GTAG, "onSingleTapUp");  
  377.         if (!mExpanded) {  
  378.             open();  
  379.         }  
  380.         return false;  
  381.     }  
  382.   
  383.   
  384.     /** 
  385.      * 滚动时出发*  
  386.      * @param e1 
  387.      * @param e2 
  388.      * @param distanceX 
  389.      * @param distanceY 
  390.      * @return 
  391.      */  
  392.     @Override  
  393.     public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {  
  394.   
  395.   
  396.         int touchY = (int) e2.getY();  
  397.         Log.i(GTAG, "onScroll:"+mContent.getY());  
  398.         moveContent(touchY - mDownY + (mExpanded ? mContentRangeTop : mContentRangeBottom));  
  399.         return false;  
  400.     }  
  401.   
  402.   
  403.     /** 
  404.      * 长按*  
  405.      * @param e 
  406.      */  
  407.     @Override  
  408.     public void onLongPress(MotionEvent e) {  
  409.         Log.i(GTAG, "onLongPress");  
  410.     }  
  411.   
  412.   
  413.     /** 
  414.      * 瞬时滑动* 
  415.      * @param e1 
  416.      * @param e2 
  417.      * @param velocityX 
  418.      * @param velocityY 
  419.      * @return 
  420.      */  
  421.     @Override  
  422.     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {  
  423.         Log.i(GTAG, "onFling");  
  424.         return false;  
  425.     }  
  426.   
  427.   
  428.     OnTouchListener touchListener = new OnTouchListener() {  
  429.         @Override  
  430.         public boolean onTouch(View v, MotionEvent event) {  
  431.             final View handle = mHandle;  
  432.             if (event.getAction() == MotionEvent.ACTION_DOWN) {  
  433.                 final int top = handle.getTop();  
  434.                 mDownY = (int) event.getY();  
  435.   
  436.   
  437.                 Log.i(GTAG,"mDownY:"+mDownY);  
  438.             } else if (event.getAction() == MotionEvent.ACTION_UP) {  
  439.                  adjustContentView();  
  440.             }  
  441.             Log.i(GTAG, "onTouch:" + event.getAction() + " y:" + event.getY());  
  442.             return mGestureDetector.onTouchEvent(event);  
  443.         }  
  444.   
  445.   
  446.     };  
  447. }  

xml

[html]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <com.example.huwei.slidingpaneldemo.ui.SlidingPanel xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     android:layout_width="match_parent"  
  3.     android:layout_height="match_parent">  
  4.   
  5.   
  6.     <Button  
  7.         android:layout_width="wrap_content"  
  8.         android:layout_height="wrap_content" />  
  9.     <Button  
  10.         android:layout_width="wrap_content"  
  11.         android:layout_height="wrap_content" />  
  12.   
  13.     <LinearLayout  
  14.         android:id="@+id/handle"  
  15.         android:orientation="horizontal"  
  16.         android:layout_width="fill_parent"  
  17.         android:layout_height="68dp"  
  18.         android:background="@drawable/handle_seletor">  
  19.   
  20.         <Button  
  21.             android:layout_width="wrap_content"  
  22.             android:layout_height="wrap_content" />  
  23.         </LinearLayout>  
  24.   
  25.     <LinearLayout  
  26.         android:orientation="vertical"  
  27.         android:id="@+id/content"  
  28.         android:background="#78ADFF2F"  
  29.         android:layout_width="match_parent"  
  30.         android:layout_height="match_parent" >  
  31.         <Button   
  32.             android:layout_width="wrap_content"  
  33.             android:layout_height="wrap_content"/>  
  34.     </LinearLayout>  
  35. </com.example.huwei.slidingpaneldemo.ui.SlidingPanel>  

百度网盘地址

csdn下载

相关文章
|
1天前
|
Android开发
Android RIL 动态切换 4G 模块适配
Android RIL 动态切换 4G 模块适配
4 0
|
9天前
|
安全 Android开发 数据安全/隐私保护
Android中的动态权限请求与最佳实践
【4月更文挑战第14天】 在现代安卓应用开发中,用户隐私和安全被赋予了前所未有的重要性。随着Android 6.0(API级别23)引入的运行时权限模型,开发者必须更加细致地处理权限请求,以确保应用功能的完整性同时不侵犯用户的隐私。本文将深入探讨如何在Android应用中实现动态权限请求,分析常见问题,并提供一系列最佳实践,以帮助开发者优雅地处理这一挑战。
19 5
|
15天前
|
XML Java Android开发
Android之UI基础控件
Android之UI基础控件
|
1月前
|
Android开发
[Android]RadioButton控件
[Android]RadioButton控件
12 0
|
2月前
|
Android开发 数据安全/隐私保护
【Android Studio】简单的QQ登录界面
【Android Studio】简单的QQ登录界面
|
3月前
|
Android开发
分享88个Android控件源代码总有一个是你想要的
分享88个Android控件源代码总有一个是你想要的
23 0
|
3月前
|
Android开发
分享89个Android控件源代码总有一个是你想要的
分享89个Android控件源代码总有一个是你想要的
73 0
|
4月前
|
XML Android开发 数据格式
[Android]开关控件Switch
[Android]开关控件Switch
36 0
|
4月前
|
Android开发 Kotlin
android开发,使用kotlin学习滚动控件RecyclerView
android开发,使用kotlin学习滚动控件RecyclerView
36 0