在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类,大概的你也知道核心代码会是哪些内容,下面列举下
 
核心代码:
 
窗帘效果:
 
  1. public class CurtainView extends RelativeLayout implements OnTouchListener{
  2. private static String TAG = "CurtainView";
  3. private Context mContext;
  4. /** Scroller 拖动类 */
  5. private Scroller mScroller;
  6. /** 屏幕高度  */
  7. private int mScreenHeigh = 0;
  8. /** 屏幕宽度  */
  9. private int mScreenWidth = 0;
  10. /** 点击时候Y的坐标*/
  11. private int downY = 0;
  12. /** 拖动时候Y的坐标*/
  13. private int moveY = 0;
  14. /** 拖动时候Y的方向距离*/
  15. private int scrollY = 0;
  16. /** 松开时候Y的坐标*/
  17. private int upY = 0;
  18. /** 广告幕布的高度*/
  19. private int curtainHeigh = 0;
  20. /** 是否 打开*/
  21. private boolean isOpen = false;
  22. /** 是否在动画 */
  23. private boolean isMove = false;
  24. /** 绳子的图片*/
  25. private ImageView img_curtain_rope;
  26. /** 广告的图片*/
  27. private ImageView img_curtain_ad;
  28. /** 上升动画时间 */
  29. private int upDuration = 1000;
  30. /** 下落动画时间 */
  31. private int downDuration = 500;
  32. public CurtainView(Context context) {
  33. super(context);
  34. init(context);
  35. }
  36. public CurtainView(Context context, AttributeSet attrs, int defStyle) {
  37. super(context, attrs, defStyle);
  38. init(context);
  39. }
  40. public CurtainView(Context context, AttributeSet attrs) {
  41. super(context, attrs);
  42. init(context);
  43. }
  44. /** 初始化 */
  45. private void init(Context context) {
  46. this.mContext = context;
  47. //Interpolator 设置为有反弹效果的  (Bounce:反弹)
  48. Interpolator interpolator = new BounceInterpolator();
  49. mScroller = new Scroller(context, interpolator);
  50. mScreenHeigh = BaseTools.getWindowHeigh(context);
  51. mScreenWidth = BaseTools.getWindowWidth(context);
  52. // 背景设置成透明
  53. this.setBackgroundColor(Color.argb(0, 0, 0, 0));
  54. final View view = LayoutInflater.from(mContext).inflate(R.layout.curtain, null);
  55. img_curtain_ad = (ImageView)view.findViewById(R.id.img_curtain_ad);
  56. img_curtain_rope = (ImageView)view.findViewById(R.id.img_curtain_rope);
  57. addView(view);
  58. img_curtain_ad.post(new Runnable() {
  59. @Override
  60. public void run() {
  61. // TODO Auto-generated method stub
  62. curtainHeigh  = img_curtain_ad.getHeight();
  63. Log.d(TAG, "curtainHeigh= " + curtainHeigh);
  64. CurtainView.this.scrollTo(0, curtainHeigh);
  65. //注意scrollBy和scrollTo的区别
  66. }
  67. });
  68. img_curtain_rope.setOnTouchListener(this);
  69. }
  70. /**
  71. * 拖动动画
  72. * @param startY
  73. * @param dy  垂直距离, 滚动的y距离
  74. * @param duration 时间
  75. */
  76. public void startMoveAnim(int startY, int dy, int duration) {
  77. isMove = true;
  78. mScroller.startScroll(0, startY, 0, dy, duration);
  79. invalidate();//通知UI线程的更新
  80. }
  81. @Override
  82. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  83. // TODO Auto-generated method stub
  84. super.onLayout(changed, l, t, r, b);
  85. }
  86. @Override
  87. public void computeScroll() {
  88. //判断是否还在滚动,还在滚动为true
  89. if (mScroller.computeScrollOffset()) {
  90. scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  91. //更新界面
  92. postInvalidate();
  93. isMove = true;
  94. } else {
  95. isMove = false;
  96. }
  97. super.computeScroll();
  98. }
  99. @Override
  100. public boolean onTouch(View v, MotionEvent event) {
  101. // TODO Auto-generated method stub
  102. if (!isMove) {
  103. int offViewY = 0;//屏幕顶部和该布局顶部的距离
  104. switch (event.getAction()) {
  105. case MotionEvent.ACTION_DOWN:
  106. downY = (int) event.getRawY();
  107. offViewY = downY - (int)event.getX();
  108. return true;
  109. case MotionEvent.ACTION_MOVE:
  110. moveY = (int) event.getRawY();
  111. scrollY = moveY - downY;
  112. if (scrollY < 0) {
  113. // 向上滑动
  114. if(isOpen){
  115. if(Math.abs(scrollY) <= img_curtain_ad.getBottom() - offViewY){
  116. scrollTo(0, -scrollY);
  117. }
  118. }
  119. } else {
  120. // 向下滑动
  121. if(!isOpen){
  122. if (scrollY <= curtainHeigh) {
  123. scrollTo(0, curtainHeigh - scrollY);
  124. }
  125. }
  126. }
  127. break;
  128. case MotionEvent.ACTION_UP:
  129. upY = (int) event.getRawY();
  130. if(Math.abs(upY - downY) < 10){
  131. onRopeClick();
  132. break;
  133. }
  134. if (downY > upY) {
  135. // 向上滑动
  136. if(isOpen){
  137. if (Math.abs(scrollY) > curtainHeigh / 2) {
  138. // 向上滑动超过半个屏幕高的时候 开启向上消失动画
  139. startMoveAnim(this.getScrollY(),
  140. (curtainHeigh - this.getScrollY()), upDuration);
  141. isOpen = false;
  142. } else {
  143. startMoveAnim(this.getScrollY(), -this.getScrollY(),upDuration);
  144. isOpen = true;
  145. }
  146. }
  147. } else {
  148. // 向下滑动
  149. if (scrollY > curtainHeigh / 2) {
  150. // 向上滑动超过半个屏幕高的时候 开启向上消失动画
  151. startMoveAnim(this.getScrollY(), -this.getScrollY(),upDuration);
  152. isOpen = true;
  153. } else {
  154. startMoveAnim(this.getScrollY(),(curtainHeigh - this.getScrollY()), upDuration);
  155. isOpen = false;
  156. }
  157. }
  158. break;
  159. default:
  160. break;
  161. }
  162. }
  163. return false;
  164. }
  165. /**
  166. * 点击绳索开关,会展开关闭
  167. * 在onToch中使用这个中的方法来当点击事件,避免了点击时候响应onTouch的衔接不完美的影响
  168. */
  169. public void onRopeClick(){
  170. if(isOpen){
  171. CurtainView.this.startMoveAnim(0, curtainHeigh, upDuration);
  172. }else{
  173. CurtainView.this.startMoveAnim(curtainHeigh,-curtainHeigh, downDuration);
  174. }
  175. isOpen = !isOpen;
  176. }
  177. }
 
