1. 程式人生 > >Android實現滑動的7種方法

Android實現滑動的7種方法

    在Android4.x之後,滑動操作大量的出現在Android系統中,滑動的操作方式具有更好的使用者體驗。所以本章會對Android中

實現滑動效果的方式做一個小歸納。

    在介紹滑動方法前,先講一下Android的座標系,Android的座標系分為兩種。

    1,Android座標,

    以螢幕的左上角為座標0點,0點向右為X軸正方向,0點向下為Y軸的正方向。在觸控事件中event.getRawX()與event.getRawY()可一個獲得

觸點的X座標值與Y軸座標值。

    2,檢視座標

    以父控制元件的左上角為座標0點,0點向右為X軸的正方向,0點向下為Y軸的正方向。在觸控事件中event.getX(),與event.getY()可以獲得在檢視座標中

X軸座標與Y軸座標。

    在Android中系統提供了非常多的方法來獲取座標值和相對位置,我剛學的時候常常弄混。我就畫了張圖,來理清這些問題。


    View提供的獲取座標的方法。

getLeft():view本身左邊到父控制元件左邊的距離。

getRight():view本身右邊到父控制元件的左邊的距離。

getTop():view本身頂部到父控制元件頂部的距離。

getBottom():view本身底部到父控制元件頂部的距離。

    MotionEvent提供的觸點獲取座標的的方法。

getX(),檢視座標X的座標

getY(),檢視座標Y軸座標。

getRawX(),Android座標系X軸座標。

getRawY(),Android座標系Y軸座標。

      講完座標系後,我們正式歸納scroll滑動的方法,這裡我們就以實現View跟隨手指觸控的滑動為例。

在計算手指偏移的時候我們有兩種方法。源於兩種座標系的選擇(Android座標系,檢視座標系);

檢視座標

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //檢視座標
        int x = (int) event.getX();
        int y = (int) event.getY();
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                lastX = x;
                lastY = y;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = x - lastX;//X偏移
                int offsetY = y - lastY;//Y偏移
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }
Android座標
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //android座標
        int rawX = (int) event.getRawX();
        int rawY = (int) event.getRawY();
        switch(event.getAction()){
            case MotionEvent.ACTION_DOWN:
                lastX = rawX;
                lastY = rawY;
                break;
            case MotionEvent.ACTION_MOVE:
                int offsetX = rawX - lastX;
                int offsetY = rawY - lastY;
                layout(getLeft() + offsetX, getTop() + offsetY,getRight() + offsetX, getBottom() + offsetY);
                lastX = rawX;
                lastY = rawY;
                break;
            case MotionEvent.ACTION_UP:
                break;
        }
        return true;
    }

一,Layout方法。

呼叫Layout方法對自己重新佈局。達到移動的效果。

     layout(getLeft() + offsetX, getTop() + offsetY,getRight() + offsetX, getBottom() + offsetY);

二,setOffsetLeftAndRight與setOffsetTopAndRight方法。
     offsetLeftAndRight(offsetX);
     offsetTopAndBottom(offsetY);

三,LayoutParams方法。

LayoutParams這個類相信大家都不陌生,這個類是View(子檢視)向ViewGroup(父檢視)傳達自己意願的方式,是ViewGroup的內部類。

不同ViewGroup有不同的LayoutParams的實現類。所以這個使用比較基礎的MarginLayoutParams。

     ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
     lp.leftMargin  = getLeft() + offsetX;
     lp.topMargin = getTop() + offsetY;
     setLayoutParams(lp);

四,ScrollTo()與 scrollBy().

scrollTo(x,y)標示移動到(x,y)這個座標。

scrollBy (dx,dy),dx,dy表示X軸與Y軸方向上的偏移量。

同時scroll&移動的並不是View本身,移動的是View的內容,ViewGroup使用移動的是所有的子View。

