仿天天动态上拉播放界面控件
2014-12-26 09:38
204 查看
看到天天动听的播放界面,可以从底部划出来,被其效果惊艳到,于是想自己动手模仿。
效果: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
xml
百度网盘地址
csdn下载
效果: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>
百度网盘地址
csdn下载
相关文章推荐
- 仿天天动态上拉播放界面控件
- WinForm中使用XtraGrid控件,实现在界面中动态修改列显示,列名列宽等
- WinForm中使用XtraGrid控件,实现在界面中动态修改列显示,列名列宽等(进阶)
- 通过javascript动态显示界面控件
- 安卓特效 界面控件 直接动态图预览 源码
- Android使用的webcview中带有音乐播放控件,在关闭或分享时处于界面不可见状态下,声音仍在播放的问题解决
- WPF学习笔记--向界面动态添加控件或者usercontrol
- 界面中动态添加控件
- Android使用的webcview中带有音乐播放控件,在关闭或分享时处于界面不可见状态下,声音仍在播放的问题解决
- Swing动态添加控件,界面刷新重绘
- VS2003 C#:重写键盘响应事件、动态创建控件、创建线程、在线程中使用委托在界面显示数据
- XtraGrid控件,实现在界面中动态修改列显示,列名列宽
- iOS视频播放界面显示音量调节控件
- 类似朋友圈或微博的动态界面,NineGridImageView(九宫格图片控件)
- 不在界面上用控件 动态创建idhttp,IdAntiFreeze来用
- MFC会播放的界面,动态界面
- 毕业设计生产通知单界面及代码(动态创建控件)
- Ext界面下做图片动态加载的图片放大镜效果并解决IE6下遮罩mask无法遮盖HTML的select控件问题
- jsp界面的动态效果判断以及控件直接d动态关联,整数运算
- 动态生成Gallery控件组合实现无限循环自动播放广告图片效果总结