登录界面:
 
  1. public class LoginView extends RelativeLayout {
  2. /** Scroller 拖动类 */
  3. private Scroller mScroller;
  4. /** 屏幕高度  */
  5. private int mScreenHeigh = 0;
  6. /** 屏幕宽度  */
  7. private int mScreenWidth = 0;
  8. /** 点击时候Y的坐标*/
  9. private int downY = 0;
  10. /** 拖动时候Y的坐标*/
  11. private int moveY = 0;
  12. /** 拖动时候Y的方向距离*/
  13. private int scrollY = 0;
  14. /** 松开时候Y的坐标*/
  15. private int upY = 0;
  16. /** 是否在移动*/
  17. private Boolean isMoving = false;
  18. /** 布局的高度*/
  19. private int viewHeight = 0;
  20. /** 是否打开*/
  21. public boolean isShow = false;
  22. /** 是否可以拖动*/
  23. public boolean mEnabled = true;
  24. /** 点击外面是否关闭该界面*/
  25. public boolean mOutsideTouchable = true;
  26. /** 上升动画时间 */
  27. private int mDuration = 800;
  28. private final static String TAG = "LoginView";
  29. public LoginView(Context context) {
  30. super(context);
  31. init(context);
  32. }
  33. public LoginView(Context context, AttributeSet attrs) {
  34. super(context, attrs);
  35. init(context);
  36. }
  37. public LoginView(Context context, AttributeSet attrs, int defStyle) {
  38. super(context, attrs, defStyle);
  39. init(context);
  40. }
  41. private void init(Context context) {
  42. setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
  43. setFocusable(true);
  44. mScroller = new Scroller(context);
  45. mScreenHeigh = BaseTools.getWindowHeigh(context);
  46. mScreenWidth = BaseTools.getWindowWidth(context);
  47. // 背景设置成透明
  48. this.setBackgroundColor(Color.argb(0, 0, 0, 0));
  49. final View view = LayoutInflater.from(context).inflate(R.layout.view_login,null);
  50. LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);// 如果不给他设这个,它的布局的MATCH_PARENT就不知道该是多少
  51. addView(view, params);// ViewGroup的大小,
  52. // 背景设置成透明
  53. this.setBackgroundColor(Color.argb(0, 0, 0, 0));
  54. view.post(new Runnable() {
  55. @Override
  56. public void run() {
  57. // TODO Auto-generated method stub
  58. viewHeight = view.getHeight();
  59. }
  60. });
  61. LoginView.this.scrollTo(0, mScreenHeigh);
  62. ImageView btn_close = (ImageView)view.findViewById(R.id.btn_close);
  63. btn_close.setOnClickListener(new OnClickListener() {
  64. @Override
  65. public void onClick(View v) {
  66. // TODO Auto-generated method stub
  67. dismiss();
  68. }
  69. });
  70. }
  71. @Override
  72. public boolean onInterceptTouchEvent(MotionEvent ev) {
  73. if(!mEnabled){
  74. return false;
  75. }
  76. return super.onInterceptTouchEvent(ev);
  77. }
  78. @Override
  79. public boolean onTouchEvent(MotionEvent event) {
  80. // TODO Auto-generated method stub
  81. switch (event.getAction()) {
  82. case MotionEvent.ACTION_DOWN:
  83. downY = (int) event.getY();
  84. Log.d(TAG, "downY = " + downY);
  85. //如果完全显示的时候,让布局得到触摸监听,如果不显示,触摸事件不拦截,向下传递
  86. if(isShow){
  87. return true;
  88. }
  89. break;
  90. case MotionEvent.ACTION_MOVE:
  91. moveY = (int) event.getY();
  92. scrollY = moveY - downY;
  93. //向下滑动
  94. if (scrollY > 0) {
  95. if(isShow){
  96. scrollTo(0, -Math.abs(scrollY));
  97. }
  98. }else{
  99. if(mScreenHeigh - this.getTop() <= viewHeight && !isShow){
  100. scrollTo(0, Math.abs(viewHeight - scrollY));
  101. }
  102. }
  103. break;
  104. case MotionEvent.ACTION_UP:
  105. upY = (int) event.getY();
  106. if(isShow){
  107. if( this.getScrollY() <= -(viewHeight /2)){
  108. startMoveAnim(this.getScrollY(),-(viewHeight - this.getScrollY()), mDuration);
  109. isShow = false;
  110. Log.d("isShow", "false");
  111. } else {
  112. startMoveAnim(this.getScrollY(), -this.getScrollY(), mDuration);
  113. isShow = true;
  114. Log.d("isShow", "true");
  115. }
  116. }
  117. Log.d("this.getScrollY()", ""+this.getScrollY());
  118. changed();
  119. break;
  120. case MotionEvent.ACTION_OUTSIDE:
  121. Log.d(TAG, "ACTION_OUTSIDE");
  122. break;
  123. default:
  124. break;
  125. }
  126. return super.onTouchEvent(event);
  127. }
  128. /**
  129. * 拖动动画
  130. * @param startY
  131. * @param dy  移动到某点的Y坐标距离
  132. * @param duration 时间
  133. */
  134. public void startMoveAnim(int startY, int dy, int duration) {
  135. isMoving = true;
  136. mScroller.startScroll(0, startY, 0, dy, duration);
  137. invalidate();//通知UI线程的更新
  138. }
  139. @Override
  140. public void computeScroll() {
  141. if (mScroller.computeScrollOffset()) {
  142. scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
  143. // 更新界面
  144. postInvalidate();
  145. isMoving = true;
  146. } else {
  147. isMoving = false;
  148. }
  149. super.computeScroll();
  150. }
  151. /** 开打界面 */
  152. public void show(){
  153. if(!isShow && !isMoving){
  154. LoginView.this.startMoveAnim(-viewHeight,   viewHeight, mDuration);
  155. isShow = true;
  156. Log.d("isShow", "true");
  157. changed();
  158. }
  159. }
  160. /** 关闭界面 */
  161. public void dismiss(){
  162. if(isShow && !isMoving){
  163. LoginView.this.startMoveAnim(0, -viewHeight, mDuration);
  164. isShow = false;
  165. Log.d("isShow", "false");
  166. changed();
  167. }
  168. }
  169. /** 是否打开 */
  170. public boolean isShow(){
  171. return isShow;
  172. }
  173. /** 获取是否可以拖动*/
  174. public boolean isSlidingEnabled() {
  175. return mEnabled;
  176. }
  177. /** 设置是否可以拖动*/
  178. public void setSlidingEnabled(boolean enabled) {
  179. mEnabled = enabled;
  180. }
  181. /**
  182. * 设置监听接口,实现遮罩层效果
  183. */
  184. public void setOnStatusListener(onStatusListener listener){
  185. this.statusListener = listener;
  186. }
  187. public void setOutsideTouchable(boolean touchable) {
  188. mOutsideTouchable = touchable;
  189. }
  190. /**
  191. * 显示状态发生改变时候执行回调借口
  192. */
  193. public void changed(){
  194. if(statusListener != null){
  195. if(isShow){
  196. statusListener.onShow();
  197. }else{
  198. statusListener.onDismiss();
  199. }
  200. }
  201. }
  202. /** 监听接口*/
  203. public onStatusListener statusListener;
  204. /**
  205. * 监听接口,来在主界面监听界面变化状态
  206. */
  207. public interface onStatusListener{
  208. /**  开打状态  */
  209. public void onShow();
  210. /**  关闭状态  */
  211. public void onDismiss();
  212. }
  213. @Override
  214. protected void onLayout(boolean changed, int l, int t, int r, int b) {
  215. // TODO Auto-generated method stub
  216. super.onLayout(changed, l, t, r, b);
  217. }
  218. }