所以個要想View有滑動效果。我應該呼叫的其父控制元件(getParent())的scrol$方法。然後再想想相對運動。所以程式碼如下。

     ((View)getParent()).scrollBy(-offsetX, -offsetY);
     ((View)getParent()).scrollTo(((View)getParent()).getScrollX() -offsetX,((View)getParent()).getScrollY() -offsetY);

五,Scroller。

大家看到這個類的名稱就知道,它和scrollTo與scrollBy方法羈絆很深。其實功能是一樣的。不多它可以定義滑動的時長,而ScrollTo與scrollBy

方法幾乎都是一瞬間完成的。

Scroller的使用步驟。

1,初始化:

mScroller = new Scroller(context);

2,重寫computeScroll()方法。
    @Override
    public void computeScroll() {
        super.computeScroll();
        if (mScroller.computeScrollOffset()) {
            ((View) getParent()).scrollTo(
                    mScroller.getCurrX(),
                    mScroller.getCurrY()
            );
            //通過重繪製來不停的呼叫computeScroll
            invalidate();
        }
    }

3,啟動滑動,這裡我們實現當手指擡起,即ACTION_UP響應時。回到初始位置。
    case MotionEvent.ACTION_UP:
         View viewGroup = (View) getParent();
         mScroller.startScroll(viewGroup.getScrollX(), viewGroup.getScrollY(), -viewGroup.getScrollX(), -viewGroup.getScrollY(), 3000);
         invalidate();
         break;

6,屬性動畫。

      屬性動畫

7,ViewDragHelper方法。

     ViewDragHelper是所有滑動方法中最複雜的,但是也是功能最強大的。下面是ViewDragHelper的使用步驟:

1,ViewDragHelper的初始化:

    //第一步
    private void initView() {
        mViewDragHelper = ViewDragHelper.create(this, mCallback);
    }

 2,處理事件攔截:

    //第二步
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //將觸控事件傳遞給ViewDragHelper,此操作必不可少
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

 3,重寫computeScroll()方法。

    //第三步
    @Override
    public void computeScroll() {
        //super.computeScroll();
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }

4,處理ViewDragHelper.Callback()回撥,重寫一些重要的方法:
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //開始滑動檢測的條件,返回true開始檢測,即指定跟隨手指滑動的View
            return child == mMainView;
        }
        
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            return left;//通常只要這樣寫MainView在水平方向就能滑動了
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;//預設返回0,不能滑動。
        }

以上是全部步驟。我直接上程式碼,是我自定義的DrawerLayout側滑選單。
package com.example.songbinwang.littledemo.view;

import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;

/**
 * Created by songbinwang on 2016/6/2.
 */
public class MyDrawerLayout extends FrameLayout {

    View mMenuView, mMainView;
    ViewDragHelper mViewDragHelper;
    private int mMenuWidth;

    //第四步
    ViewDragHelper.Callback mCallback = new ViewDragHelper.Callback() {

        @Override
        public boolean tryCaptureView(View child, int pointerId) {
            //開始滑動檢測的條件,返回true開始檢測
            return child == mMainView;
        }

        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //通常只要return left就可以實現滑動。
            //return left;
            //這裡我們複雜一點,控制其滑動的範圍為(leftPadding, leftPadding+mMenuView.Width)
            int newLeft = Math.min(Math.max(getPaddingLeft(), left), getPaddingLeft() + mMenuWidth);
            return newLeft;
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            return 0;
        }

