記錄Android中實現滑動的幾種方法
一、layou方法
程式碼示例如下,自定義一個view,在onTouchEvent()方法中計算手指滑動時的偏移量,呼叫view的layout()方法,在當前left、top、right、bottom上加上偏移量,實現view的滑動。
public class TestView extends View { private static final String TAG = "TestView"; public TestView(Context context) { super(context); } public TestView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } float startX; float startY; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //記錄初始觸控點座標 startX = event.getRawX(); startY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: //手指滑動過程中經過的點座標 float lastX = event.getRawX(); float lastY = event.getRawY(); //計算偏移量 int offsetX = (int) (lastX - startX); int offsetY = (int) (lastY - startY); //在當前的left、top、right、bottom上加上偏移量 layout(getLeft() + offsetX, getTop() + offsetY, getRight() + offsetX, getBottom() + offsetY); Log.e(TAG, "onTouchEvent: layout: " + offsetX + "/" + offsetY); //重新設定初始座標 ,這一步很重要 startX = lastX; startY = lastY; break; } return true; } }
二、offsetLeftAndRight()與offsetTopAndBottom()方法
這兩個方法相當於系統提供的一個對左右、上下移動的API的封裝。使用方法同樣先在onTouchEvent()方法中計算出手指滑動的偏移量,然後將偏移量作為方法引數傳入。關鍵程式碼如下:
float startX; float startY; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //記錄初始觸控點座標 startX = event.getRawX(); startY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: //手指滑動過程中經過的點座標 float lastX = event.getRawX(); float lastY = event.getRawY(); //計算偏移量 int offsetX = (int) (lastX - startX); int offsetY = (int) (lastY - startY); //呼叫方法,引數為計算出的偏移量 offsetLeftAndRight(offsetX); offsetTopAndBottom(offsetY); Log.e(TAG, "onTouchEvent: offsetLeftAndRight_offsetTopAndBottom: " + offsetX + "/" + offsetY); //重新設定初始座標 ,這一步很重要 startX = lastX; startY = lastY; break; } return true; }
三、修改view的LayoutParams
LayoutParams儲存著一個View的佈局引數。可以通過改變LayoutParams來動態修改一個View的位置引數,以此達到改變View位置的效果。關鍵程式碼如下:
float startX; float startY; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //記錄初始觸控點座標 startX = event.getRawX(); startY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: //手指滑動過程中經過的點座標 float lastX = event.getRawX(); float lastY = event.getRawY(); //計算偏移量 int offsetX = (int) (lastX - startX); int offsetY = (int) (lastY - startY); //修改LayoutParams引數,這裡要改變位置主要是修改LayoutParams的margin屬性,因此這裡直接使用 // ViewGroup.MarginLayoutParams以求方便,不必考慮view的父佈局型別。在一般應用中,如若有需要 // 修改LayoutParams的其他屬性,要注意getLayoutParams()必須獲取的是父佈局型別的LayoutParams。 ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams(); layoutParams.leftMargin = getLeft() + offsetX; layoutParams.topMargin = getTop() + offsetY; setLayoutParams(layoutParams); Log.e(TAG, "onTouchEvent: layoutParams: " + offsetX + "/" + offsetY); //重新設定初始座標,這一步很重要 startX = lastX; startY = lastY; break; } return true; }
四、scrollTo與scrollBy
1.scrollTo(int x,int y):移動到一個座標點
2.scrollBy(int x,int y):按照x軸增量x,y軸增量y的方式移動
這兩個方法實質上移動的是呼叫方法的物件所包含的內容。即:如果對一個ViewGroup(如LinearLayout)呼叫這兩個方法,會發現其內部所有的子view會一起發生移動;而如果對一個view(如TextView)呼叫,則會發現看不到移動的效果。要對view使用這兩個方法,實際上是需要對該view 的parent使用。程式碼示例如下:
1.在ViewGroup中,可以直接呼叫
float startX;
float startY;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startX = event.getRawX();
startY = event.getRawY();
break;
case MotionEvent.ACTION_MOVE:
float lastX = event.getRawX();
float lastY = event.getRawY();
int offsetX = (int) (lastX - startX);
int offsetY = (int) (lastY - startY);
//注意這裡的引數要變成相反數。由於作用物件是parent,所以實際發生
//移動的應該是parent,但我們的目標是子view。因此,以相對檢視關係
//來看,要讓子view產生所期望的移動效果,parent應該要向反方向移動。
scrollBy(-offsetX, -offsetY);
startX = lastX;
startY = lastY;
break;
}
return true;
}
2.在view中,需要由view的parent來呼叫
((View) getParent()).scrollBy(-offsetX, -offsetY);
五、使用Scroller實現帶過度動畫的滑動
使用前面幾種方法實現view位置的改變,如果不是通過手指觸控事件,而是通過如按鈕點選事件呼叫,view的位置變化是瞬間完成的。如果要讓位置變化有一個過渡動畫的過程,可以使用Scroller來實現。Scroller的使用可以分為3步驟:
1.初始化Scroller: Scroller scroller=new Scroller(context);
2.重寫computeScroll()方法,實現模擬滑動
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(scroller.getCurrX(), scroller.getCurrY());
invalidate();
}
}
3.startScroll()方法開啟模擬過程。可以在自定義view內部合適的位置呼叫下列方法,也可以在自定義view內部宣告一個呼叫該方法的方法,然後在外部Activity等需要呼叫的位置呼叫。
scroller.startScroll(startX, startY, dx, dy, duration);
程式碼示例:
自定義View
public class TestView extends View {
private static final String TAG = "TestView";
private Scroller mScroller;
public TestView(Context context) {
super(context);
}
public TestView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
mScroller = new Scroller(context);
}
public TestView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void computeScroll() {
super.computeScroll();
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(mScroller.getCurrX(), mScroller.getCurrY());
invalidate();
}
}
public void scrollAnimation(int startX, int startY, int dx, int dy, int duration) {
mScroller.startScroll(startX, startY, dx, dy, duration);
invalidate();
}
}
在Activity中呼叫:
@Override
public void onClick(View v) {
mTestView.scrollAnimation(0,0,-300,-400,2000);
}