app直播原始碼,列表越界後自動彈回原樣的效果
app直播原始碼,列表越界後自動彈回原樣的效果實現的相關程式碼
一、越界回彈效果的例子
二、效果拆分
下拉、上拉越界回彈:已到達列表頂部,執行下拉操作,此時會進行越界回彈
慣性滑動 越界回彈:快速滑動列表,列表已到達底部,但由於慣性會繼續前進一部分,此時會根據速度決定越界的距離以及回彈的速度
三、下拉、上拉越界回彈實現方式及程式碼
在實際專案中可能在多個頁面都需要回彈的效果,所以需要儘可能的減少與需要回彈view的耦合程式碼。所以可以自定義一個ViewGroup來包裹需要回彈的view。
public class PullOverLayout extends RelativeLayout { //允許的越界回彈的高度 protected float mOverScrollHeight; //被包裹的子View private View mChildView; //~~~~~ public PullOverLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mOverScrollHeight = DensityUtil.dp2px(context, 240); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); //獲得子控制元件,也就是被包裹的recycleview等 mChildView = getChildAt(0); } }
重寫事件攔截方法onInterceptTouchEvent()
通過豎直和水平方向Move的距離來判斷是否攔截該事件
dy大於0為手指向下滑動,dy小於0為手指向上滑動,通過setStatePTD、setStatePBU方法記錄Down或者Up
當被包裹的子view無法滑動時即發生越界時攔截並響應該Move事件
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mTouchX = ev.getX(); mTouchY = ev.getY(); break; case MotionEvent.ACTION_MOVE: float dx = ev.getX() - mTouchX; float dy = ev.getY() - mTouchY; if (Math.abs(dx) <= Math.abs(dy)) { if (dy > 0 && !ScrollingUtil.canChildScrollUp(mChildView)) { //1、下拉操作 LogUtil.d(TAG, "setStatePTD"); cp.setStatePTD(); return true; } else if (dy < 0 && !ScrollingUtil.canChildScrollDown(cp.getContent())) { //2、上拉操作 LogUtil.d(TAG, "setStatePBU"); cp.setStatePBU(); return true; } } break; } return super.onInterceptTouchEvent(ev); }
重寫onTouchEvent()方法響應攔截的事件
onTouchEvent用來響應事件,當move和up時,做相應的“越界”操作和“回彈”操作
下拉和上拉操作分別執行對應的越界和回彈操作即可
@Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { //當發生越界時由viewGroup響應Move事件 case MotionEvent.ACTION_MOVE: float dy = e.getY() - mTouchY; if (cp.isStatePTD()) { //1、如果是下拉操作的move事件,則執行頂部越界操作 dy = Math.min(cp.getOsHeight() * 2, dy); dy = Math.max(0, dy); cp.getAnimProcessor().scrollHeadByMove(dy); } else if (cp.isStatePBU()) { //2、如果是上拉操作的move事件,則執行底部越界的操作 dy = Math.min(cp.getOsHeight() * 2, Math.abs(dy)); dy = Math.max(0, dy); cp.getAnimProcessor().scrollBottomByMove(dy); } return true; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: if (cp.isStatePTD()) { //3、如果是下拉操作,當收到Up事件時,進行釋放操作下拉操作,即執行下拉回彈操作 cp.getAnimProcessor().dealPullDownRelease(); } else if (cp.isStatePBU()) { //4、如果是上拉操作,當收到up事件時,進行釋放上拉操作,執行上拉回彈操作 cp.getAnimProcessor().dealPullUpRelease(); } return true; } return super.onTouchEvent(e); }
第三步onTouchEvent中的下拉越界方法scrollHeadByMove(float moveY)和下拉釋放的回彈方法dealPullDownRelease()
public void scrollHeadByMove(float moveY) { float offsetY = decelerateInterpolator.getInterpolation(moveY / cp.getOsHeight() / 2) * moveY / 2; LogUtil.d(TAG, "scrollHeadByMove ", "offsetY:", String.valueOf(offsetY)); cp.getHeader().setVisibility(GONE); cp.getHeader().getLayoutParams().height = (int) Math.abs(offsetY); cp.getHeader().requestLayout(); cp.getContent().setTranslationY(offsetY); } public void dealPullDownRelease() { int start = getVisibleHeadHeight(); int end = 0; LogUtil.d(TAG, "dealPullDownRelease ", "start:", String.valueOf(start), "end:", String.valueOf(end)); ValueAnimator va = ValueAnimator.ofInt(start, end); va.setInterpolator(new DecelerateInterpolator()); va.addUpdateListener(animHeadUpListener); va.setDuration((int) (Math.abs(start - end) * animFraction)); va.start(); }
四、慣性滑動 越界回彈實現方式及程式碼
給被包裹的子view設定監聽
給定一個最低速度的閾值,當滑動速度超過這個值時根據慣性執行越界回彈操作
下方程式碼只實現了recycleview的越界回彈,listview類似。即被包裹的子view必須是recycleview
public void initChildViewFlingListener() { final View mChildView = cp.getContent(); //1、給被包裹的子view設定監聽事件,監聽fling操作,並記錄速度 final GestureDetector gestureDetector = new GestureDetector(cp.getContext(), new GestureDetector.SimpleOnGestureListener() { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { LogUtil.d(TAG, "onScroll ", "distanceY:", String.valueOf(distanceY)); return super.onScroll(e1, e2, distanceX, distanceY); } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { LogUtil.d(TAG, "onFling ", "velocityY:", String.valueOf(velocityY)); mVelocityY = velocityY; return false; } }); mChildView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { //手勢監聽的兩個任務:1.監聽fling動作,獲取速度 2.監聽滾動狀態變化 return gestureDetector.onTouchEvent(event); } }); if (mChildView instanceof RecyclerView) { ((RecyclerView) mChildView).addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { if (newState == RecyclerView.SCROLL_STATE_IDLE) { //2、到達越界速度閾值後,執行fling的頂部越界操作 if (mVelocityY >= OVER_SCROLL_MIN_VX && ScrollingUtil.isRecyclerViewToTop((RecyclerView) mChildView)) { cp.getAnimProcessor().animOverScrollTop(mVelocityY, cur_delay_times); mVelocityY = 0; cur_delay_times = ALL_DELAY_TIMES; } if (mVelocityY <= -OVER_SCROLL_MIN_VX && ScrollingUtil.isRecyclerViewToBottom((RecyclerView) mChildView)) { cp.getAnimProcessor().animOverScrollBottom(mVelocityY, cur_delay_times); mVelocityY = 0; cur_delay_times = ALL_DELAY_TIMES; } } super.onScrollStateChanged(recyclerView, newState); } }); } }
執行越界動畫
底部越界和頂部越界程式碼類似,此處只講解頂部越界即可
/** * 執行頂部越界 * 越界高度height ∝ vy/computeTimes,此處採用的模型是height=A*(vy + B)/computeTimes */ public void animOverScrollTop(float vy, int computeTimes) { LogUtil.d(TAG, "animOverScrollTop ", "vy:", String.valueOf(vy), "computeTimes:", String.valueOf(computeTimes)); if (cp.isOsTopLocked()) return; cp.lockOsTop(); cp.setStatePTD(); //1、計算越界高度,最大不超過設定的越界高度 int oh = (int) Math.abs(vy / computeTimes / 2); final int overHeight = Math.min(oh, cp.getOsHeight()); //2、計算越界時間 final int time = overHeight <= 50 ? 115 : (int) (0.3 * overHeight + 100); //3、執行越界操作 animLayoutByTime(0, overHeight, time, overScrollTopUpListener, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { //4、執行回彈操作 animLayoutByTime(overHeight, 0, 2 * time, overScrollTopUpListener, new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { cp.releaseOsTopLock(); } }); } }); } //最終執行的就是個屬性動畫 public void animLayoutByTime(int start, int end, long time, AnimatorUpdateListener listener, AnimatorListener animatorListener) { ValueAnimator va = ValueAnimator.ofInt(start, end); va.setInterpolator(new DecelerateInterpolator()); va.addUpdateListener(listener); va.addListener(animatorListener); va.setDuration(time); va.start(); }
以上就是 app直播原始碼,列表越界後自動彈回原樣的效果實現的相關程式碼,更多內容歡迎關注之後的文章
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69978258/viewspace-2851690/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 影片直播app原始碼,vue實現列表自動滾動的方式APP原始碼Vue
- 視訊直播原始碼,提醒類彈窗,到時間後自動彈出原始碼
- 直播視訊app原始碼,底部彈出的列表對話方塊APP原始碼
- 直播app原始碼,Flutter 彈窗元件APP原始碼Flutter元件
- 手機直播原始碼,Flutter 中的彈簧按鈕效果原始碼Flutter
- 原來如此!直播原始碼技術是這樣做的,直播互動篇原始碼
- 直播原始碼網站,實現文字自動翻轉效果原始碼網站
- 線上直播系統原始碼,實現搜尋後介面顯示商品列表效果原始碼
- 直播網站原始碼,安卓防止輸入框自動彈出網站原始碼安卓
- 直播app原始碼,登入時自動輸入密碼/自動記住密碼APP原始碼密碼
- app直播原始碼,彈出層 加遮罩層 頁面禁止滑動APP原始碼遮罩
- app直播原始碼,android中幾種常用的彈框APP原始碼Android
- 直播app系統原始碼,css優化滾動條樣式APP原始碼CSS優化
- 影片直播app原始碼,Swift動態修改Icon,消除系統彈窗APP原始碼Swift
- 直播app系統原始碼,輸入完內容後自動隱藏軟鍵盤APP原始碼
- app直播原始碼,收到訊息時出現彈窗APP原始碼
- 短視訊系統原始碼,直播間實現彈幕的自動傳送原始碼
- 直播平臺原始碼,Android中常用Dialog彈窗效果原始碼Android
- 線上直播系統原始碼,橫向無限迴圈滾動的單行彈幕效果原始碼
- 直播app原始碼,標題欄隨頁面滑動之title移動定位效果APP原始碼
- app直播原始碼,flutter Text自動計算文字內容的寬度APP原始碼Flutter
- app直播原始碼,Flutter 寬高自適應APP原始碼Flutter
- app直播原始碼,uniapp之自定義頂部樣式APP原始碼
- 直播原始碼,實現內容列表的豎向滑動原始碼
- app直播原始碼,下拉檢視圖文詳情效果APP原始碼
- 直播系統app原始碼,滑塊效果、slider用法APP原始碼IDE
- app直播原始碼,為文字/圖片新增按壓效果APP原始碼
- app直播原始碼,實現進度條自增長及漸變樣式APP原始碼
- 直播app系統原始碼,使用者登入後選擇記住密碼,下次自動填充密碼APP原始碼密碼
- 成品直播原始碼,點選滑動切換效果原始碼
- 線上直播原始碼,JS動態效果之,側邊欄滾動固定效果原始碼JS
- 直播商城原始碼,vue 彈窗 慣性滾動 加速滾動原始碼Vue
- 直播平臺原始碼,Uniapp text 樣式設定原始碼APP
- 線上直播系統原始碼,滑鼠懸停後彈出氣泡原始碼
- 直播app原始碼,使用者首次登入時彈出左右滑動導航頁APP原始碼
- 直播原始碼,懸浮窗滾動漸變色效果原始碼
- 直播app系統原始碼,對視訊新增擦拭去除效果APP原始碼
- 線上直播系統原始碼,平臺彈窗自適應裝置原始碼