其实代码大同小异,了解后你就可以举一反三,去自己的VIEW中实现自己想要的效果。
 
 
最后,上源码:下载地址

版权声明:本文为博主原创文章,未经博主允许不得转载。

Android 仿 窗帘效果 和 登录界面拖动效果 (Scroller类的应用) 附 2个DEMO及源码的更多相关文章

  1. Android 仿 窗帘效果 和 登录界面拖动效果 (Scroller类的应用) 附 2个DEMO及源代码

    在android学习中,动作交互是软件中重要的一部分.当中的Scroller就是提供了拖动效果的类,在网上.比方说一些Launcher实现滑屏都能够通过这个类去实现.以下要说的就是上次Scroller ...

  2. Android 界面滑动实现---Scroller类 从源码和开发文档中学习(让你的布局动起来)

    在android学习中,动作交互是软件中重要的一部分,其中的Scroller就是提供了拖动效果的类,在网上,比如说一些Launcher实现滑屏都可以通过这个类去实现..   例子相关博文:Androi ...

  3. 仿知乎app登录界面(Material Design设计框架拿来就用的TexnInputLayout)

    在我脑子里还没有Material Design这种概念,就我个人而言,PC端应用扁平化设计必须成为首选,手当其冲的两款即时通讯旺旺和QQ早就完成UI扁平化的更新,然而客户端扁平化的设计本身就存在天生的 ...

  4. Android studio 开发一个用户登录界面

    Android studio 开发一个用户登录界面 activity_main.xml <?xml version="1.0" encoding="utf-8&qu ...

  5. Android开发实例之miniTwitter登录界面的实现

    原文: http://www.jizhuomi.com/android/example/134.html 本文要演示的Android开发实例是如何完成一个Android中的miniTwitter登录界 ...

  6. Android 仿 新闻阅读器 菜单弹出效果(附源码DEMO)

    这一系列博文都是:(android高仿系列)今日头条 --新闻阅读器 (一) 开发中碰到问题之后实现的,觉得可能有的开发者用的到或则希望独立成一个小功能DEMO,所以就放出来这么一个DEMO. 原本觉 ...

  7. Android 比较好看的注册登录界面

    各位看官姥爷: 对于一款android手机app而言,美观的界面使得用户有好的使用体验,而一款好看的注册登录界面也会给用户好的用户体验,那么话不多说,直接上代码 首先是一款简单的界面展示 1.登陆界面 ...

  8. Android中滑屏实现----触摸滑屏以及Scroller类详解 .

    转:http://blog.csdn.net/qinjuning/article/details/7419207 知识点一:  关于scrollTo()和scrollBy()以及偏移坐标的设置/取值问 ...

  9. 商城项目实战 | 1.1 Android 仿京东商城底部布局的选择效果 —— Selector 选择器的实现

    前言 本文为菜鸟窝作者刘婷的连载."商城项目实战"系列来聊聊仿"京东淘宝的购物商城"如何实现. 京东商城的底部布局的选择效果看上去很复杂,其实很简单,这主要是要 ...

