在Android学习中,动作交互是软件中重要的一部分,其中的Scroller就是提供了拖动效果的类,在网上,比如说一些Launcher实现滑屏都可以通过这个类去实现。下面要说的就是上次Scroller类学习的后的实践了。
如果你还不了解Scroller类,那请先点击:Android 界面滑动实现---Scroller类 从源码和开发文档中学习(让你的布局动起来)
了解之后再阅读以下内容,你会发现原来实现起来很简单。
之前说到过,在广泛使用的侧边滑动导航开源库 --SlidingLayer其实就是使用到了Scroller类进行的实现,(SlidingLayer下载地址:GITHUB ),而是这个库的实现过程中使用到的---Scroller类,我们可以使用这个库实现以下我要达到的效果,可是这样拿来就用,对于初学者提升不大,所以我决定直接去使用Scroller类去实现:
1)窗帘展开和关闭效果
2)登录界面拖动效果(有点类似PopupWindow,可是带上了拖拽效果)。
通过这2个例子,你就大概知道了Scroller类的基本使用情况,可以自己去写一些类似的效果了。
先上图,在上主要代码,最后上DEMO源码。
申明下:DEMO中的资源文件是在网上下载的2个应用中,发现效果不错和可以进一步完善(比如窗帘效果,原本是不带推拽效果),提取了应用的资源文件去自己实现的,目的是为了更好的达到展示效果。
代码中都带上了注释和说明,以便更好的了解实现过程。可能有的地方优化做的不足,望大家见谅。
效果图:
1)窗帘 效果
用途:可以使用于广告墙,公告栏等地方
说明:点击开关可以实现展开关闭功能,也可以通过推拽开关实现展开关闭效果,动画中加入了反弹效果,更加真实。

2)登录窗体 效果
用途:可以使用在登录时候的登录方式选择,菜单选项等,有点类似于带拖拽效果的PopupWindow
说明:可以登录按钮展开关闭登录窗体,也可以通过推拽进行关闭。
注:这里的点击窗体之外消失是通过回调接口实现,这里没有列出,可以下载源码查看

