編輯:關於Android編程
首先這個控件應該是繼承ViewGroup:
初始化:
public class MyGroup extends ViewGroup{
private Scroller mScroller;
private float mOriMotionX;
private float mLastMotionX;
private VelocityTracker mVelocityTracker;
private int mTouchState = TOUCH_STATE_REST;
private static final int TOUCH_STATE_REST = 0;
private int mTouchSlop;
private int mMaximumVelocity;
private static final int TOUCH_STATE_SCROLLING = 1;
private float mLastDownX;
private static final int DEFAULT_VALUE = 1000;
private int mNextScreen = -1;
private boolean mFlagLimitUp = false;
private static final int SNAP_VELOCITY = 700;
private int mCurrentScreen;
public MyGroup(Context context, AttributeSet attrs) {
super(context, attrs);
initWorkspace();
}
private void initWorkspace() {
mScroller = new Scroller(getContext());
setCurrentScreen(0);
final ViewConfiguration configuration = ViewConfiguration
.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();//這個是定義控件在scroll的最小像素距離
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); //速率,fling的一個以每秒滑動多少像素的值
}先重寫onmeasure:
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
final int width = MeasureSpec.getSize(widthMeasureSpec);
final int count = getChildCount();
for (int i = 0; i < count; i++) {
getChildAt(i).measure(widthMeasureSpec, heightMeasureSpec);
}
}@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
int paddingleft = 0;
int paddingTop = 0;
int childLeft = paddingleft;
final int count = getChildCount();
for (int i = 0; i < count; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != View.GONE) {
final int childWidth = child.getMeasuredWidth();
final int childHeight = child.getMeasuredHeight() ;
child.layout(childLeft, paddingTop, childLeft + childWidth,
childHeight + paddingTop);
childLeft += child.getMeasuredWidth(); //下個child的左邊距和第一個child的左邊距之間的距離正好是第一個child的width
}
}
}onInterceptTouchEvent只有返回false事件才會傳遞給控件裡的view,就是view的ontouch事件才可以捕捉 View裡的onTouchEvent返回為true,才能執行多次touch事件,事件才能得了傳遞
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
//如果為move事件,mTouchState為TOUCH_STATE_REST為靜止狀態,這個是防止子控件在滑動時又用手指去滑,這種情況下不響應這個事件
if ((action == MotionEvent.ACTION_MOVE)
&& (mTouchState != TOUCH_STATE_REST)) {
return true;
}
final float x = ev.getX();
switch (action) {
case MotionEvent.ACTION_MOVE:
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int touchSlop = mTouchSlop;
boolean xMoved = xDiff > touchSlop;
//如果xMoved為true表示手指在滑動
if (xMoved) {
mTouchState = TOUCH_STATE_SCROLLING;
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionX = x;
//mScroller.isFinished() 為true表示滑動結束了,這時候我們把狀態置為TOUCH_STATE_REST
mTouchState = mScroller.isFinished() ? TOUCH_STATE_REST
: TOUCH_STATE_SCROLLING;
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mTouchState = TOUCH_STATE_REST;
break;
default:
break;
}
//如果不是在靜止狀態,都返回true,這樣事件就不會傳遞給onTouchEvent了
return mTouchState != TOUCH_STATE_REST;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
mVelocityTracker.addMovement(ev);
int mScrollX = this.getScrollX(); //mScrollX表示X軸上的距離,往左滑動為正,這個時候屏幕向右移動
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
mOriMotionX = x;
mLastMotionX = x;
if (!mScroller.isFinished()) {
mScroller.abortAnimation();
}
mOriMotionX = x;
mLastMotionX = x;
mLastDownX = x;
return true;
case MotionEvent.ACTION_MOVE:
System.out.println("====action move mScrollX="+mScrollX);
final int buffer = getWidth() / 2; //這個表示在第一頁或是最後一頁還可以滑動半個屏幕
//如果是往後滑動,屏幕向前,那麼mLastMotionX是比x大的,deltaX是正的
int deltaX = (int) (mLastMotionX - x);
mLastMotionX = x;
System.out.println("=====deltaX="+deltaX);
//deltaX<0表示往前滑動
if (deltaX < 0) {
//這個是往右滑動,屏幕向左移動
scrollBy(Math.max(-mScrollX - buffer, deltaX), 0);
}else{
int availableToScroll = 0;
if (getChildCount() > 0) { //此時Workspace上可能未加任何item,count == 0
System.out.println("====rihgt="+(getChildAt(
getChildCount() - 1).getRight())+"avail="+(getChildAt(
getChildCount() - 1).getRight()- mScrollX - getWidth()
));
//getChildAt(getChildCount() - 1).getRight()為所有的view加一起的寬度,這裡加了3個view,一個view為1080,則這個值為3240
availableToScroll = getChildAt(
getChildCount() - 1).getRight()
- mScrollX - getWidth();
//availableToScroll + buffer可以滑動的最大距離,deltax為滑動的距離
scrollBy(Math.min(availableToScroll + buffer, deltaX), 0);
}
}
return true;
case MotionEvent.ACTION_UP:
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(DEFAULT_VALUE,
mMaximumVelocity);
int velocityX = (int) velocityTracker.getXVelocity();
//velocityX為手指滑動的速率,我們會跟給定值SNAP_VELOCITY做比較
if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {
// 這個時候是手指往前滑動,屏幕是向後移動
snapToScreen(mCurrentScreen - 1);
} else if (velocityX < -SNAP_VELOCITY
&& mCurrentScreen < getChildCount() - 1) {
// move right
snapToScreen(mCurrentScreen + 1);
} else {
snapToDestination(mLastMotionX < mOriMotionX);
}
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
mTouchState = TOUCH_STATE_REST;
if (Math.abs(mLastDownX - x) > 10) {
return true;
}
return false;
case MotionEvent.ACTION_CANCEL:
mTouchState = TOUCH_STATE_REST;
return false;
default:
break;
}
return true;
}/**滑動的距離,離屏寬幾分之一時,就開始執行換屏動作。*/
/**
* snapToDestination.
* mLastMotionX < mOriMotionX (mLastMotion < mOriMotionX)表示這個是手向後滑動,但屏幕是往前的,反之是向前
* forward為true為往前劃動,這時將scrollX加上三分之二的屏幕的寬度
* scrollX / screenWidth 來決定當前在哪個屏幕
* @param forward 是前進還是後退.
*/
public void snapToDestination(boolean forward) {
final int screenWidth = getWidth();
int scrollX = getScrollX();
if (forward) {
scrollX += screenWidth - screenWidth / 3;
} else {
scrollX += screenWidth / 3;
}
System.out.println("======screenWidth="+screenWidth+"scrollX / screenWidth="+(scrollX / screenWidth));
snapToScreen(scrollX / screenWidth);
}
/**
* 如果計算要滑動的距離:(whichScreen * getWidth())為滑動後的X坐標,this.getScrollX()為當前的坐標,兩者相減為滑動的距離
* Math.abs(delta) * 2為滑動的持續時間
*/
public void snapToScreen(int whichScreen) {
whichScreen = Math.max(0, Math.min(whichScreen, getChildCount() - 1));
boolean changingScreens = whichScreen != mCurrentScreen;
mNextScreen = whichScreen;
int mScrollX = this.getScrollX();
final int newX = whichScreen * getWidth();
final int delta = newX - mScrollX;
System.out.println("====snapToScreen delta="+delta);
mScroller.startScroll(mScrollX, 0, delta, 0, Math.abs(delta) * 2);
//invalidate非常重要,不然你移動一點頁面不能回復原狀
invalidate();
}
手把手教你做藍牙小車(二)
第5節 BTChat本節開始介紹Arduino藍牙模塊,配合Android應用,實現一個藍牙聊天應用。5.1 什麼是藍牙簡單說就是一種不同設備之間點對點通訊的技術。有大篇
Android實現標題顯示隱藏功能
本文實例嘗試模仿實現Android標題顯示隱藏功能,通過給listview設置 mListView.setOnTouchListener 監聽 重寫ontouch方法 監
Android 中View的繪制機制源碼分析 二
本篇文章接著上篇文章的內容來繼續討論View的繪制機制,上篇文章中我們主要講解了View的measure過程,今天我們就來學習ViewGroup的measur
從源碼角度剖析Handler機制
android中,在進行耗時操作更新UI用到最多的方法就是Handler了,一般在子線程中進行耗時操作(訪問網絡等),然後發送消息到UI線程(主線程),使得界面得以更新。