Android自定義View實(shí)現(xiàn)豎向滑動(dòng)回彈效果
本文實(shí)例為大家分享了Android自定義View實(shí)現(xiàn)滑動(dòng)回彈的具體代碼,供大家參考,具體內(nèi)容如下
前言
Android 頁(yè)面滑動(dòng)的時(shí)候的回彈效果
一、關(guān)鍵代碼
public class UniversalBounceView extends FrameLayout implements IPull {
?
? ? private static final String TAG = "UniversalBounceView";
? ? //default.
? ? private static final int SCROLL_DURATION = 200;
? ? private static final float SCROLL_FRACTION = 0.4f;
?
? ? private static final int VIEW_TYPE_NORMAL = 0;
? ? private static final int VIEW_TYPE_ABSLISTVIEW = 1;
? ? private static final int VIEW_TYPE_SCROLLVIEW = 2;
?
? ? private static float VIEW_SCROLL_MAX = 720;
? ? private int viewHeight;
?
? ? private AbsListView alv;
? ? private OnBounceStateListener onBounceStateListener;
?
? ? private View child;
? ? private Scroller scroller;
? ? private boolean pullEnabled = true;
? ? private boolean pullPaused;
? ? private int touchSlop = 8;
?
? ? private int mPointerId;
?
? ? private float downY, lastDownY, tmpY;
? ? private int lastPointerIndex;
?
? ? private float moveDiffY;
? ? private boolean isNotJustInClickMode;
? ? private int moveDelta;
? ? private int viewType = VIEW_TYPE_NORMAL;
?
? ? public UniversalBounceView(Context context) {
? ? ? ? super(context);
? ? ? ? init(context);
? ? }
?
? ? public UniversalBounceView(Context context, AttributeSet attrs) {
? ? ? ? super(context, attrs);
? ? ? ? init(context);
? ? }
?
? ? public UniversalBounceView(Context context, AttributeSet attrs, int defStyleAttr) {
? ? ? ? super(context, attrs, defStyleAttr);
? ? ? ? init(context);
? ? }
?
? ? private void init(Context context) {
? ? ? ? scroller = new Scroller(context, new CustomDecInterpolator());
? ? ? ? touchSlop = (int) (ViewConfiguration.get(context).getScaledTouchSlop() * 1.5);
? ? }
?
? ? class CustomDecInterpolator extends DecelerateInterpolator {
?
? ? ? ? public CustomDecInterpolator() {
? ? ? ? ? ? super();
? ? ? ? }
?
? ? ? ? public CustomDecInterpolator(float factor) {
? ? ? ? ? ? super(factor);
? ? ? ? }
?
? ? ? ? public CustomDecInterpolator(Context context, AttributeSet attrs) {
? ? ? ? ? ? super(context, attrs);
? ? ? ? }
?
? ? ? ? @Override
? ? ? ? public float getInterpolation(float input) {
? ? ? ? ? ? return (float) Math.pow(input, 6.0 / 12);
? ? ? ? }
? ? }
?
? ? private void checkCld() {
? ? ? ? int cnt = getChildCount();
? ? ? ? if (1 <= cnt) {
? ? ? ? ? ? child = getChildAt(0);
? ? ? ? } else if (0 == cnt) {
? ? ? ? ? ? pullEnabled = false;
? ? ? ? ? ? child = new View(getContext());
? ? ? ? } else {
? ? ? ? ? ? throw new ArrayIndexOutOfBoundsException("child count can not be less than 0.");
? ? ? ? }
? ? }
?
? ? @Override
? ? protected void onFinishInflate() {
? ? ? ? checkCld();
? ? ? ? super.onFinishInflate();
? ? }
?
? ? @Override
? ? protected void onSizeChanged(int w, int h, int oldw, int oldh) {
? ? ? ? super.onSizeChanged(w, h, oldw, oldh);
? ? ? ? viewHeight = h;
? ? ? ? VIEW_SCROLL_MAX = h * 1 / 3;
? ? }
?
? ? private boolean isTouch = true;
?
? ? public void setTouch(boolean isTouch) {
? ? ? ? this.isTouch = isTouch;
? ? }
?
? ? @Override
? ? public boolean dispatchTouchEvent(MotionEvent ev) {
? ? ? ? if (!isTouch) {
? ? ? ? ? ? return true;
? ? ? ? } else {
? ? ? ? ? ? try {
? ? ? ? ? ? ? ? if (isPullEnable()) {
? ? ? ? ? ? ? ? ? ? if (ev.getActionMasked() == MotionEvent.ACTION_MOVE) {
? ? ? ? ? ? ? ? ? ? ? ? if (Math.abs(ev.getY() - tmpY) < touchSlop) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? return super.dispatchTouchEvent(ev);
? ? ? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? ? ? tmpY = Integer.MIN_VALUE;
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? return takeEvent(ev);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? } catch (IllegalArgumentException | IllegalStateException e) {
? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? }
? ? ? ? ? ? if (getVisibility() != View.VISIBLE) {
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? ? ? return super.dispatchTouchEvent(ev);
? ? ? ? }
? ? }
?
? ? private boolean takeEvent(MotionEvent ev) {
? ? ? ? int action = ev.getActionMasked();
? ? ? ? switch (action) {
? ? ? ? ? ? case MotionEvent.ACTION_DOWN:
? ? ? ? ? ? ? ? mPointerId = ev.getPointerId(0);
? ? ? ? ? ? ? ? downY = ev.getY();
? ? ? ? ? ? ? ? tmpY = downY;
? ? ? ? ? ? ? ? scroller.setFinalY(scroller.getCurrY());
? ? ? ? ? ? ? ? setScrollY(scroller.getCurrY());
? ? ? ? ? ? ? ? scroller.abortAnimation();
? ? ? ? ? ? ? ? pullPaused = true;
? ? ? ? ? ? ? ? isNotJustInClickMode = false;
? ? ? ? ? ? ? ? moveDelta = 0;
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case MotionEvent.ACTION_UP:
? ? ? ? ? ? case MotionEvent.ACTION_CANCEL:
? ? ? ? ? ? ? ? pullPaused = false;
? ? ? ? ? ? ? ? smoothScrollTo(0);
? ? ? ? ? ? ? ? if (isNotJustInClickMode) {
? ? ? ? ? ? ? ? ? ? ev.setAction(MotionEvent.ACTION_CANCEL);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? postDelayed(new Runnable() {
? ? ? ? ? ? ? ? ? ? @Override
? ? ? ? ? ? ? ? ? ? public void run() {
? ? ? ? ? ? ? ? ? ? ? ? if (getScrollY() == 0 && onBounceStateListener != null) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? onBounceStateListener.overBounce();
? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }, 200);
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case MotionEvent.ACTION_MOVE:
? ? ? ? ? ? ? ? lastPointerIndex = ev.findPointerIndex(mPointerId);
? ? ? ? ? ? ? ? lastDownY = ev.getY(lastPointerIndex);
? ? ? ? ? ? ? ? moveDiffY = Math.round((lastDownY - downY) * getScrollFraction());
? ? ? ? ? ? ? ? downY = lastDownY;
? ? ? ? ? ? ? ? boolean canStart = isCanPullStart();
? ? ? ? ? ? ? ? boolean canEnd = isCanPullEnd();
? ? ? ? ? ? ? ? int scroll = getScrollY();
? ? ? ? ? ? ? ? float total = scroll - moveDiffY;
? ? ? ? ? ? ? ? if (canScrollInternal(scroll, canStart, canEnd)) {
? ? ? ? ? ? ? ? ? ? handleInternal();
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (Math.abs(scroll) > VIEW_SCROLL_MAX) {
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if ((canStart && total < 0) || (canEnd && total > 0)) {
? ? ? ? ? ? ? ? ? ? if (moveDelta < touchSlop) {
? ? ? ? ? ? ? ? ? ? ? ? moveDelta += Math.abs(moveDiffY);
? ? ? ? ? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? ? ? ? ? isNotJustInClickMode = true;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? if (onBounceStateListener != null) {
? ? ? ? ? ? ? ? ? ? ? ? onBounceStateListener.onBounce();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? scrollBy(0, (int) -moveDiffY);
? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? }
// ? ? ? ? ? ? ? ?else if ((total > 0 && canStart) || (total < 0 && canEnd)) {
// ? ? ? ? ? ? ? ? ? ?if (moveDelta < touchSlop) {
// ? ? ? ? ? ? ? ? ? ? ? ?moveDelta += Math.abs(moveDiffY);
// ? ? ? ? ? ? ? ? ? ?} else {
// ? ? ? ? ? ? ? ? ? ? ? ?isNotJustInClickMode = true;
// ? ? ? ? ? ? ? ? ? ?}
// ? ? ? ? ? ? ? ? ? ?scrollBy(0, -scroll);
// ? ? ? ? ? ? ? ? ? ?return true;
// ? ? ? ? ? ? ? ?}
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? case MotionEvent.ACTION_POINTER_UP:
? ? ? ? ? ? ? ? handlePointerUp(ev, 1);
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? default:
? ? ? ? ? ? ? ? break;
? ? ? ? }
? ? ? ? return super.dispatchTouchEvent(ev);
? ? }
?
? ? private boolean canScrollInternal(int scroll, boolean canStart, boolean canEnd) {
? ? ? ? boolean result = false;
? ? ? ? if ((child instanceof RecyclerView) || (child instanceof AbsListView) || child instanceof ScrollView) {
? ? ? ? ? ? viewType = VIEW_TYPE_ABSLISTVIEW;
? ? ? ? ? ? result = canStart && canEnd;
? ? ? ? } else if (child instanceof ScrollView || child instanceof NestedScrollView) {
? ? ? ? ? ? viewType = VIEW_TYPE_SCROLLVIEW;
? ? ? ? } else {
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? if (result) {
? ? ? ? ? ? isNotJustInClickMode = true;
? ? ? ? ? ? if (moveDelta < touchSlop) {
? ? ? ? ? ? ? ? moveDelta += Math.abs(moveDiffY);
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? ? ? return false;
? ? ? ? }
? ? ? ? if (((scroll == 0 && canStart && moveDiffY < 0) || (scroll == 0 && canEnd && moveDiffY > 0) || (!canStart && !canEnd))) {
? ? ? ? ? ? return true;
? ? ? ? }
? ? ? ? if (moveDelta < touchSlop) {
? ? ? ? ? ? moveDelta += Math.abs(moveDiffY);
? ? ? ? ? ? return true;
? ? ? ? } else {
? ? ? ? ? ? isNotJustInClickMode = true;
? ? ? ? }
? ? ? ? return false;
? ? }
?
? ? private void handleInternal() {
?
? ? }
?
? ? private void handlePointerUp(MotionEvent event, int type) {
? ? ? ? int pointerIndexLeave = event.getActionIndex();
? ? ? ? int pointerIdLeave = event.getPointerId(pointerIndexLeave);
? ? ? ? if (mPointerId == pointerIdLeave) {
? ? ? ? ? ? int reIndex = pointerIndexLeave == 0 ? 1 : 0;
? ? ? ? ? ? mPointerId = event.getPointerId(reIndex);
? ? ? ? ? ? // 調(diào)整觸摸位置,防止出現(xiàn)跳動(dòng)
? ? ? ? ? ? downY = event.getY(reIndex);
? ? ? ? }
? ? }
?
? ? @Override
? ? public boolean onTouchEvent(MotionEvent event) {
? ? ? ? return super.onTouchEvent(event);
? ? }
?
? ? private void smoothScrollTo(int value) {
? ? ? ? int scroll = getScrollY();
? ? ? ? scroller.startScroll(0, scroll, 0, value - scroll, SCROLL_DURATION);
? ? ? ? postInvalidate();
? ? }
?
? ? @Override
? ? public void computeScroll() {
? ? ? ? super.computeScroll();
? ? ? ? if (!pullPaused && scroller.computeScrollOffset()) {
? ? ? ? ? ? scrollTo(scroller.getCurrX(), scroller.getCurrY());
? ? ? ? ? ? postInvalidate();
? ? ? ? }
? ? }
?
? ? private float getScrollFraction() {
? ? ? ? float ratio = Math.abs(getScrollY()) / VIEW_SCROLL_MAX;
? ? ? ? ratio = ratio < 1 ? ratio : 1;
? ? ? ? float fraction = (float) (-2 * Math.cos((ratio + 1) * Math.PI) / 5.0f) + 0.1f;
? ? ? ? return fraction < 0.10f ? 0.10f : fraction;
? ? }
?
? ? @Override
? ? public boolean isPullEnable() {
? ? ? ? return pullEnabled;
? ? }
?
? ? @Override
? ? public boolean isCanPullStart() {
? ? ? ? if (child instanceof RecyclerView) {
? ? ? ? ? ? RecyclerView recyclerView = (RecyclerView) child;
? ? ? ? ? ? return !recyclerView.canScrollVertically(-1);
? ? ? ? }
? ? ? ? if (child instanceof AbsListView) {
? ? ? ? ? ? AbsListView lv = (AbsListView) child;
? ? ? ? ? ? return !lv.canScrollVertically(-1);
? ? ? ? }
? ? ? ? if (child instanceof RelativeLayout
? ? ? ? ? ? ? ? || child instanceof FrameLayout
? ? ? ? ? ? ? ? || child instanceof LinearLayout
? ? ? ? ? ? ? ? || child instanceof WebView
? ? ? ? ? ? ? ? || child instanceof View) {
? ? ? ? ? ? return child.getScrollY() == 0;
? ? ? ? }
? ? ? ? return false;
? ? }
?
? ? @Override
? ? public boolean isCanPullEnd() {
? ? ? ? if (child instanceof RecyclerView) {
? ? ? ? ? ? RecyclerView recyclerView = (RecyclerView) child;
? ? ? ? ? ? return !recyclerView.canScrollVertically(1);
? ? ? ? }
? ? ? ? if (child instanceof AbsListView) {
? ? ? ? ? ? AbsListView lv = (AbsListView) child;
? ? ? ? ? ? int first = lv.getFirstVisiblePosition();
? ? ? ? ? ? int last = lv.getLastVisiblePosition();
? ? ? ? ? ? View view = lv.getChildAt(last - first);
? ? ? ? ? ? if (null == view) {
? ? ? ? ? ? ? ? return false;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? return (lv.getCount() - 1 == last) &&
? ? ? ? ? ? ? ? ? ? ? ? (view.getBottom() <= lv.getHeight());
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (child instanceof ScrollView) {
? ? ? ? ? ? View v = ((ScrollView) child).getChildAt(0);
? ? ? ? ? ? if (null == v) {
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? return child.getScrollY() >= v.getHeight() - child.getHeight();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (child instanceof NestedScrollView) {
? ? ? ? ? ? View v = ((NestedScrollView) child).getChildAt(0);
? ? ? ? ? ? if (null == v) {
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? } else {
? ? ? ? ? ? ? ? return child.getScrollY() >= v.getHeight() - child.getHeight();
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? if (child instanceof WebView) {
? ? ? ? ? ? return (((WebView) child).getContentHeight() * ((WebView) child).getScale()) - (((WebView) child).getHeight() + ((WebView) child).getScrollY()) <= 10;
? ? ? ? }
? ? ? ? if (child instanceof RelativeLayout
? ? ? ? ? ? ? ? || child instanceof FrameLayout
? ? ? ? ? ? ? ? || child instanceof LinearLayout
? ? ? ? ? ? ? ? || child instanceof View) {
? ? ? ? ? ? return (child.getScrollY() == 0);
? ? ? ? }
? ? ? ? return false;
? ? }
?
? ? /**
? ? ?* 通過(guò)addView實(shí)現(xiàn)效果回彈效果
? ? ?*
? ? ?* @param replaceChildView 需要替換的View
? ? ?*/
? ? public void replaceAddChildView(View replaceChildView) {
? ? ? ? if (replaceChildView != null) {
? ? ? ? ? ? removeAllViews();
? ? ? ? ? ? child = replaceChildView;
? ? ? ? ? ? addView(replaceChildView);
? ? ? ? }
? ? }
?
? ? public void setPullEnabled(boolean enable) {
? ? ? ? pullEnabled = enable;
? ? }
?
? ? public interface OnBounceStateListener {
? ? ? ? public void onBounce();
?
? ? ? ? public void overBounce();
? ? }
?
? ? public void setOnBounceStateListener(OnBounceStateListener onBounceStateListener) {
? ? ? ? this.onBounceStateListener = onBounceStateListener;
? ? }
?
? ? @Override
? ? public boolean dispatchKeyEvent(KeyEvent event) {
? ? ? ? try {
? ? ? ? ? ? return super.dispatchKeyEvent(event);
? ? ? ? } catch (IllegalArgumentException | IllegalStateException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? return false;
? ? }
?
? ? @Override
? ? public void dispatchWindowFocusChanged(boolean hasFocus) {
? ? ? ? try {
? ? ? ? ? ? super.dispatchWindowFocusChanged(hasFocus);
? ? ? ? } catch (IllegalArgumentException | IllegalStateException e) {
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? }
?
?
}
二、注意要點(diǎn)
滑動(dòng)結(jié)束的時(shí)候要防止動(dòng)畫(huà)抖動(dòng)
private void handlePointerUp(MotionEvent event, int type) {
? ? ? ? int pointerIndexLeave = event.getActionIndex();
? ? ? ? int pointerIdLeave = event.getPointerId(pointerIndexLeave);
? ? ? ? if (mPointerId == pointerIdLeave) {
? ? ? ? ? ? int reIndex = pointerIndexLeave == 0 ? 1 : 0;
? ? ? ? ? ? mPointerId = event.getPointerId(reIndex);
? ? ? ? ? ? // 調(diào)整觸摸位置,防止出現(xiàn)跳動(dòng)
? ? ? ? ? ? downY = event.getY(reIndex);
? ? ? ? }
? ? }?總結(jié)
以上就是文章的主要內(nèi)容,實(shí)現(xiàn)了豎向滑動(dòng)回彈的效果。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android4.4 WebAPI實(shí)現(xiàn)拍照上傳功能
這篇文章主要介紹了Android4.4 WebAPI實(shí)現(xiàn)拍照上傳功能,本文給出4.4版本后拍照上傳的具體實(shí)現(xiàn)方法,感興趣的小伙伴們可以參考一下2016-07-07
Android開(kāi)發(fā)實(shí)現(xiàn)圖片大小與質(zhì)量壓縮及保存
這篇文章主要為大家介紹了Android開(kāi)發(fā)實(shí)現(xiàn)圖片大小與質(zhì)量壓縮及保存的方法,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
Android中使用BitmapShader類(lèi)來(lái)制作各種圖片的圓角
這篇文章主要介紹了Android中使用BitmapShader類(lèi)來(lái)制作各種圖片的圓角的方法,文中隨教程講解帶出的例子可以輕松控制圖片圓形的變換,很好很強(qiáng)大,需要的朋友可以參考下2016-04-04
Android開(kāi)發(fā)基于ViewPager+GridView實(shí)現(xiàn)仿大眾點(diǎn)評(píng)橫向滑動(dòng)功能
這篇文章主要介紹了Android開(kāi)發(fā)基于ViewPager+GridView實(shí)現(xiàn)仿大眾點(diǎn)評(píng)橫向滑動(dòng)功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
android使用intent傳遞參數(shù)實(shí)現(xiàn)乘法計(jì)算
這篇文章主要為大家詳細(xì)介紹了android使用intent傳遞參數(shù)實(shí)現(xiàn)乘法計(jì)算,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04
Android實(shí)現(xiàn)偵聽(tīng)電池狀態(tài)顯示、電量及充電動(dòng)態(tài)顯示的方法
這篇文章主要介紹了Android實(shí)現(xiàn)偵聽(tīng)電池狀態(tài)顯示、電量及充電動(dòng)態(tài)顯示的方法,非常實(shí)用的功能,需要的朋友可以參考下2014-09-09
Kotlin 匿名類(lèi)實(shí)現(xiàn)接口和抽象類(lèi)的區(qū)別詳解
這篇文章主要介紹了Kotlin 匿名類(lèi)實(shí)現(xiàn)接口和抽象類(lèi)的區(qū)別詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
基于Android實(shí)現(xiàn)一個(gè)簡(jiǎn)易音樂(lè)播放器
在Android平臺(tái)上開(kāi)發(fā)一個(gè)音樂(lè)播放器是一項(xiàng)常見(jiàn)的任務(wù),這涉及到對(duì)音頻文件的處理、用戶界面設(shè)計(jì)以及多媒體框架的運(yùn)用,本項(xiàng)目基于樣例代碼進(jìn)行擴(kuò)展,雖然功能相對(duì)簡(jiǎn)單,但包含了Android音樂(lè)播放器開(kāi)發(fā)的核心知識(shí)點(diǎn),需要的朋友可以參考下2024-08-08

