編輯:關於Android編程
實現滑動的基本思想是:當觸摸View時,系統記下當前觸摸點坐標;當手指移動時,系統記下移動後的觸摸點坐標,從而獲取到相對於前一次坐標點的偏移量,並通過偏移量來修改View的坐標,這樣不斷重復,從而實現滑動過程.
在View進行繪制時,會調用onLayout()方法來設置顯示的位置
通過修改View的left,top,right,bottom四個屬性來控制View的坐標,在每次回調onTouchEvent的時候,獲取一下觸摸點的坐標:
// 視圖坐標方式
@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;
int offsetY = y - lastY;
// 在當前left、top、right、bottom的基礎上加上偏移量
layout(getLeft() + offsetX,
getTop() + offsetY,
getRight() + offsetX,
getBottom() + offsetY);
// offsetLeftAndRight(offsetX);
// offsetTopAndBottom(offsetY);
break;
}
return true;
}
//使用getX(),getY()方法來獲取坐標值,即通過視圖坐標來獲取偏移量
使用getRawX(),getRawY()來獲取坐標,並使用絕對坐標來計算偏移量,要在每次執行完ACTION_MOVE的邏輯後,一定要重新設置初始坐標,這樣才能准確地獲取偏移量.
// 絕對坐標方式
@Override
public boolean onTouchEvent(MotionEvent event) {
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;
// 在當前left、top、right、bottom的基礎上加上偏移量
layout(getLeft() + offsetX,
getTop() + offsetY,
getRight() + offsetX,
getBottom() + offsetY);
// 重新設置初始坐標
lastX = rawX;
lastY = rawY;
break;
}
return true;
}
當計算出偏移量後,只需使用如下代碼就可以完成View的重新布局:
//同時對left和right進行偏移
offsetLeftAndRight(offsetX);
//同時對top和bottom進行偏移
offsetTopAndBottom(offsetY);
LayoutParams保存了一個View的布局參數,通過LayoutParams來動態改變View的位置參數,從而改變View位置效果.
使用getLayoutParams()來獲取一個View的LayoutParams.
通過setLayoutParams來改變其LayoutParams.
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 記錄觸摸點坐標
lastX = (int) event.getX();
lastY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
// 計算偏移量
int offsetX = x - lastX;
int offsetY = y - lastY;
ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) getLayoutParams();
// LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) getLayoutParams();
layoutParams.leftMargin = getLeft() + offsetX;
layoutParams.topMargin = getTop() + offsetY;
setLayoutParams(layoutParams);
break;
}
return true;
}
通過getLayoutParams()獲取LayoutParams時,需要根據View所在父布局的類型來設置不同的類型.
還可以使用ViewGroup.MarginLayoutParams來實現這樣的功能.
在View中,提供了scrollTo,scrollBy兩種方式來改變一個View的位置,兩者的區別:與英文中To與By的區別類似,scroll(x,y)表示移動到一個具體的坐標點(x,y),而scrollBy(dx,dy)表示移動的增量為dx,dy.
scrollTo,scrollBy方法移動的是View的content,即讓View的內容移動,如果在ViewGroup中使用scrollTo,scrollBy方法,那麼移動的將是所有子View,但如果在View中使用,那麼移動的將是View的內容.
將scrollBy中的參數dx和dy設置為正數,那麼content將向坐標軸負方向移動;如果將scrollBy中的參數dx和dy設置為負數,那麼content將向坐標軸正方向移動.
因此,要實現跟隨手指移動而滑動的效果,就必須將偏移量改為負值:
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getX();
lastY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - lastX;
int offsetY = y - lastY;
((View) getParent()).scrollBy(-offsetX, -offsetY);
break;
}
return true;
}
在使用絕對坐標時,也可以通過使用scroll方法來實現.
使用Scroller對象,需要三個步驟:
1.初始化Scroller
首先,通過它的構造方法來創建一個Scroller對象:
//初始化Scroller
mScroller=new Scroller(context);
2.重寫computeScroll()方法,實現模擬滑動
重寫computeScroll()方法,它是Scroller類的核心,系統在繪制View的時候會在draw()方法中調用該方法:
@Override
public void computeScroll() {
super.computeScroll();
// 判斷Scroller是否執行完畢
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(
mScroller.getCurrX(),
mScroller.getCurrY());
// 通過重繪來不斷調用computeScroll
invalidate();
}
}
Scroller類提供了computeScrollOffset()方法來判斷是否完成了整個滑動,通過getCurrX(),getCurrY()方法來獲得當前的滑動坐標,注意invalidate()方法,因為只能在computeScroll()方法中獲取模擬過程中的scrollX和scrollY坐標.但computeScroll()方法是不會自動調用的,只能通過invalidate()->draw()->computeScroll()來間接調用computeScroll()方法,所有需要調用invalidate()方法,實現循環獲取scrollX和scrollY的目的,而當模擬過程結束後,scroller.computeScrollOffset()方法會返回false,從而中斷循環,完成整個平滑移動過程.
3.startScroll開啟模擬過程
在需要使用平滑移動的事件中,使用Scroller類的startScroll()方法來開啟平滑移動過程.
startScroll()方法具有兩個重載方法:
public void startScroll(int startX,int startY,int dx,int dy,int duration)
public void startScroll(int startX,int startY,int dx,int dy)
它們的區別在於:是否具有指定的持續時長.其它參數分別為起始坐標與偏移量.
在獲取坐標時,使用getScrollX()和getScrollY()方法來獲取父視圖中content所滑動到的點的坐標.注意正負值,與scrollBy,scrollTo的情況相同.
例子:
演示一下如何使用Scroller類實現平滑移動.在這個實例中,讓子View跟隨手指的滑動而滑動,但是在手指離開屏幕是,讓子View平滑的移動到初始位置,即屏幕左上角:
public class DragView extends View {
private int lastX;
private int lastY;
private Scroller mScroller;
public DragView(Context context) {
super(context);
ininView(context);
}
public DragView(Context context, AttributeSet attrs) {
super(context, attrs);
ininView(context);
}
public DragView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
ininView(context);
}
private void ininView(Context context) {
setBackgroundColor(Color.BLUE);
// 初始化Scroller
mScroller = new Scroller(context);
}
@Override
public void computeScroll() {
super.computeScroll();
// 判斷Scroller是否執行完畢
if (mScroller.computeScrollOffset()) {
((View) getParent()).scrollTo(
mScroller.getCurrX(),
mScroller.getCurrY());
// 通過重繪來不斷調用computeScroll
invalidate();
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
int x = (int) event.getX();
int y = (int) event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
lastX = (int) event.getX();
lastY = (int) event.getY();
break;
case MotionEvent.ACTION_MOVE:
int offsetX = x - lastX;
int offsetY = y - lastY;
((View) getParent()).scrollBy(-offsetX, -offsetY);
break;
case MotionEvent.ACTION_UP:
// 手指離開時,執行滑動過程
View viewGroup = ((View) getParent());
mScroller.startScroll(
viewGroup.getScrollX(),
viewGroup.getScrollY(),
-viewGroup.getScrollX(),
-viewGroup.getScrollY());
invalidate();
break;
}
return true;
}
}
在startScroll()方法中,獲取子View移動的距離–getScrollX(),getScrollY(),並將偏移量設置為其相反數,從而將子View滑動到原位置.注意invalidate()方法,需要使用這個方法來通知View進行重繪,從而來調用computeScroll()的模擬過程.
ViewDragHelper可以實現各種不同的滑動 拖放需求.
使用方法:
初始化ViewDragHelper
首先,自然是需要初始化ViewDragHelper.ViewDragHelper通常定義在一個ViewGroup的內部,並通過其靜態工廠方法進行初始化.
mViewDragHelper=ViewDragHelper.create(this,callback);
第一個參數是要監聽的View,通常需要是一個ViewGroup,即parentView;第二個參數是一個Callback回調,這個回調是整個ViewDragHelper核心.
攔截事件
接下來,重寫事件攔截方法,將事件傳遞給ViewDragHelper進行處理:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//將觸摸事件傳遞給ViewDragHelper,此操作必不可少
mViewDragHelper.processTouchEvent(event);
return true;
}
處理computeScroll()
因為ViewDragHelp內部是通過Scroller來實現平滑移動的,所以需要實現處理computeScroll().
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
處理回調Callback
下面就是最關鍵的Callback實現:
private ViewDragHelper.Callback callback =
new ViewDragHelper.Callback() {
// 何時開始檢測觸摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果當前觸摸的child是mMainView時開始檢測
return mMainView == child;
}
// 觸摸到View後回調
@Override
public void onViewCaptured(View capturedChild,
int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
// 當拖拽狀態改變,比如idle,dragging
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
}
// 當位置改變的時候調用,常用與滑動時更改scale等
@Override
public void onViewPositionChanged(View changedView,
int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
}
// 處理垂直滑動
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
// 處理水平滑動
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
// 拖動結束後調用
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起後緩慢移動到指定位置
if (mMainView.getLeft() < 500) {
//關閉菜單
//相當於Scroller的startScroll方法
mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
} else {
//打開菜單
mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
實現側滑菜單的完整例子:
public class DragViewGroup extends FrameLayout {
private ViewDragHelper mViewDragHelper;
private View mMenuView, mMainView;
private int mWidth;
public DragViewGroup(Context context) {
super(context);
initView();
}
public DragViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
initView();
}
public DragViewGroup(Context context,
AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
@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);
mWidth = mMenuView.getMeasuredWidth();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return mViewDragHelper.shouldInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
//將觸摸事件傳遞給ViewDragHelper,此操作必不可少
mViewDragHelper.processTouchEvent(event);
return true;
}
private void initView() {
mViewDragHelper = ViewDragHelper.create(this, callback);
}
private ViewDragHelper.Callback callback =
new ViewDragHelper.Callback() {
// 何時開始檢測觸摸事件
@Override
public boolean tryCaptureView(View child, int pointerId) {
//如果當前觸摸的child是mMainView時開始檢測
return mMainView == child;
}
// 觸摸到View後回調
@Override
public void onViewCaptured(View capturedChild,
int activePointerId) {
super.onViewCaptured(capturedChild, activePointerId);
}
// 當拖拽狀態改變,比如idle,dragging
@Override
public void onViewDragStateChanged(int state) {
super.onViewDragStateChanged(state);
}
// 當位置改變的時候調用,常用與滑動時更改scale等
@Override
public void onViewPositionChanged(View changedView,
int left, int top, int dx, int dy) {
super.onViewPositionChanged(changedView, left, top, dx, dy);
}
// 處理垂直滑動
@Override
public int clampViewPositionVertical(View child, int top, int dy) {
return 0;
}
// 處理水平滑動
@Override
public int clampViewPositionHorizontal(View child, int left, int dx) {
return left;
}
// 拖動結束後調用
@Override
public void onViewReleased(View releasedChild, float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
//手指抬起後緩慢移動到指定位置
if (mMainView.getLeft() < 500) {
//關閉菜單
//相當於Scroller的startScroll方法
mViewDragHelper.smoothSlideViewTo(mMainView, 0, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
} else {
//打開菜單
mViewDragHelper.smoothSlideViewTo(mMainView, 300, 0);
ViewCompat.postInvalidateOnAnimation(DragViewGroup.this);
}
}
};
@Override
public void computeScroll() {
if (mViewDragHelper.continueSettling(true)) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
}
寫在前面的話: 看到標題這麼長可能大家有點抓狂了,是的,我在剛剛學這一篇的時候有一些不理解,什麼是布局泵?編輯每一個模板然後什麼是自定義Adapter?下面我們開始學習這
這次我們試水高德LBS開放平台,那麼,什麼是LBS?基於位置的服務,它是通過電信移動運營商的無線電通訊網絡(如GSM網、CDMA網)或外部定位方式(如GPS)獲取移動終端
WireShark是一個非常准確和穩定的tcp抓包工具,但看其40多m的安裝包就可以想象其功能的強大,借助其功能強大的表達式篩選器,可以迅速的篩選出來我們所需要報文和記錄
如圖是效果圖是仿餓了的點餐界面1.點擊左側的ListView,通過在在適配器中設置Item來改變顏色,再通過notifyDataSetInvalidated來刷新並用lv
前言Universal-Image-Loader,android-Vol