随机推荐

  1. 【Web】CXF WebService 服务端和客户端 环境搭建及测试

    cxf服务端 1.去官方下载对应的jar包:http://cxf.apache.org/ 2.maven配置相应jar包 3.修改web.xml,完成spring和cxf配置 <!-- Spri ...

  2. c++ ,protected 和 private修饰的构造函数

    c++ protected 和 private修饰的构造函数: 1.在类的外部创建对象时,不能调用protected或private修饰的构造函数. 2.当子类中的构造函数调用父类的private构造 ...

  3. [Swust OJ 247]--皇帝的新衣(组合数+Lucas定理)

    题目链接:http://acm.swust.edu.cn/problem/0247/ Time limit(ms): 1000 Memory limit(kb): 65535   Descriptio ...

  4. BZOJ 1263: [SCOI2006]整数划分( 高精度 )

    yy一下发现好像越小越好...分解成3*3*3*3……这种形式是最好的...然后就是高精度了 ----------------------------------------------------- ...

  5. C#路径,文件,目录,I/O常见操作

         C#路径,文件,目录,I/O常见操作 文件操作是程序中非常基础和重要的内容,而路径.文件.目录以及I/O都是在进行文件操作时的常见主题,这里想把这些常见的问题作个总结,对于每个问题,尽量提供 ...

  6. 【STL__set_的应用】

    1.关于set C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器, 更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结 ...

  7. 基于visual Studio2013解决算法导论之051区间树

     题目 区间树 解决代码及点评 #include <stdio.h> #include <string.h> #include <iostream> #def ...

  8. Pencil OJ 02 安装

    Mongo 官方的安装方法 官方教程已经很好啦,这里就不罗嗦了. 源码编译 待补.我是从这里看到的. 遇到的问题 启动时的警告信息 2015-03-06T21:01:15.526-0800 I CON ...

  9. sprintf,你知道多少?

    选自<CSDN 社区电子杂志——C/C++杂志>http://emag.csdn.net 2005 年1 月 总第1 期 - 93 -本文作者:steedhorse(晨星)printf 可 ...

  10. 开源 免费 java CMS - FreeCMS1.9 职位管理

    项目地址:http://code.google.com/p/freecms/ 职位管理 管理职位,实现招聘功能. 1. 职位管理 从左側管理菜单点击职位管理进入. 2. 加入职位 在职位列表下方点击& ...