List of usage examples for android.view.animation AnimationSet AnimationSet
public AnimationSet(boolean shareInterpolator)
From source file:com.androzic.vnspeech.MapFragment.java
private void onUpdateNavigationStatus() { if (!application.isNavigating()) return;/* w w w . j a va 2 s.co m*/ if (!map.isFollowing()) map.refreshMap(); long now = System.currentTimeMillis(); double distance = application.navigationService.navDistance; double bearing = application.navigationService.navBearing; long turn = application.navigationService.navTurn; double vmg = application.navigationService.navVMG; int ete = application.navigationService.navETE; String[] dist = StringFormatter.distanceC(distance, StringFormatter.precisionFormat); String eteString = (ete == Integer.MAX_VALUE) ? getString(R.string.never) : (String) DateUtils.getRelativeTimeSpanString(now + (ete + 1) * 60000, now, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE); String extra = StringFormatter.speedH(vmg) + " | " + eteString; String trnsym = ""; if (turn > 0) { trnsym = "R"; } else if (turn < 0) { trnsym = "L"; turn = -turn; } bearing = application.fixDeclination(bearing); distanceValue.setText(dist[0]); distanceUnit.setText(dist[1]); bearingValue.setText(StringFormatter.angleC(bearing)); turnValue.setText(StringFormatter.angleC(turn) + trnsym); waypointExtra.setText(extra); if (application.navigationService.isNavigatingViaRoute()) { View rootView = getView(); boolean hasNext = application.navigationService.hasNextRouteWaypoint(); if (distance < application.navigationService.navProximity * 3 && !animationSet) { AnimationSet animation = new AnimationSet(true); animation.addAnimation(new AlphaAnimation(1.0f, 0.3f)); animation.addAnimation(new AlphaAnimation(0.3f, 1.0f)); animation.setDuration(500); animation.setRepeatCount(10); rootView.findViewById(R.id.waypointinfo).startAnimation(animation); if (!hasNext) { rootView.findViewById(R.id.routeinfo).startAnimation(animation); } animationSet = true; } else if (animationSet) { rootView.findViewById(R.id.waypointinfo).setAnimation(null); if (!hasNext) { rootView.findViewById(R.id.routeinfo).setAnimation(null); } animationSet = false; } if (application.navigationService.navXTK == Double.NEGATIVE_INFINITY) { xtkValue.setText("--"); xtkUnit.setText("--"); } else { String xtksym = application.navigationService.navXTK == 0 ? "" : application.navigationService.navXTK > 0 ? "R" : "L"; String[] xtks = StringFormatter.distanceC(Math.abs(application.navigationService.navXTK)); xtkValue.setText(xtks[0] + xtksym); xtkUnit.setText(xtks[1]); } double navDistance = application.navigationService.navRouteDistanceLeft(); int eta = application.navigationService.navRouteETE(navDistance); if (eta < Integer.MAX_VALUE) eta += application.navigationService.navETE; String etaString = (eta == Integer.MAX_VALUE) ? getString(R.string.never) : (String) DateUtils.getRelativeTimeSpanString(now + (eta + 1) * 60000, now, DateUtils.MINUTE_IN_MILLIS, DateUtils.FORMAT_ABBREV_RELATIVE); extra = StringFormatter.distanceH(navDistance + distance, 1000) + " | " + etaString; routeExtra.setText(extra); } }
From source file:android.support.v7.app.MediaRouteControllerDialog.java
private void animateGroupListItemsInternal(Map<MediaRouter.RouteInfo, Rect> previousRouteBoundMap, Map<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap) { if (mGroupMemberRoutesAdded == null || mGroupMemberRoutesRemoved == null) { return;//from ww w.ja v a2 s . c o m } int groupSizeDelta = mGroupMemberRoutesAdded.size() - mGroupMemberRoutesRemoved.size(); boolean listenerRegistered = false; Animation.AnimationListener listener = new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { mVolumeGroupList.startAnimationAll(); mVolumeGroupList.postDelayed(mGroupListFadeInAnimation, mGroupListAnimationDurationMs); } @Override public void onAnimationEnd(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } }; // Animate visible items from previous positions to current positions except routes added // just before. Added routes will remain hidden until translate animation finishes. int first = mVolumeGroupList.getFirstVisiblePosition(); for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { View view = mVolumeGroupList.getChildAt(i); int position = first + i; MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); Rect previousBounds = previousRouteBoundMap.get(route); int currentTop = view.getTop(); int previousTop = previousBounds != null ? previousBounds.top : (currentTop + mVolumeGroupListItemHeight * groupSizeDelta); AnimationSet animSet = new AnimationSet(true); if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) { previousTop = currentTop; Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f); alphaAnim.setDuration(mGroupListFadeInDurationMs); animSet.addAnimation(alphaAnim); } Animation translationAnim = new TranslateAnimation(0, 0, previousTop - currentTop, 0); translationAnim.setDuration(mGroupListAnimationDurationMs); animSet.addAnimation(translationAnim); animSet.setFillAfter(true); animSet.setFillEnabled(true); animSet.setInterpolator(mInterpolator); if (!listenerRegistered) { listenerRegistered = true; animSet.setAnimationListener(listener); } view.clearAnimation(); view.startAnimation(animSet); previousRouteBoundMap.remove(route); previousRouteBitmapMap.remove(route); } // If a member route doesn't exist any longer, it can be either removed or moved out of the // ListView layout boundary. In this case, use the previously captured bitmaps for // animation. for (Map.Entry<MediaRouter.RouteInfo, BitmapDrawable> item : previousRouteBitmapMap.entrySet()) { final MediaRouter.RouteInfo route = item.getKey(); final BitmapDrawable bitmap = item.getValue(); final Rect bounds = previousRouteBoundMap.get(route); OverlayObject object = null; if (mGroupMemberRoutesRemoved.contains(route)) { object = new OverlayObject(bitmap, bounds).setAlphaAnimation(1.0f, 0.0f) .setDuration(mGroupListFadeOutDurationMs).setInterpolator(mInterpolator); } else { int deltaY = groupSizeDelta * mVolumeGroupListItemHeight; object = new OverlayObject(bitmap, bounds).setTranslateYAnimation(deltaY) .setDuration(mGroupListAnimationDurationMs).setInterpolator(mInterpolator) .setAnimationEndListener(new OverlayObject.OnAnimationEndListener() { @Override public void onAnimationEnd() { mGroupMemberRoutesAnimatingWithBitmap.remove(route); mVolumeGroupAdapter.notifyDataSetChanged(); } }); mGroupMemberRoutesAnimatingWithBitmap.add(route); } mVolumeGroupList.addOverlayObject(object); } }
From source file:androidx.mediarouter.app.MediaRouteControllerDialog.java
void animateGroupListItemsInternal(Map<MediaRouter.RouteInfo, Rect> previousRouteBoundMap, Map<MediaRouter.RouteInfo, BitmapDrawable> previousRouteBitmapMap) { if (mGroupMemberRoutesAdded == null || mGroupMemberRoutesRemoved == null) { return;// ww w .ja v a 2 s .c om } int groupSizeDelta = mGroupMemberRoutesAdded.size() - mGroupMemberRoutesRemoved.size(); boolean listenerRegistered = false; Animation.AnimationListener listener = new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { mVolumeGroupList.startAnimationAll(); mVolumeGroupList.postDelayed(mGroupListFadeInAnimation, mGroupListAnimationDurationMs); } @Override public void onAnimationEnd(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } }; // Animate visible items from previous positions to current positions except routes added // just before. Added routes will remain hidden until translate animation finishes. int first = mVolumeGroupList.getFirstVisiblePosition(); for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { View view = mVolumeGroupList.getChildAt(i); int position = first + i; MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); Rect previousBounds = previousRouteBoundMap.get(route); int currentTop = view.getTop(); int previousTop = previousBounds != null ? previousBounds.top : (currentTop + mVolumeGroupListItemHeight * groupSizeDelta); AnimationSet animSet = new AnimationSet(true); if (mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) { previousTop = currentTop; Animation alphaAnim = new AlphaAnimation(0.0f, 0.0f); alphaAnim.setDuration(mGroupListFadeInDurationMs); animSet.addAnimation(alphaAnim); } Animation translationAnim = new TranslateAnimation(0, 0, previousTop - currentTop, 0); translationAnim.setDuration(mGroupListAnimationDurationMs); animSet.addAnimation(translationAnim); animSet.setFillAfter(true); animSet.setFillEnabled(true); animSet.setInterpolator(mInterpolator); if (!listenerRegistered) { listenerRegistered = true; animSet.setAnimationListener(listener); } view.clearAnimation(); view.startAnimation(animSet); previousRouteBoundMap.remove(route); previousRouteBitmapMap.remove(route); } // If a member route doesn't exist any longer, it can be either removed or moved out of the // ListView layout boundary. In this case, use the previously captured bitmaps for // animation. for (Map.Entry<MediaRouter.RouteInfo, BitmapDrawable> item : previousRouteBitmapMap.entrySet()) { final MediaRouter.RouteInfo route = item.getKey(); final BitmapDrawable bitmap = item.getValue(); final Rect bounds = previousRouteBoundMap.get(route); OverlayListView.OverlayObject object = null; if (mGroupMemberRoutesRemoved.contains(route)) { object = new OverlayListView.OverlayObject(bitmap, bounds).setAlphaAnimation(1.0f, 0.0f) .setDuration(mGroupListFadeOutDurationMs).setInterpolator(mInterpolator); } else { int deltaY = groupSizeDelta * mVolumeGroupListItemHeight; object = new OverlayListView.OverlayObject(bitmap, bounds).setTranslateYAnimation(deltaY) .setDuration(mGroupListAnimationDurationMs).setInterpolator(mInterpolator) .setAnimationEndListener(new OverlayListView.OverlayObject.OnAnimationEndListener() { @Override public void onAnimationEnd() { mGroupMemberRoutesAnimatingWithBitmap.remove(route); mVolumeGroupAdapter.notifyDataSetChanged(); } }); mGroupMemberRoutesAnimatingWithBitmap.add(route); } mVolumeGroupList.addOverlayObject(object); } }
From source file:org.androfarsh.widget.DragGridLayout.java
@Override public boolean onTouchEvent(MotionEvent ev) { final float x = ev.getX(); final float y = ev.getY(); if (mGestureDetector.onTouchEvent(ev)) { mPrevX = x;//from w ww . j av a 2 s.co m mPrevY = y; return true; } switch (ev.getAction() & MotionEvent.ACTION_MASK) { case MotionEvent.ACTION_DOWN: if (mEditMode) { final View child = findIntersectChild(x, y); if (child != null) { if ((mDragNode != null) && (mDragNode.view != child)) { return true; } mPrevX = x; mPrevY = y; stopAnimation(mDragNode != null ? mDragNode.view : null); mDragNode = new Node(child, requestPreferredRect(mTmpRect, child)); Node.scale(mDragNode.currentRect, mScaleFactor); requestFreeCellRegion(child); requestHoveredCells(mDragNode); if (mDragListener != null) { mDragListener.onDrag(mDragNode.view, this); } final Animation animation = new ScaleAnimation((1f - mScaleFactor) + 1f, 1f, (1f - mScaleFactor) + 1f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f); animation.setDuration(DURATION); animation.setAnimationListener(new AbstractAnimationListener() { @Override public void onAnimationEnd(Animation animation) { mTmpRect.set(mDragNode.startRect); child.setAnimation(null); child.requestLayout(); invalidate(mTmpRect); } }); requestLayout(); child.startAnimation(animation); return true; } final Cell cell = findCellUnder(x, y); if (cell != null) { mPressedCell = cell; invalidate(); return true; } mEditModeSwitchOff = true; return true; } break; case MotionEvent.ACTION_MOVE: if (mPressedCell != null) { final Cell cell = findCellUnder(x, y); if (mPressedCell != cell) { mPressedCell = null; invalidate(); return true; } } else if (mDragNode != null) { mDragNode.currentRect.offset((int) (x - mPrevX), (int) (y - mPrevY)); if (mDebugMode) { Log.w(VIEW_LOG_TAG, "ACTION_MOVE: x=" + x + " y=" + y + " prevX=" + mPrevX + " prevY=" + mPrevY + " dX=" + (x - mPrevX) + " dY=" + (y - mPrevY)); } requestHoveredCells(mDragNode); boolean dragged = (Math.abs(x - mPrevX) < DELTA) && (Math.abs(y - mPrevY) < DELTA); if (dragged) { requestReorderRevert(); } if (!mHoveredCells.isEmpty() && dragged) { if (!mLoongHoveredRequested) { mLoongHoveredRequested = true; mHandler.sendEmptyMessageDelayed(LONGHOVER_MESSAGE, LONGPRESS_TIMEOUT + TAP_TIMEOUT); } } else if (mLoongHoveredRequested) { mLoongHoveredRequested = false; mHandler.removeMessages(LONGHOVER_MESSAGE); } mPrevX = x; mPrevY = y; requestLayout(); invalidate(); return true; } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: mLoongHoveredRequested = false; mHandler.removeMessages(LONGHOVER_MESSAGE); if (mPressedCell != null) { if (mCellClickListener != null) { mCellClickListener.onClick(new Point(mPressedCell.rect.left, mPressedCell.rect.top), this); } mPressedCell = null; invalidate(); return true; } else if (mDragNode != null) { mDragNode.currentRect.offset((int) (x - mPrevX), (int) (y - mPrevY)); requestHoveredCells(mDragNode); requestReorderRevert(); requestDrop(mDragNode); mPrevX = x; mPrevY = y; mTmpRect.set(mDragNode.currentRect); mTmpRect.union(mDragNode.startRect); final View child = mDragNode.view; final LayoutParams lp = (LayoutParams) child.getLayoutParams(); lp.mX = mDragNode.startRect.left; lp.mY = mDragNode.startRect.top; mNodes.clear(); mDragNode.startRect.offset(lp.leftMargin, lp.topMargin); final AnimationSet animation = new AnimationSet(true); animation.addAnimation(new TranslateAnimation(mDragNode.currentRect.left - mDragNode.startRect.left, 0, mDragNode.currentRect.top - mDragNode.startRect.top, 0)); animation.addAnimation(new ScaleAnimation(mScaleFactor, 1f, mScaleFactor, 1f)); animation.setDuration(DURATION); animation.setAnimationListener(new AbstractAnimationListener() { @Override public void onAnimationEnd(final Animation a) { mDragNode.dispose(); mDragNode = null; child.setAnimation(null); requestLayout(); invalidate(); } }); if (mDragListener != null) { mDragListener.onDrop(mDragNode.view, this); } mDragNode.currentRect.set(mDragNode.startRect); child.requestLayout(); child.startAnimation(animation); invalidate(mTmpRect); return true; } else if (mEditModeSwitchOff) { setEditMode(false); mEditModeSwitchOff = false; return true; } break; } return super.onTouchEvent(ev); }
From source file:android.support.v7.app.MediaRouteControllerDialog.java
void clearGroupListAnimation(boolean exceptAddedRoutes) { int first = mVolumeGroupList.getFirstVisiblePosition(); for (int i = 0; i < mVolumeGroupList.getChildCount(); ++i) { View view = mVolumeGroupList.getChildAt(i); int position = first + i; MediaRouter.RouteInfo route = mVolumeGroupAdapter.getItem(position); if (exceptAddedRoutes && mGroupMemberRoutesAdded != null && mGroupMemberRoutesAdded.contains(route)) { continue; }// ww w . ja va 2s. c o m LinearLayout container = (LinearLayout) view.findViewById(R.id.volume_item_container); container.setVisibility(View.VISIBLE); AnimationSet animSet = new AnimationSet(true); Animation alphaAnim = new AlphaAnimation(1.0f, 1.0f); alphaAnim.setDuration(0); animSet.addAnimation(alphaAnim); Animation translationAnim = new TranslateAnimation(0, 0, 0, 0); translationAnim.setDuration(0); animSet.setFillAfter(true); animSet.setFillEnabled(true); view.clearAnimation(); view.startAnimation(animSet); } mVolumeGroupList.stopAnimationAll(); if (!exceptAddedRoutes) { finishAnimation(false); } }
From source file:com.ebaonet.lawyer.ui.weight.DraggableGridViewPager.java
private void animateDragged() { if (mLastDragged >= 0) { final View v = getChildAt(mLastDragged); final Rect r = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); r.inset(-r.width() / 20, -r.height() / 20); v.measure(MeasureSpec.makeMeasureSpec(r.width(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(r.height(), MeasureSpec.EXACTLY)); v.layout(r.left, r.top, r.right, r.bottom); AnimationSet animSet = new AnimationSet(true); ScaleAnimation scale = new ScaleAnimation(0.9091f, 1, 0.9091f, 1, v.getWidth() / 2, v.getHeight() / 2); scale.setDuration(ANIMATION_DURATION); AlphaAnimation alpha = new AlphaAnimation(1, .8f); alpha.setDuration(ANIMATION_DURATION); animSet.addAnimation(scale);/*from w ww. j av a2 s. co m*/ animSet.addAnimation(alpha); animSet.setFillEnabled(true); animSet.setFillAfter(true); v.clearAnimation(); v.startAnimation(animSet); } }
From source file:com.android.hcframe.DraggableGridViewPager.java
private void animateDragged() { if (mLastDragged >= 0) { final View v = getChildAt(mLastDragged); final Rect r = new Rect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom()); r.inset(-r.width() / 20, -r.height() / 20); v.measure(MeasureSpec.makeMeasureSpec(r.width(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(r.height(), MeasureSpec.EXACTLY)); v.layout(r.left, r.top, r.right, r.bottom); AnimationSet animSet = new AnimationSet(true); ScaleAnimation scale = new ScaleAnimation(0.9091f, 1, 0.9091f, 1, v.getWidth() / 2, v.getHeight() / 2); scale.setDuration(ANIMATION_DURATION); AlphaAnimation alpha = new AlphaAnimation(1, .5f); alpha.setDuration(ANIMATION_DURATION); animSet.addAnimation(scale);/*ww w . jav a2s.c o m*/ animSet.addAnimation(alpha); animSet.setFillEnabled(true); animSet.setFillAfter(true); v.clearAnimation(); v.startAnimation(animSet); } }
From source file:org.androfarsh.widget.DragGridLayout.java
private void requestReorder() { if (mDragNode == null) { return;//from ww w.j av a2 s . c om } Set<Node> nodes = findNodesUnder(mDragNode, mHoveredCells); for (final Node childNode : nodes) { requestFreeCellRegion(childNode.view, mDragNode.view); mFreeCellsRegion.op(mDragNode.currentRect, Op.DIFFERENCE); final LayoutParams lp = (LayoutParams) childNode.view.getLayoutParams(); final LayoutParams newLp = validateLayoutParams(generateLayoutParams(lp), mFreeCellsRegion); if ((lp.mX == newLp.mX) && (lp.mY == newLp.mY)) { continue; } childNode.view.setLayoutParams(newLp); requestPreferredRect(childNode.currentRect, childNode.view); final AnimationSet animation = new AnimationSet(true); animation.addAnimation(new TranslateAnimation(childNode.startRect.left - childNode.currentRect.left, 0, childNode.startRect.top - childNode.currentRect.top, 0)); animation.setDuration(DURATION / 2); animation.setAnimationListener(new AbstractAnimationListener() { @Override public void onAnimationEnd(final Animation a) { childNode.view.setAnimation(null); requestLayout(); invalidate(); } }); childNode.view.setAnimation(animation); mNodes.add(childNode); } if (!nodes.isEmpty()) { requestFreeCellRegion(); requestLayout(); invalidate(); } }
From source file:com.androzic.MapActivity.java
protected void updateNavigationInfo() { if (navigationService == null || !navigationService.isNavigating()) return;//w ww .j a v a2 s. co m double distance = navigationService.navDistance; double bearing = navigationService.navBearing; long turn = navigationService.navTurn; double vmg = navigationService.navVMG * speedFactor; int ete = navigationService.navETE; String[] dist = StringFormatter.distanceC(distance, precisionFormat); String extra = String.format(precisionFormat, vmg) + " " + speedAbbr + " | " + StringFormatter.timeH(ete); String trnsym = ""; if (turn > 0) { trnsym = "R"; } else if (turn < 0) { trnsym = "L"; turn = -turn; } bearing = application.fixDeclination(bearing); distanceValue.setText(dist[0]); distanceUnit.setText(dist[1]); bearingValue.setText(String.valueOf(Math.round(bearing))); turnValue.setText(String.valueOf(Math.round(turn)) + trnsym); waypointExtra.setText(extra); if (navigationService.isNavigatingViaRoute()) { boolean hasNext = navigationService.hasNextRouteWaypoint(); if (distance < navigationService.navProximity * 3 && !animationSet) { AnimationSet animation = new AnimationSet(true); animation.addAnimation(new AlphaAnimation(1.0f, 0.3f)); animation.addAnimation(new AlphaAnimation(0.3f, 1.0f)); animation.setDuration(500); animation.setRepeatCount(10); findViewById(R.id.waypointinfo).startAnimation(animation); if (!hasNext) { findViewById(R.id.routeinfo).startAnimation(animation); } animationSet = true; } else if (animationSet) { findViewById(R.id.waypointinfo).setAnimation(null); if (!hasNext) { findViewById(R.id.routeinfo).setAnimation(null); } animationSet = false; } if (navigationService.navXTK == Double.NEGATIVE_INFINITY) { xtkValue.setText("--"); xtkUnit.setText("--"); } else { String xtksym = navigationService.navXTK == 0 ? "" : navigationService.navXTK > 0 ? "R" : "L"; String[] xtks = StringFormatter.distanceC(Math.abs(navigationService.navXTK)); xtkValue.setText(xtks[0] + xtksym); xtkUnit.setText(xtks[1]); } double navDistance = navigationService.navRouteDistanceLeft(); int eta = navigationService.navRouteETE(navDistance); if (eta < Integer.MAX_VALUE) eta += navigationService.navETE; extra = StringFormatter.distanceH(navDistance + distance, 1000) + " | " + StringFormatter.timeH(eta); routeExtra.setText(extra); } }
From source file:org.androfarsh.widget.DragGridLayout.java
private void requestReorderRevert() { boolean needInvalidate = false; for (final Iterator<Node> it = mNodes.iterator(); it.hasNext();) { final Node node = it.next(); if (mDragNode != null) { requestFreeCellRegion(mDragNode.view); } else {/* www. j a va 2 s. c o m*/ requestFreeCellRegion(); } for (Cell cell : mHoveredCells) { mFreeCellsRegion.op(cell.rect, Op.DIFFERENCE); } mTmpRegion.set(mFreeCellsRegion); mTmpRegion.op(node.startRect, Op.INTERSECT); mTmpRegion.getBounds(mTmpRect); if ((mTmpRect.width() == node.startRect.width()) && (mTmpRect.height() == node.startRect.height())) { it.remove(); LayoutParams lp = (LayoutParams) node.view.getLayoutParams(); lp.mX = node.startRect.left - lp.leftMargin; lp.mY = node.startRect.top - lp.topMargin; final AnimationSet animation = new AnimationSet(true); animation.addAnimation(new TranslateAnimation(-node.startRect.left + node.currentRect.left, 0, -node.startRect.top + node.currentRect.top, 0)); animation.setDuration(DURATION / 2); animation.setAnimationListener(new AbstractAnimationListener() { @Override public void onAnimationEnd(final Animation a) { node.view.setAnimation(null); requestLayout(); invalidate(); } }); node.view.setAnimation(animation); needInvalidate = true; } } if (needInvalidate) { invalidate(); } }