        /**
         * 使用者手指離開時回撥。
         * @param releasedChild
         * @param xvel
         * @param yvel
         */
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            super.onViewReleased(releasedChild, xvel, yvel);
            if (mMainView.getLeft() > mMenuWidth / 2) {
                //滑到一半時展開選單
                mViewDragHelper.smoothSlideViewTo(mMainView, mMenuWidth, 0);
                ViewCompat.postInvalidateOnAnimation(MyDrawerLayout.this);
            } else {
                //未滑到一半時,關閉選單
                mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
                ViewCompat.postInvalidateOnAnimation(MyDrawerLayout.this);
            }
        }

        /**
         * 使用者觸控到MainView時回撥
         * @param capturedChild
         * @param activePointerId
         */
        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
        }

        /**
         * 滑動狀態改變時呼叫
         * @param state
         */
        @Override
        public void onViewDragStateChanged(int state) {
            super.onViewDragStateChanged(state);
        }

        /**
         * 在位置改變時回撥,通常用於滑動時進行縮放效果
         * @param changedView
         * @param left
         * @param top
         * @param dx
         * @param dy
         */
        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
        }
    };


    public MyDrawerLayout(Context context) {
        super(context);
        initView();
    }

    public MyDrawerLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView();
    }

    public MyDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    public MyDrawerLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initView();
    }

    //第一步
    private void initView() {
        mViewDragHelper = ViewDragHelper.create(this, mCallback);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mMenuView = getChildAt(0);
        mMainView = getChildAt(1);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mMenuWidth = mMenuView.getMeasuredWidth();
    }

    //第二步
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return mViewDragHelper.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //將觸控事件傳遞給ViewDragHelper,此操作必不可少
        mViewDragHelper.processTouchEvent(event);
        return true;
    }

    //第三步
    @Override
    public void computeScroll() {
        //super.computeScroll();
        if (mViewDragHelper.continueSettling(true)) {
            ViewCompat.postInvalidateOnAnimation(this);
        }
    }
}

程式碼的引用:
<?xml version="1.0" encoding="utf-8"?>
<com.example.songbinwang.littledemo.view.MyDrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/menu"
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:background="@android:color/holo_red_light"></FrameLayout>

    <FrameLayout
        android:id="@+id/main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_light"></FrameLayout>
</com.example.songbinwang.littledemo.view.MyDrawerLayout>





相關推薦

Android群英傳——第五章實現滑動7方法(一二三)

實現滑動的基本思想: 當觸控View時,系統記下當前的座標 當手指移動時,系統記下當前移動點的座標 從而獲取了一個偏移量 通過這個偏移量修改這個View的座標 即可實現滑動的過程 要實現的效果如下: 方法一:layout方法 直接採用了基本思想:

Android群英傳——第五章實現滑動7方法(四)scrollTo和scrollBy

scrollTo()和scrollBy()的區別 scrollTo()代表讓View移動到一個具體的座標點:scrollTo(x,y) 移動到(x,y)點 scrollBy()則代表橫縱增量:設原點

Android實現滑動7方法

    在Android4.x之後,滑動操作大量的出現在Android系統中,滑動的操作方式具有更好的使用者體驗。所以本章會對Android中 實現滑動效果的方式做一個小歸納。     在介紹滑動方法前,先講一下Android的座標系,Android的座標系分為兩種。  

Android實現滑動的七方法實踐

  在講解滑動之前,要先熟悉一下安卓的座標系。安卓檢視有兩個座標系,一個是Android座標系,一個是檢視座標系。前者以螢幕的最左上角為原點,向右為X軸正方向,向下為Y軸正方向。後者以父檢視的左上角為原點,其它與前者一致。   而獲取座標的方法也可以分為兩類,View提供的

Android實現滑動的幾方法

下面通過一個例子來總結實現滑動的幾種方式,例子的主要功能就是讓我們的自定義View能夠隨著手指的移動而移動。 佈局檔案如下: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:andro

Android實現滑動的七方法

