Android 仿 窗簾效果 和 登錄界面拖動效果 (Scroller類的應用) 附 2個DEMO及源代碼
阿新 • • 發佈:2017-06-22
@override 宋體 off down != 過程 事件 學習 border
下載地址:GITHUB
),而是這個庫的實現過程中使用到的---Scroller類。我們能夠使用這個庫實現下面我要達到的效果,但是這樣拿來就用,對於剛開始學習的人提升不大。所以我決定直接去使用Scroller類去實現:
1)窗簾展開和關閉效果
2)登錄界面拖動效果(有點類似PopupWindow,但是帶上了拖拽效果)。
通過這2個樣例,你就大概知道了Scroller類的基本使用情況,能夠自己去寫一些類似的效果了。
先上圖。在上主要代碼,最後上DEMO源代碼。
申明下:DEMO中的資源文件是在網上下載的2個應用中。發現效果不錯和能夠進一步完好(比方窗簾效果,原本是不帶推拽效果),提取了應用的資源文件去自己實現的。目的是為了更好的達到展示效果。
代碼中都帶上了凝視和說明,以便更好的了解實現過程。
效果圖:
1)窗簾 效果 用途:能夠使用於廣告墻,公告欄等地方 說明:點擊開關能夠實現展開關閉功能,也能夠通過推拽開關實現展開關閉效果,動畫中增加了反彈效果。更加真實。
2)登錄窗口 效果 用途:能夠使用在登錄時候的登錄方式選擇,菜單選項等,有點類似於帶拖拽效果的PopupWindow 說明:能夠登錄button展開關閉登錄窗口。也能夠通過推拽進行關閉。 註:這裏的點擊窗口之外消失是通過回調接口實現,這裏沒有列出,能夠下載源代碼查看
學習了Scroller類。大概的你也知道核心代碼會是哪些內容,以下列舉下
核心代碼:
窗簾效果:
登錄界面:
事實上代碼大同小異。了解後你就能夠舉一反三,去自己的VIEW中實現自己想要的效果。
最後,上源代碼:下載地址
在android學習中,動作交互是軟件中重要的一部分。當中的Scroller就是提供了拖動效果的類,在網上。比方說一些Launcher實現滑屏都能夠通過這個類去實現。以下要說的就是上次Scroller類學習的後的實踐了。
假設你還不了解Scroller類,那請先點擊:Android 界面滑動實現---Scroller類 從源代碼和開發文檔中學習(讓你的布局動起來)
了解之後再閱讀下面內容。你會發現原來實現起來非常easy。
之前說到過。在廣泛使用的側邊滑動導航開源庫 --SlidingLayer事實上就是使用到了Scroller類進行的實現,(SlidingLayer通過這2個樣例,你就大概知道了Scroller類的基本使用情況,能夠自己去寫一些類似的效果了。
先上圖。在上主要代碼,最後上DEMO源代碼。
申明下:DEMO中的資源文件是在網上下載的2個應用中。發現效果不錯和能夠進一步完好(比方窗簾效果,原本是不帶推拽效果),提取了應用的資源文件去自己實現的。目的是為了更好的達到展示效果。
可能有的地方優化做的不足,望大家見諒。
效果圖:
1)窗簾 效果 用途:能夠使用於廣告墻,公告欄等地方 說明:點擊開關能夠實現展開關閉功能,也能夠通過推拽開關實現展開關閉效果,動畫中增加了反彈效果。更加真實。
2)登錄窗口 效果 用途:能夠使用在登錄時候的登錄方式選擇,菜單選項等,有點類似於帶拖拽效果的PopupWindow 說明:能夠登錄button展開關閉登錄窗口。也能夠通過推拽進行關閉。 註:這裏的點擊窗口之外消失是通過回調接口實現,這裏沒有列出,能夠下載源代碼查看
學習了Scroller類。大概的你也知道核心代碼會是哪些內容,以下列舉下
核心代碼:
窗簾效果:
public class CurtainView extends RelativeLayout implements OnTouchListener{ private static String TAG = "CurtainView"; private Context mContext; /** Scroller 拖動類 */ private Scroller mScroller; /** 屏幕高度 */ private int mScreenHeigh = 0; /** 屏幕寬度 */ private int mScreenWidth = 0; /** 點擊時候Y的坐標*/ private int downY = 0; /** 拖動時候Y的坐標*/ private int moveY = 0; /** 拖動時候Y的方向距離*/ private int scrollY = 0; /** 松開時候Y的坐標*/ private int upY = 0; /** 廣告幕布的高度*/ private int curtainHeigh = 0; /** 是否 打開*/ private boolean isOpen = false; /** 是否在動畫 */ private boolean isMove = false; /** 繩子的圖片*/ private ImageView img_curtain_rope; /** 廣告的圖片*/ private ImageView img_curtain_ad; /** 上升動畫時間 */ private int upDuration = 1000; /** 下落動畫時間 */ private int downDuration = 500; public CurtainView(Context context) { super(context); init(context); } public CurtainView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } public CurtainView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } /** 初始化 */ private void init(Context context) { this.mContext = context; //Interpolator 設置為有反彈效果的 (Bounce:反彈) Interpolator interpolator = new BounceInterpolator(); mScroller = new Scroller(context, interpolator); mScreenHeigh = BaseTools.getWindowHeigh(context); mScreenWidth = BaseTools.getWindowWidth(context); // 背景設置成透明 this.setBackgroundColor(Color.argb(0, 0, 0, 0)); final View view = 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(new Runnable() { @Override public void run() { // TODO Auto-generated method stub curtainHeigh = img_curtain_ad.getHeight(); Log.d(TAG, "curtainHeigh= " + curtainHeigh); CurtainView.this.scrollTo(0, curtainHeigh); //註意scrollBy和scrollTo的差別 } }); img_curtain_rope.setOnTouchListener(this); } /** * 拖動動畫 * @param startY * @param dy 垂直距離, 滾動的y距離 * @param duration 時間 */ public void startMoveAnim(int startY, int dy, int duration) { isMove = true; mScroller.startScroll(0, startY, 0, dy, duration); invalidate();//通知UI線程的更新 } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub super.onLayout(changed, l, t, r, b); } @Override public void computeScroll() { //推斷是否還在滾動,還在滾動為true if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); //更新界面 postInvalidate(); isMove = true; } else { isMove = false; } super.computeScroll(); } @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub if (!isMove) { int offViewY = 0;//屏幕頂部和該布局頂部的距離 switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) event.getRawY(); offViewY = downY - (int)event.getX(); return true; case MotionEvent.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; case MotionEvent.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; } } return false; } /** * 點擊繩索開關,會展開關閉 * 在onToch中使用這個中的方法來當點擊事件,避免了點擊時候響應onTouch的銜接不完美的影響 */ public void onRopeClick(){ if(isOpen){ CurtainView.this.startMoveAnim(0, curtainHeigh, upDuration); }else{ CurtainView.this.startMoveAnim(curtainHeigh,-curtainHeigh, downDuration); } isOpen = !isOpen; } }
登錄界面:
public class LoginView extends RelativeLayout { /** Scroller 拖動類 */ private Scroller mScroller; /** 屏幕高度 */ private int mScreenHeigh = 0; /** 屏幕寬度 */ private int mScreenWidth = 0; /** 點擊時候Y的坐標*/ private int downY = 0; /** 拖動時候Y的坐標*/ private int moveY = 0; /** 拖動時候Y的方向距離*/ private int scrollY = 0; /** 松開時候Y的坐標*/ private int upY = 0; /** 是否在移動*/ private Boolean isMoving = false; /** 布局的高度*/ private int viewHeight = 0; /** 是否打開*/ public boolean isShow = false; /** 能否夠拖動*/ public boolean mEnabled = true; /** 點擊外面是否關閉該界面*/ public boolean mOutsideTouchable = true; /** 上升動畫時間 */ private int mDuration = 800; private final static String TAG = "LoginView"; public LoginView(Context context) { super(context); init(context); } public LoginView(Context context, AttributeSet attrs) { super(context, attrs); init(context); } public LoginView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(context); } private void init(Context context) { setDescendantFocusability(FOCUS_AFTER_DESCENDANTS); setFocusable(true); mScroller = new Scroller(context); mScreenHeigh = BaseTools.getWindowHeigh(context); mScreenWidth = BaseTools.getWindowWidth(context); // 背景設置成透明 this.setBackgroundColor(Color.argb(0, 0, 0, 0)); final View view = LayoutInflater.from(context).inflate(R.layout.view_login,null); LayoutParams params = new LayoutParams(LayoutParams.MATCH_PARENT,LayoutParams.WRAP_CONTENT);// 假設不給他設這個,它的布局的MATCH_PARENT就不知道該是多少 addView(view, params);// ViewGroup的大小, // 背景設置成透明 this.setBackgroundColor(Color.argb(0, 0, 0, 0)); view.post(new Runnable() { @Override public void run() { // TODO Auto-generated method stub viewHeight = view.getHeight(); } }); LoginView.this.scrollTo(0, mScreenHeigh); ImageView btn_close = (ImageView)view.findViewById(R.id.btn_close); btn_close.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub dismiss(); } }); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { if(!mEnabled){ return false; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { // TODO Auto-generated method stub switch (event.getAction()) { case MotionEvent.ACTION_DOWN: downY = (int) event.getY(); Log.d(TAG, "downY = " + downY); //假設全然顯示的時候。讓布局得到觸摸監聽,假設不顯示,觸摸事件不攔截。向下傳遞 if(isShow){ return true; } break; case MotionEvent.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; case MotionEvent.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; case MotionEvent.ACTION_OUTSIDE: Log.d(TAG, "ACTION_OUTSIDE"); break; default: break; } return super.onTouchEvent(event); } /** * 拖動動畫 * @param startY * @param dy 移動到某點的Y坐標距離 * @param duration 時間 */ public void startMoveAnim(int startY, int dy, int duration) { isMoving = true; mScroller.startScroll(0, startY, 0, dy, duration); invalidate();//通知UI線程的更新 } @Override public void computeScroll() { if (mScroller.computeScrollOffset()) { scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); // 更新界面 postInvalidate(); isMoving = true; } else { isMoving = false; } super.computeScroll(); } /** 開打界面 */ public void show(){ if(!isShow && !isMoving){ LoginView.this.startMoveAnim(-viewHeight, viewHeight, mDuration); isShow = true; Log.d("isShow", "true"); changed(); } } /** 關閉界面 */ public void dismiss(){ if(isShow && !isMoving){ LoginView.this.startMoveAnim(0, -viewHeight, mDuration); isShow = false; Log.d("isShow", "false"); changed(); } } /** 是否打開 */ public boolean isShow(){ return isShow; } /** 獲取能否夠拖動*/ public boolean isSlidingEnabled() { return mEnabled; } /** 設置能否夠拖動*/ public void setSlidingEnabled(boolean enabled) { mEnabled = enabled; } /** * 設置監聽接口,實現遮罩層效果 */ public void setOnStatusListener(onStatusListener listener){ this.statusListener = listener; } public void setOutsideTouchable(boolean touchable) { mOutsideTouchable = touchable; } /** * 顯示狀態發生改變時候運行回調借口 */ public void changed(){ if(statusListener != null){ if(isShow){ statusListener.onShow(); }else{ statusListener.onDismiss(); } } } /** 監聽接口*/ public onStatusListener statusListener; /** * 監聽接口,來在主界面監聽界面變化狀態 */ public interface onStatusListener{ /** 開打狀態 */ public void onShow(); /** 關閉狀態 */ public void onDismiss(); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { // TODO Auto-generated method stub super.onLayout(changed, l, t, r, b); } }
事實上代碼大同小異。了解後你就能夠舉一反三,去自己的VIEW中實現自己想要的效果。
最後,上源代碼:下載地址
Android 仿 窗簾效果 和 登錄界面拖動效果 (Scroller類的應用) 附 2個DEMO及源代碼