展会信息港展会大全

android UI 优化之 AbsListView之深度优化
来源:互联网   发布日期:2015-11-26 09:34:29   浏览:938次  

导读:android 提供的很多List控件如 listview、gridview 默认都会显示一个fadingedge的东西,它在View的top和bottom处各显示一个渐变半透的阴影以达到更好的视觉效果,但是这个带来的副作 用就是导致在性能不是那......

android 提供的很多List控件如 listview、gridview 默认都会显示一个fadingedge的东西,它在View的top和bottom处各显示一个渐变半透的阴影以达到更好的视觉效果,但是这个带来的副作 用就是导致在性能不是那么强劲的机器上,一些listview,gridview的拖动会显得很不流畅,因为我们知道绘制带Alpha的图片是最耗时 的。

我们的优化思路就是对这个fadingedge做一些修改,当view处于滚动状态时,通过接口setVerticalFadingEdgeEnabled(false)让其不显示fadingedge,当view处于静止状态时,通过接口setVerticalFadingEdgeEnabled(true)恢复显示fadingedge。以上的listview和gridview等控件都是继承与AbsListView,所以我们直接修改framework中的AbsListView.java文件,就可以达到系统级的改动效果了。

具体修改如下:

@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,你就可以看到比较明显的效果了!

赞助本站

人工智能实验室

相关热词: UI 优化 AbsListView

相关内容
AiLab云推荐
展开

热门栏目HotCates

Copyright © 2010-2024 AiLab Team. 人工智能实验室 版权所有    关于我们 | 联系我们 | 广告服务 | 公司动态 | 免责声明 | 隐私条款 | 工作机会 | 展会港