編輯:Android開發實例
android 提供的很多List控件如 listview、gridview 默認都會顯示一個fadingedge的東西,它在View的top和bottom處各顯示一個漸變半透的陰影以達到更好的視覺效果,但是這個帶來的副作用就是導致在性能不是那麼強勁的機器上,一些listview,gridview的拖動會顯得很不流暢,因為我們知道繪制帶Alpha的圖片是最耗時的。
我們的優化思路就是對這個fadingedge做一些修改,當view處於滾動狀態時,通過接口setVerticalFadingEdgeEna
具體修改如下:
- @Override
- public boolean onTouchEvent(MotionEvent ev) {
- if (!isEnabled()) {
- // A disabled view that is clickable still consumes the touch
- // events, it just doesn't respond to them.
- return isClickable() || isLongClickable();
- }
- if (mFastScroller != null) {
- boolean intercepted = mFastScroller.onTouchEvent(ev);
- if (intercepted) {
- return true;
- }
- }
- final int action = ev.getAction();
- View v;
- int deltaY;
- if (mVelocityTracker == null) {
- mVelocityTracker = VelocityTracker.obtain();
- }
- mVelocityTracker.addMovement(ev);
- switch (action & MotionEvent.ACTION_MASK) {
- case MotionEvent.ACTION_DOWN: {
- setVerticalFadingEdgeEnabled(false);
- mActivePointerId = ev.getPointerId(0);
- final int x = (int) ev.getX();
- final int y = (int) ev.getY();
- int motionPosition = pointToPosition(x, y);
- if (!mDataChanged) {
- if ((mTouchMode != TOUCH_MODE_FLING) && (motionPosition >= 0)
- && (getAdapter().isEnabled(motionPosition))) {
- // User clicked on an actual view (and was not stopping a fling). It might be a
- // click or a scroll. Assume it is a click until proven otherwise
- mTouchMode = TOUCH_MODE_DOWN;
- // FIXME Debounce
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
- } else {
- if (ev.getEdgeFlags() != 0 && motionPosition < 0) {
- // If we couldn't find a view to click on, but the down event was touching
- // the edge, we will bail out and try again. This allows the edge correcting
- // code in ViewRoot to try to find a nearby view to select
- return false;
- }
- if (mTouchMode == TOUCH_MODE_FLING) {
- // Stopped a fling. It is a scroll.
- createScrollingCache();
- mTouchMode = TOUCH_MODE_SCROLL;
- mMotionCorrection = 0;
- motionPosition = findMotionRow(y);
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_TOUCH_SCROLL);
- }
- }
- }
- if (motionPosition >= 0) {
- // Remember where the motion event started
- v = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = v.getTop();
- }
- mMotionX = x;
- mMotionY = y;
- mMotionPosition = motionPosition;
- mLastY = Integer.MIN_VALUE;
- break;
- }
- case MotionEvent.ACTION_MOVE: {
- final int pointerIndex = ev.findPointerIndex(mActivePointerId);
- final int y = (int) ev.getY(pointerIndex);
- deltaY = y - mMotionY;
- switch (mTouchMode) {
- case TOUCH_MODE_DOWN:
- case TOUCH_MODE_TAP:
- case TOUCH_MODE_DONE_WAITING:
- // Check if we have moved far enough that it looks more like a
- // scroll than a tap
- startScrollIfNeeded(deltaY);
- break;
- case TOUCH_MODE_SCROLL:
- if (PROFILE_SCROLLING) {
- if (!mScrollProfilingStarted) {
- Debug.startMethodTracing("AbsListViewScroll");
- mScrollProfilingStarted = true;
- }
- }
- if (y != mLastY) {
- deltaY -= mMotionCorrection;
- int incrementalDeltaY = mLastY != Integer.MIN_VALUE ? y - mLastY : deltaY;
- // No need to do all this work if we're not going to move anyway
- boolean atEdge = false;
- if (incrementalDeltaY != 0) {
- atEdge = trackMotionScroll(deltaY, incrementalDeltaY);
- }
- // Check to see if we have bumped into the scroll limit
- if (atEdge && getChildCount() > 0) {
- // Treat this like we're starting a new scroll from the current
- // position. This will let the user start scrolling back into
- // content immediately rather than needing to scroll back to the
- // point where they hit the limit first.
- int motionPosition = findMotionRow(y);
- if (motionPosition >= 0) {
- final View motionView = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = motionView.getTop();
- }
- mMotionY = y;
- mMotionPosition = motionPosition;
- invalidate();
- }
- mLastY = y;
- }
- break;
- }
- break;
- }
- case MotionEvent.ACTION_UP: {
- switch (mTouchMode) {
- case TOUCH_MODE_DOWN:
- case TOUCH_MODE_TAP:
- case TOUCH_MODE_DONE_WAITING:
- setVerticalFadingEdgeEnabled(true);
- final int motionPosition = mMotionPosition;
- final View child = getChildAt(motionPosition - mFirstPosition);
- if (child != null && !child.hasFocusable()) {
- if (mTouchMode != TOUCH_MODE_DOWN) {
- child.setPressed(false);
- }
- if (mPerformClick == null) {
- mPerformClick = new PerformClick();
- }
- final AbsListView.PerformClick performClick = mPerformClick;
- performClick.mChild = child;
- performClick.mClickMotionPosition = motionPosition;
- performClick.rememberWindowAttachCount();
- mResurrectToPosition = motionPosition;
- if (mTouchMode == TOUCH_MODE_DOWN || mTouchMode == TOUCH_MODE_TAP) {
- final Handler handler = getHandler();
- if (handler != null) {
- handler.removeCallbacks(mTouchMode == TOUCH_MODE_DOWN ?
- mPendingCheckForTap : mPendingCheckForLongPress);
- }
- mLayoutMode = LAYOUT_NORMAL;
- if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
- mTouchMode = TOUCH_MODE_TAP;
- setSelectedPositionInt(mMotionPosition);
- layoutChildren();
- child.setPressed(true);
- positionSelector(child);
- setPressed(true);
- if (mSelector != null) {
- Drawable d = mSelector.getCurrent();
- if (d != null && d instanceof TransitionDrawable) {
- ((TransitionDrawable) d).resetTransition();
- }
- }
- postDelayed(new Runnable() {
- public void run() {
- child.setPressed(false);
- setPressed(false);
- if (!mDataChanged) {
- post(performClick);
- }
- mTouchMode = TOUCH_MODE_REST;
- }
- }, ViewConfiguration.getPressedStateDuration());
- } else {
- mTouchMode = TOUCH_MODE_REST;
- }
- return true;
- } else if (!mDataChanged && mAdapter.isEnabled(motionPosition)) {
- post(performClick);
- }
- }
- mTouchMode = TOUCH_MODE_REST;
- break;
- case TOUCH_MODE_SCROLL:
- final int childCount = getChildCount();
- if (childCount > 0) {
- if (mFirstPosition == 0 && getChildAt(0).getTop() >= mListPadding.top &&
- mFirstPosition + childCount < mItemCount &&
- getChildAt(childCount - 1).getBottom() <=
- getHeight() - mListPadding.bottom) {
- mTouchMode = TOUCH_MODE_REST;
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- setVerticalFadingEdgeEnabled(true);
- } else {
- final VelocityTracker velocityTracker = mVelocityTracker;
- velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
- if (Math.abs(initialVelocity) > mMinimumVelocity) {
- if (mFlingRunnable == null) {
- mFlingRunnable = new FlingRunnable();
- }
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_FLING);
- mFlingRunnable.start(-initialVelocity);
- } else {
- mTouchMode = TOUCH_MODE_REST;
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- setVerticalFadingEdgeEnabled(true);
- }
- }
- } else {
- mTouchMode = TOUCH_MODE_REST;
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- setVerticalFadingEdgeEnabled(true);
- }
- break;
- }
- setPressed(false);
- // Need to redraw since we probably aren't drawing the selector anymore
- invalidate();
- final Handler handler = getHandler();
- if (handler != null) {
- handler.removeCallbacks(mPendingCheckForLongPress);
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mActivePointerId = INVALID_POINTER;
- if (PROFILE_SCROLLING) {
- if (mScrollProfilingStarted) {
- Debug.stopMethodTracing();
- mScrollProfilingStarted = false;
- }
- }
- break;
- }
- case MotionEvent.ACTION_CANCEL: {
- mTouchMode = TOUCH_MODE_REST;
- setPressed(false);
- View motionView = this.getChildAt(mMotionPosition - mFirstPosition);
- if (motionView != null) {
- motionView.setPressed(false);
- }
- clearScrollingCache();
- final Handler handler = getHandler();
- if (handler != null) {
- handler.removeCallbacks(mPendingCheckForLongPress);
- }
- if (mVelocityTracker != null) {
- mVelocityTracker.recycle();
- mVelocityTracker = null;
- }
- mActivePointerId = INVALID_POINTER;
- break;
- }
- case MotionEvent.ACTION_POINTER_UP: {
- onSecondaryPointerUp(ev);
- final int x = mMotionX;
- final int y = mMotionY;
- final int motionPosition = pointToPosition(x, y);
- if (motionPosition >= 0) {
- // Remember where the motion event started
- v = getChildAt(motionPosition - mFirstPosition);
- mMotionViewOriginalTop = v.getTop();
- mMotionPosition = motionPosition;
- }
- mLastY = y;
- break;
- }
- }
- return true;
- }
- ====================================================================
- private class FlingRunnable implements Runnable {
- private final Scroller mScroller;
- private int mLastFlingY;
- FlingRunnable() {
- mScroller = new Scroller(getContext());
- }
- void start(int initialVelocity) {
- int initialY = initialVelocity < 0 ? Integer.MAX_VALUE : 0;
- mLastFlingY = initialY;
- mScroller.fling(0, initialY, 0, initialVelocity,
- 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE);
- mTouchMode = TOUCH_MODE_FLING;
- post(this);
- if (PROFILE_FLINGING) {
- if (!mFlingProfilingStarted) {
- Debug.startMethodTracing("AbsListViewFling");
- mFlingProfilingStarted = true;
- }
- }
- }
- void startScroll(int distance, int duration) {
- int initialY = distance < 0 ? Integer.MAX_VALUE : 0;
- mLastFlingY = initialY;
- mScroller.startScroll(0, initialY, 0, distance, duration);
- mTouchMode = TOUCH_MODE_FLING;
- post(this);
- }
- private void endFling() {
- mTouchMode = TOUCH_MODE_REST;
- reportScrollStateChange(OnScrollListener.SCROLL_STATE_IDLE);
- clearScrollingCache();
- removeCallbacks(this);
- if (mPositionScroller != null) {
- removeCallbacks(mPositionScroller);
- }
- }
- public void run() {
- switch (mTouchMode) {
- default:
- return;
- case TOUCH_MODE_FLING: {
- if (mItemCount == 0 || getChildCount() == 0) {
- endFling();
- return;
- }
- final Scroller scroller = mScroller;
- boolean more = scroller.computeScrollOffset();
- final int y = scroller.getCurrY();
- // Flip sign to convert finger direction to list items direction
- // (e.g. finger moving down means list is moving towards the top)
- int delta = mLastFlingY - y;
- // Pretend that each frame of a fling scroll is a touch scroll
- if (delta > 0) {
- // List is moving towards the top. Use first view as mMotionPosition
- mMotionPosition = mFirstPosition;
- final View firstView = getChildAt(0);
- mMotionViewOriginalTop = firstView.getTop();
- // Don't fling more than 1 screen
- delta = Math.min(getHeight() - mPaddingBottom - mPaddingTop - 1, delta);
- } else {
- // List is moving towards the bottom. Use last view as mMotionPosition
- int offsetToLast = getChildCount() - 1;
- mMotionPosition = mFirstPosition + offsetToLast;
- final View lastView = getChildAt(offsetToLast);
- mMotionViewOriginalTop = lastView.getTop();
- // Don't fling more than 1 screen
- delta = Math.max(-(getHeight() - mPaddingBottom - mPaddingTop - 1), delta);
- }
- final boolean atEnd = trackMotionScroll(delta, delta);
- if (more && !atEnd) {
- invalidate();
- mLastFlingY = y;
- post(this);
- } else {
- endFling();
- AbsListView.this.setVerticalFadingEdgeEnabled(true);
- if (PROFILE_FLINGING) {
- if (mFlingProfilingStarted) {
- Debug.stopMethodTracing();
- mFlingProfilingStarted = false;
- }
- }
- }
- break;
- }
- }
- }
- }
修改後重新編譯,在性能稍差的機器上運行,滾動一下listview或者gridview,你就可以看到比較明顯的效果了!
有時候,為了實現項目中的需求,完成設計好的用戶交互體驗,不的不把這些View重新改造成自己想要的效果。 Android原生的ListView是不支持左右滑動的,但
在4.5.6節介紹過一個<include>標簽,該標簽可以在布局文件中引用另外一個布局文件,並可以覆蓋被引用布局文件根節點所有與布局相關的屬性,也就是
Fragment是Android honeycomb 3.0新增的概念,在如何
對於很多初學者或者剛工作的Android新手來說,我們的項目經驗還停留在做demo的階段,有沒有一種很low的感覺,並且當你真正上手做大項目的時候又