最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Android實現(xiàn)回彈ScrollView的原理

 更新時間:2022年04月18日 15:05:35   作者:天道_韋澤鵬  
這篇文章主要為大家詳細介紹了Android實現(xiàn)回彈ScrollView的原理,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下

本文實例為大家分享了Android實現(xiàn)回彈ScrollView的原理,供大家參考,具體內容如下

回彈的ScrollView

網上看到的通常是ElasticScrollView,
有一個Bug:點擊子控件滑動時,滑動無效,
所以針對此問題,我對ElasticScrollView做了改進。

原理圖

代碼

我在注釋中做了詳細的說明

import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.TranslateAnimation;
import android.widget.ScrollView;

/**
?* Created by Tindle Wei.
?*/
public class ElasticScrollView extends ScrollView {

? ? /**
? ? ?* 手指抖動誤差
? ? ?*/
? ? private static final int SHAKE_MOVE_VALUE = 8;
? ? /**
? ? ?* Scrollview內部的view
? ? ?*/
? ? private View innerView;
? ? /**
? ? ?* 記錄innerView最初的Y位置
? ? ?*/
? ? private float startY;
? ? /**
? ? ?* 記錄原始innerView的大小位置
? ? ?*/
? ? private Rect outRect = new Rect();

? ? private boolean animationFinish = true;

? ? public ElasticScrollView(Context context) {
? ? ? ? super(context);
? ? }

? ? public ElasticScrollView(Context context, AttributeSet attrs) {
? ? ? ? super(context, attrs);
? ? }

? ? /**
? ? ?* 繼承自View
? ? ?* 在xml的所有布局加載完之后執(zhí)行
? ? ?*/
? ? @Override
? ? protected void onFinishInflate() {
? ? ? ? if (getChildCount() > 0) {
? ? ? ? ? ? innerView = getChildAt(0);
? ? ? ? }
? ? }

? ? /**
? ? ?* 繼承自ViewGroup
? ? ?* 返回true, 截取觸摸事件
? ? ?* 返回false, 將事件傳遞給onTouchEvent()和子控件的dispatchTouchEvent()
? ? ?*/
? ? @Override
? ? public boolean onInterceptTouchEvent(MotionEvent ev) {

? ? ? ? // 判斷 點擊子控件 or 按住子控件滑動
? ? ? ? // 如果點擊子控件,則返回 false, 子控件響應點擊事件
? ? ? ? // 如果按住子控件滑動,則返回 true, 滑動布局
? ? ? ? switch (ev.getAction()) {
? ? ? ? ? ? case MotionEvent.ACTION_DOWN: {
? ? ? ? ? ? ? ? startY = ev.getY();
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? ? ? case MotionEvent.ACTION_MOVE: {
? ? ? ? ? ? ? ? float currentY = ev.getY();
? ? ? ? ? ? ? ? float scrollY = currentY - startY;

? ? ? ? ? ? ? ? // 是否返回 true
? ? ? ? ? ? ? ? return Math.abs(scrollY) > SHAKE_MOVE_VALUE;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? // 默認返回 false
? ? ? ? return super.onInterceptTouchEvent(ev);
? ? }

? ? @Override
? ? public boolean onTouchEvent(MotionEvent ev) {
? ? ? ? if (innerView == null) {
? ? ? ? ? ? return super.onTouchEvent(ev);
? ? ? ? } else {
? ? ? ? ? ? myTouchEvent(ev);
? ? ? ? }
? ? ? ? return super.onTouchEvent(ev);
? ? }

? ? public void myTouchEvent(MotionEvent ev) {
? ? ? ? if (animationFinish) {

? ? ? ? ? ? switch (ev.getAction()) {
? ? ? ? ? ? ? ? case MotionEvent.ACTION_DOWN:
? ? ? ? ? ? ? ? ? ? startY = ev.getY();
? ? ? ? ? ? ? ? ? ? super.onTouchEvent(ev);
? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? ? case MotionEvent.ACTION_UP:
? ? ? ? ? ? ? ? ? ? startY = 0;
? ? ? ? ? ? ? ? ? ? if (isNeedAnimation()) {
? ? ? ? ? ? ? ? ? ? ? ? animation();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? super.onTouchEvent(ev);
? ? ? ? ? ? ? ? ? ? break;

? ? ? ? ? ? ? ? case MotionEvent.ACTION_MOVE:
? ? ? ? ? ? ? ? ? ? final float preY =?
? ? ? ? ? ? ? ? ? ? ? ? ? startY == 0 ? ev.getY() : startY;
? ? ? ? ? ? ? ? ? ? float nowY = ev.getY();
? ? ? ? ? ? ? ? ? ? int deltaY = (int) (preY - nowY);
? ? ? ? ? ? ? ? ? ? startY = nowY;

? ? ? ? ? ? ? ? ? ? // 當滾動到最上或者最下時就不會再滾動,這時移動布局
? ? ? ? ? ? ? ? ? ? if (isNeedMove()) {
? ? ? ? ? ? ? ? ? ? ? ? if (outRect.isEmpty()) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? // 保存正常的布局位置
? ? ? ? ? ? ? ? ? ? ? ? ? ? outRect.set(innerView
? ? ? ? ? ? ? ? ? ? ? ? ? ? .getLeft(), ?innerView.getTop(),?
? ? ? ? ? ? ? ? ? ? ? ? ? ? innerView.getRight(),?
? ? ? ? ? ? ? ? ? ? ? ? ? ? innerView.getBottom());
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? // 移動布局
? ? ? ? ? ? ? ? ? ? ? ? // 這里 deltaY/2 為了操作體驗更好
? ? ? ? ? ? ? ? ? ? ? ? innerView.layout(innerView.getLeft(), ?
? ? ? ? ? ? ? ? ? ? ? ? innerView.getTop() - deltaY / 2, ?
? ? ? ? ? ? ? ? ? ? ? ? innerView.getRight(), ?
? ? ? ? ? ? ? ? ? ? ? ? innerView.getBottom() - deltaY / 2);
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? super.onTouchEvent(ev);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? default:
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? }
? ? }

? ? /**
? ? ?* 開啟移動動畫
? ? ?*/
? ? public void animation() {
? ? ? ? TranslateAnimation ta = new TranslateAnimation(0, 0, 0, outRect.top - innerView.getTop());
? ? ? ? ta.setDuration(400);
? ? ? ? // 減速變化 為了用戶體驗更好
? ? ? ? ta.setInterpolator(new DecelerateInterpolator());
? ? ? ? ta.setAnimationListener(new Animation.AnimationListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationStart(Animation animation) {
? ? ? ? ? ? ? ? animationFinish = false;
? ? ? ? ? ? }

? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationRepeat(Animation animation) {
? ? ? ? ? ? }

? ? ? ? ? ? @Override
? ? ? ? ? ? public void onAnimationEnd(Animation animation) {
? ? ? ? ? ? ? ? innerView.clearAnimation();
? ? ? ? ? ? ? ? // 設置innerView回到正常的布局位置
? ? ? ? ? ? ? ? innerView.layout(outRect.left,?
? ? ? ? ? ? ? ? outRect.top, outRect.right, outRect.bottom);
? ? ? ? ? ? ? ? outRect.setEmpty();
? ? ? ? ? ? ? ? animationFinish = true;
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? innerView.startAnimation(ta);
? ? }

? ? /**
? ? ?* 是否需要開啟動畫
? ? ?*/
? ? public boolean isNeedAnimation() {
? ? ? ? return !outRect.isEmpty();
? ? }

? ? /**
? ? ?* 是否需要移動布局
? ? ?*/
? ? public boolean isNeedMove() {
? ? ? ? int offset = innerView.getMeasuredHeight() - getHeight();
? ? ? ? offset = (offset < 0) ? 0: offset;
? ? ? ? int scrollY = getScrollY();
? ? ? ? return (offset == 0 || scrollY == offset);
? ? }
}

其他說明

1、下面是繼承關系:
ElasticScrollView extends ScrollView
ScrollView extends FrameLayout
FrameLayout extends ViewGroup
ViewGroup extends View

2、解決子控件 截取滑動監(jiān)聽的代碼在onInterceptTouchEvent() ,
通過監(jiān)聽Y的變化,來判斷是點擊子控件還是上拉下拉

3、getMeasuredHeight()返回的是原始測量高度,與屏幕無關,
getHeight()返回的是在屏幕上顯示的高度。
實際上在當屏幕可以包裹內容的時候,他們的值是相等的,只有當view超出屏幕后,才能看出他們的區(qū)別。當超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外沒有顯示的高度。

4、getScrollY()返回的是滑動View顯示部分的頂部

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。

相關文章

  • Android使用自定義View實現(xiàn)餅狀圖的實例代碼

    Android使用自定義View實現(xiàn)餅狀圖的實例代碼

    這篇文章主要介紹了Android使用自定義View實現(xiàn)餅狀圖的實例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-05-05
  • Android修改jar文件包名的方法分享

    Android修改jar文件包名的方法分享

    為了防止沖突,修改包名是最直接的途徑。下面這篇文章主要介紹了關于Android中修改jar文件包名的方法,文中介紹的非常詳細,需要的朋友可以參考借鑒,下面來一起看看吧。
    2017-03-03
  • Android Mms之:短信發(fā)送流程(圖文詳解)

    Android Mms之:短信發(fā)送流程(圖文詳解)

    本篇文章是對Android中的短信發(fā)送流程進行了詳細的分析介紹,需要的朋友參考下
    2013-05-05
  • Android接入USB掃碼模塊的方法

    Android接入USB掃碼模塊的方法

    這篇文章主要為大家詳細介紹了Android接入USB掃碼模塊的方法,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • Android系統(tǒng)添加Linux驅動

    Android系統(tǒng)添加Linux驅動

    今天小編就為大家分享一篇關于Android系統(tǒng)添加Linux驅動的文章,小編覺得內容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-10-10
  • Android判斷是否為飛行模式簡單方法

    Android判斷是否為飛行模式簡單方法

    這篇文章主要介紹了Android判斷是否為飛行模式簡單方法,本文使用最簡單的方法實現(xiàn)判斷是否為飛行模式,需要的朋友可以參考下
    2015-04-04
  • Flutter 構建一個常用的頁面框架

    Flutter 構建一個常用的頁面框架

    大多數(shù) App 中都會有底部導航欄,通過底部導航欄切換實現(xiàn)不同頁面之間的切換。在Flutter 中提供了 BottomNavigationBar組件實現(xiàn)底部導航。本篇介紹通過 BottomNavigationBar和 IndexedStack構建最為常見的 App 頁面框架。
    2021-05-05
  • 詳解Android廣播Broadcast的啟動流程

    詳解Android廣播Broadcast的啟動流程

    這篇文章主要為大家介紹了Android廣播Broadcast啟動流程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-03-03
  • Android用tabhost實現(xiàn) 界面切換,每個界面為一個獨立的activity操作

    Android用tabhost實現(xiàn) 界面切換,每個界面為一個獨立的activity操作

    這篇文章主要介紹了Android用tabhost實現(xiàn) 界面切換,每個界面為一個獨立的activity操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-09-09
  • 如何從UA分辨出Android設備類型

    如何從UA分辨出Android設備類型

    本文主要介紹如何從UA分辨出Android設備類型,這里整理了相關資料,來講解分辨Android設備類型,有興趣的小伙伴可以參考下
    2016-08-08

最新評論

湘潭县| 姜堰市| 申扎县| 建瓯市| 剑川县| 宁南县| 武邑县| 长岭县| 故城县| 忻州市| 托克逊县| 东海县| 鄂托克旗| 鹤壁市| 虹口区| 涟水县| 花莲县| 杂多县| 麟游县| 万载县| 三门峡市| 阳谷县| 鸡西市| 乡宁县| 贵州省| 汕头市| 胶南市| 安图县| 广安市| 克山县| 武定县| 株洲县| 泾源县| 喜德县| 北辰区| 余干县| 轮台县| 谷城县| 湟源县| 利辛县| 尖扎县|