1.layout方法 每次移動後,呼叫layout()方法對自己重新佈局從而達到移動的效果 @Override public boolean onTouchEvent(MotionEvent event) { int x = (int) e

Android 實現滑動的幾方法(三)scrollTo 與 scrollBy

scrollTo(x,y): 表示移動到一個座標點(x,y) scrollBy(dx,dy) : 表示移動的增量為dx,dy 如果在ViewGroup中使用scrollTo和scrollBy,那麼移

java中實現執行緒同步的7方法

同步的方法: 一、同步方法   即有synchronized關鍵字修飾的方法。 由於java的每個物件都有一個內建鎖,當用此關鍵字修飾方法時, 內建鎖會保護整個方法。在呼叫該方法前,需要獲得內建鎖,否則就處於阻塞狀態。 注: synchronized關鍵字也可以修飾靜態方法

從暴力求解到動態規劃—— 7 方法求解連續子陣列的最大和問題(python實現

問題描述 已知一個數組 a[n],裡面存放著浮點數,可能是正數、負數或0。求它的所有連續子陣列中的最大和。 連續子陣列:指的是陣列的一個連續切片,即可以表示為 a[i:j],0≤i≤j<n。 連續子陣列的和:比如連續子陣列為 a[i:j] ,則和為

Android按鈕實現的兩方法

剛接觸Android開發,第一篇部落格,也就當做筆記吧, 這裡先說一個問題,在很多Android開發書中可能是版本太早的問題,都說res/layout下有一個main.xml檔案中放著排版資訊,但是個人在實際中見到的是res/menu下是main.xml中只有少量其他資訊,

7方法實現陣列去重

前言去重是開發中經常會碰到的一個熱點問題,不過目前專案中碰到的情況都是後臺介面使用SQL去重,簡單高效,基本不會讓前端處理去重。那麼前端處理去重會出現什麼情況呢?假如每頁顯示10條不同的資料,如果資料重複比較嚴重,那麼要顯示10條資料,可能需要傳送多個http請求才能夠篩選出10條不同的資料,而如果在後臺就去

CSS實現居中的7方法

實現HTML元素的居中 看似簡單,實則不然 水平居中比如容易,垂直居中比較難搞定,水平垂直都居中更不容易。在這個響應式佈局的年代,很難固定元素的寬高,俺統計了一下,目前的幾種方法。本文由淺入深逐個介紹,使用了同一段HTML程式碼: <div class="ce

[Android] Android 定時任務實現的三方法(以SeekBar的進度自動實現為例)

orien nta callback 間隔 end ++ tar protect bundle [Android] Android 定時任務實現的三種方法(以SeekBar的進度自動實現為例) 一、采用Handler與線程的sleep(long)方法 二、采用H

【譯】在React中實現條件渲染的7方法

原文地址:https://scotch.io/tutorials/7-ways-to-implement-conditional-rendering-in-react-applications 藉助React,我們可以構建動態且高度互動的單頁應用程式,充分利用這種互動性的一種方法是通過條件渲染。 目錄

Delphi 導出數據至Excel的7方法【轉】

dbase cas excel classes date pbo item 方式 down 轉自:http://blog.csdn.net/zang141588761/article/details/52275948 一; delphi 快速導出excel u

MyBatis無限級分類實現的兩方法--自關聯與map集合

except app exce utf-8 elf findall ldr ati tex 1、這回先創建數據庫吧 下表cid是CategoryId的縮寫,cname是CategoryName的縮寫,pid是parentId的縮寫 無限級分類一般都包含這三個屬性,至少也要包

Linux下產生隨機密碼的7方法

隨機密碼Linux下產生隨機密碼的7種方法1[root@test-6 ~]# date +%s | sha256sum | base64 | head -c 32 ; echoM2U0YTllN2I1NzZjNTNjZDZhYzM5NzIz2[root@test-6 ~]# < /dev/urandom

關於匿名內部類實現的兩方法

匿名內部類。這兩種方法都常用。 第一種方法Timer time= new Timer();time.schedule(new TimerTask() { @Override public void run() { // TODO Auto-gene

PHP獲取文件後綴名(提供7方法) 阿星小棧

blog path 一次 總結 HP 元素 xpl extension 所有 1.$file = ‘x.y.z.png‘;echo substr(strrchr($file, ‘.‘), 1);解析:strrchr($file, ‘.‘) strrchr() 函數

線程 實現的兩方法

ble pac name runnable end extend 當前 ride xtend 1 package thread; 2 3 public class MyRunnable implements Runnable { 4 5 privat