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座標系,檢視座標系);
檢視座標
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; }
@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