学习了Scroller类,大概的你也知道核心代码会是哪些内容,下面列举下
核心代码:
窗帘效果:
- publicclassCurtainViewextendsRelativeLayoutimplementsOnTouchListener{
- privatestaticStringTAG="CurtainView";
- privateContextmContext;
- /**Scroller拖动类*/
- privateScrollermScroller;
- /**屏幕高度*/
- privateintmScreenHeigh=0;
- /**屏幕宽度*/
- privateintmScreenWidth=0;
- /**点击时候Y的坐标*/
- privateintdownY=0;
- /**拖动时候Y的坐标*/
- privateintmoveY=0;
- /**拖动时候Y的方向距离*/
- privateintscrollY=0;
- /**松开时候Y的坐标*/
- privateintupY=0;
- /**广告幕布的高度*/
- privateintcurtainHeigh=0;
- /**是否打开*/
- privatebooleanisOpen=false;
- /**是否在动画*/
- privatebooleanisMove=false;
- /**绳子的图片*/
- privateImageViewimg_curtain_rope;
- /**广告的图片*/
- privateImageViewimg_curtain_ad;
- /**上升动画时间*/
- privateintupDuration=1000;
- /**下落动画时间*/
- privateintdownDuration=500;
- publicCurtainView(Contextcontext){
- super(context);
- init(context);
- }
- publicCurtainView(Contextcontext,AttributeSetattrs,intdefStyle){
- super(context,attrs,defStyle);
- init(context);
- }
- publicCurtainView(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- init(context);
- }
- /**初始化*/
- privatevoidinit(Contextcontext){
- this.mContext=context;
- //Interpolator设置为有反弹效果的(Bounce:反弹)
- Interpolatorinterpolator=newBounceInterpolator();
- mScroller=newScroller(context,interpolator);
- mScreenHeigh=BaseTools.getWindowHeigh(context);
- mScreenWidth=BaseTools.getWindowWidth(context);
- //背景设置成透明
- this.setBackgroundColor(Color.argb(0,0,0,0));
- finalViewview=LayoutInflater.from(mContext).inflate(R.layout.curtain,null);
- img_curtain_ad=(ImageView)view.findViewById(R.id.img_curtain_ad);
- img_curtain_rope=(ImageView)view.findViewById(R.id.img_curtain_rope);
- addView(view);
- img_curtain_ad.post(newRunnable(){
- @Override
- publicvoidrun(){
- //TODOAuto-generatedmethodstub
- curtainHeigh=img_curtain_ad.getHeight();
- Log.d(TAG,"curtainHeigh="+curtainHeigh);
- CurtainView.this.scrollTo(0,curtainHeigh);
- //注意scrollBy和scrollTo的区别
- }
- });
- img_curtain_rope.setOnTouchListener(this);
- }
- /**
- *拖动动画
- *@paramstartY
- *@paramdy垂直距离,滚动的y距离
- *@paramduration时间
- */
- publicvoidstartMoveAnim(intstartY,intdy,intduration){
- isMove=true;
- mScroller.startScroll(0,startY,0,dy,duration);
- invalidate();//通知UI线程的更新
- }
- @Override
- protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
- //TODOAuto-generatedmethodstub
- super.onLayout(changed,l,t,r,b);
- }
- @Override
- publicvoidcomputeScroll(){
- //判断是否还在滚动,还在滚动为true
- if(mScroller.computeScrollOffset()){
- scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
- //更新界面
- postInvalidate();
- isMove=true;
- }else{
- isMove=false;
- }
- super.computeScroll();
- }
- @Override
- publicbooleanonTouch(Viewv,MotionEventevent){
- //TODOAuto-generatedmethodstub
- if(!isMove){
- intoffViewY=0;//屏幕顶部和该布局顶部的距离
- switch(event.getAction()){
- caseMotionEvent.ACTION_DOWN:
- downY=(int)event.getRawY();
- offViewY=downY-(int)event.getX();
- returntrue;
- caseMotionEvent.ACTION_MOVE:
- moveY=(int)event.getRawY();
- scrollY=moveY-downY;
- if(scrollY<0){
- //向上滑动
- if(isOpen){
- if(Math.abs(scrollY)<=img_curtain_ad.getBottom()-offViewY){
- scrollTo(0,-scrollY);
- }
- }
- }else{
- //向下滑动
- if(!isOpen){
- if(scrollY<=curtainHeigh){
- scrollTo(0,curtainHeigh-scrollY);
- }
- }
- }
- break;
- caseMotionEvent.ACTION_UP:
- upY=(int)event.getRawY();
- if(Math.abs(upY-downY)<10){
- onRopeClick();
- break;
- }
- if(downY>upY){
- //向上滑动
- if(isOpen){
- if(Math.abs(scrollY)>curtainHeigh/2){
- //向上滑动超过半个屏幕高的时候开启向上消失动画
- startMoveAnim(this.getScrollY(),
- (curtainHeigh-this.getScrollY()),upDuration);
- isOpen=false;
- }else{
- startMoveAnim(this.getScrollY(),-this.getScrollY(),upDuration);
- isOpen=true;
- }
- }
- }else{
- //向下滑动
- if(scrollY>curtainHeigh/2){
- //向上滑动超过半个屏幕高的时候开启向上消失动画
- startMoveAnim(this.getScrollY(),-this.getScrollY(),upDuration);
- isOpen=true;
- }else{
- startMoveAnim(this.getScrollY(),(curtainHeigh-this.getScrollY()),upDuration);
- isOpen=false;
- }
- }
- break;
- default:
- break;
- }
- }
- returnfalse;
- }
- /**
- *点击绳索开关,会展开关闭
- *在onToch中使用这个中的方法来当点击事件,避免了点击时候响应onTouch的衔接不完美的影响
- */
- publicvoidonRopeClick(){
- if(isOpen){
- CurtainView.this.startMoveAnim(0,curtainHeigh,upDuration);
- }else{
- CurtainView.this.startMoveAnim(curtainHeigh,-curtainHeigh,downDuration);
- }
- isOpen=!isOpen;
- }
- }
登录界面:
- publicclassLoginViewextendsRelativeLayout{
- /**Scroller拖动类*/
- privateScrollermScroller;
- /**屏幕高度*/
- privateintmScreenHeigh=0;
- /**屏幕宽度*/
- privateintmScreenWidth=0;
- /**点击时候Y的坐标*/
- privateintdownY=0;
- /**拖动时候Y的坐标*/
- privateintmoveY=0;
- /**拖动时候Y的方向距离*/
- privateintscrollY=0;
- /**松开时候Y的坐标*/
- privateintupY=0;
- /**是否在移动*/
- privateBooleanisMoving=false;
- /**布局的高度*/
- privateintviewHeight=0;
- /**是否打开*/
- publicbooleanisShow=false;
- /**是否可以拖动*/
- publicbooleanmEnabled=true;
- /**点击外面是否关闭该界面*/
- publicbooleanmOutsideTouchable=true;
- /**上升动画时间*/
- privateintmDuration=800;
- privatefinalstaticStringTAG="LoginView";
- publicLoginView(Contextcontext){
- super(context);
- init(context);
- }
- publicLoginView(Contextcontext,AttributeSetattrs){
- super(context,attrs);
- init(context);
- }
- publicLoginView(Contextcontext,AttributeSetattrs,intdefStyle){
- super(context,attrs,defStyle);
- init(context);
- }
- privatevoidinit(Contextcontext){
- setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
- setFocusable(true);
- mScroller=newScroller(context);
- mScreenHeigh=BaseTools.getWindowHeigh(context);
- mScreenWidth=BaseTools.getWindowWidth(context);
- //背景设置成透明
- this.setBackgroundColor(Color.argb(0,0,0,0));
- finalViewview=LayoutInflater.from(context).inflate(R.layout.view_login,null);
- LayoutParamsparams=newLayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);//如果不给他设这个,它的布局的MATCH_PARENT就不知道该是多少
- addView(view,params);//ViewGroup的大小,
- //背景设置成透明
- this.setBackgroundColor(Color.argb(0,0,0,0));
- view.post(newRunnable(){
- @Override
- publicvoidrun(){
- //TODOAuto-generatedmethodstub
- viewHeight=view.getHeight();
- }
- });
- LoginView.this.scrollTo(0,mScreenHeigh);
- ImageViewbtn_close=(ImageView)view.findViewById(R.id.btn_close);
- btn_close.setOnClickListener(newOnClickListener(){
- @Override
- publicvoidonClick(Viewv){
- //TODOAuto-generatedmethodstub
- dismiss();
- }
- });
- }
- @Override
- publicbooleanonInterceptTouchEvent(MotionEventev){
- if(!mEnabled){
- returnfalse;
- }
- returnsuper.onInterceptTouchEvent(ev);
- }
- @Override
- publicbooleanonTouchEvent(MotionEventevent){
- //TODOAuto-generatedmethodstub
- switch(event.getAction()){
- caseMotionEvent.ACTION_DOWN:
- downY=(int)event.getY();
- Log.d(TAG,"downY="+downY);
- //如果完全显示的时候,让布局得到触摸监听,如果不显示,触摸事件不拦截,向下传递
- if(isShow){
- returntrue;
- }
- break;
- caseMotionEvent.ACTION_MOVE:
- moveY=(int)event.getY();
- scrollY=moveY-downY;
- //向下滑动
- if(scrollY>0){
- if(isShow){
- scrollTo(0,-Math.abs(scrollY));
- }
- }else{
- if(mScreenHeigh-this.getTop()<=viewHeight&&!isShow){
- scrollTo(0,Math.abs(viewHeight-scrollY));
- }
- }
- break;
- caseMotionEvent.ACTION_UP:
- upY=(int)event.getY();
- if(isShow){
- if(this.getScrollY()<=-(viewHeight/2)){
- startMoveAnim(this.getScrollY(),-(viewHeight-this.getScrollY()),mDuration);
- isShow=false;
- Log.d("isShow","false");
- }else{
- startMoveAnim(this.getScrollY(),-this.getScrollY(),mDuration);
- isShow=true;
- Log.d("isShow","true");
- }
- }
- Log.d("this.getScrollY()",""+this.getScrollY());
- changed();
- break;
- caseMotionEvent.ACTION_OUTSIDE:
- Log.d(TAG,"ACTION_OUTSIDE");
- break;
- default:
- break;
- }
- returnsuper.onTouchEvent(event);
- }
- /**
- *拖动动画
- *@paramstartY
- *@paramdy移动到某点的Y坐标距离
- *@paramduration时间
- */
- publicvoidstartMoveAnim(intstartY,intdy,intduration){
- isMoving=true;
- mScroller.startScroll(0,startY,0,dy,duration);
- invalidate();//通知UI线程的更新
- }
- @Override
- publicvoidcomputeScroll(){
- if(mScroller.computeScrollOffset()){
- scrollTo(mScroller.getCurrX(),mScroller.getCurrY());
- //更新界面
- postInvalidate();
- isMoving=true;
- }else{
- isMoving=false;
- }
- super.computeScroll();
- }
- /**开打界面*/
- publicvoidshow(){
- if(!isShow&&!isMoving){
- LoginView.this.startMoveAnim(-viewHeight,viewHeight,mDuration);
- isShow=true;
- Log.d("isShow","true");
- changed();
- }
- }
- /**关闭界面*/
- publicvoiddismiss(){
- if(isShow&&!isMoving){
- LoginView.this.startMoveAnim(0,-viewHeight,mDuration);
- isShow=false;
- Log.d("isShow","false");
- changed();
- }
- }
- /**是否打开*/
- publicbooleanisShow(){
- returnisShow;
- }
- /**获取是否可以拖动*/
- publicbooleanisSlidingEnabled(){
- returnmEnabled;
- }
- /**设置是否可以拖动*/
- publicvoidsetSlidingEnabled(booleanenabled){
- mEnabled=enabled;
- }
- /**
- *设置监听接口,实现遮罩层效果
- */
- publicvoidsetOnStatusListener(onStatusListenerlistener){
- this.statusListener=listener;
- }
- publicvoidsetOutsideTouchable(booleantouchable){
- mOutsideTouchable=touchable;
- }
- /**
- *显示状态发生改变时候执行回调借口
- */
- publicvoidchanged(){
- if(statusListener!=null){
- if(isShow){
- statusListener.onShow();
- }else{
- statusListener.onDismiss();
- }
- }
- }
- /**监听接口*/
- publiconStatusListenerstatusListener;
- /**
- *监听接口,来在主界面监听界面变化状态
- */
- publicinterfaceonStatusListener{
- /**开打状态*/
- publicvoidonShow();
- /**关闭状态*/
- publicvoidonDismiss();
- }
- @Override
- protectedvoidonLayout(booleanchanged,intl,intt,intr,intb){
- //TODOAuto-generatedmethodstub
- super.onLayout(changed,l,t,r,b);
- }
- }
其实代码大同小异,了解后你就可以举一反三,去自己的VIEW中实现自己想要的效果。
最后,上源码:下载地址