Java Code Examples for android.graphics.Canvas#getClipBounds()
The following examples show how to use
android.graphics.Canvas#getClipBounds() .
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: PinnedHeaderDecoration.java From YPWGame with Apache License 2.0 | 6 votes |
@Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { //检测标签,并将标签强制固定在顶部 createPinnedHeader(parent); if (mPinnedHeaderView != null && pinnedTypeHeader.contains(viewType)) { int headerEndAt = mPinnedHeaderView.getTop() + mPinnedHeaderView.getHeight(); // 根据坐标查找view,headEnd + 1找到的就是mPinnedHeaderView底部下面的view View v = parent.findChildViewUnder(c.getWidth() / 2, headerEndAt + 1); if (isHeaderView(parent, v)) { // 如果是标签的话,缓存的标签就要同步跟此标签移动 mPinnedHeaderTop = v.getTop() - mPinnedHeaderView.getHeight(); } else { mPinnedHeaderTop = 0; } Log.d(TAG, "onDraw" + v.getTop()); Log.d(TAG, "mPinnedHeaderView.getHeight()" + mPinnedHeaderView.getHeight()); mClipBounds = c.getClipBounds(); mClipBounds.top = mPinnedHeaderTop + mPinnedHeaderView.getHeight(); c.clipRect(mClipBounds); } }
Example 2
Source File: ActivityEventView.java From arcusandroid with Apache License 2.0 | 6 votes |
protected void drawEvents(Canvas canvas) { int borderSize = getBorderSize(); int canvasEventLineHeight = canvasHeight - axisSizePX; final Rect clipBounds = canvas.getClipBounds(); for (ActivityLine line : activityIntervals) { float startDraw = getEventNextAndSkew(line, clipBounds); if (startDraw == BEFORE_LEFT_BOUND) { continue; } else if (startDraw == AFTER_RIGHT_BOUND) { return; } canvas.drawLine(startDraw, borderSize, startDraw, canvasEventLineHeight, eventPaintBrush); } }
Example 3
Source File: AwPicture.java From android-chromium with BSD 2-Clause "Simplified" License | 5 votes |
@Override public void draw(Canvas canvas) { canvas.getClipBounds(mClipBoundsTemporary); nativeDraw(mNativeAwPicture, canvas, mClipBoundsTemporary.left, mClipBoundsTemporary.top, mClipBoundsTemporary.right, mClipBoundsTemporary.bottom); }
Example 4
Source File: IndeterminateDrawable.java From material-components-android with Apache License 2.0 | 5 votes |
/** Draws the graphics based on the progress indicator's properties and the animation states. */ @Override public void draw(@NonNull Canvas canvas) { Rect clipBounds = new Rect(); if (getBounds().isEmpty() || !isVisible() || !canvas.getClipBounds(clipBounds)) { // Escape if bounds are empty, clip bounds are empty, or currently hidden. return; } canvas.save(); drawingDelegate.adjustCanvas(canvas, progressIndicator, getGrowFraction()); float displayedIndicatorWidth = progressIndicator.getIndicatorWidth() * getGrowFraction(); float displayedRoundedCornerRadius = progressIndicator.getIndicatorCornerRadius() * getGrowFraction(); // Draws the track first as the bottom layer. drawingDelegate.fillTrackWithColor( canvas, paint, combinedTrackColor, 0f, 1f, displayedIndicatorWidth, displayedRoundedCornerRadius); // Draws the indicators. for (int segmentIndex = 0; segmentIndex < animatorDelegate.segmentColors.length; segmentIndex++) { drawingDelegate.fillTrackWithColor( canvas, paint, animatorDelegate.segmentColors[segmentIndex], animatorDelegate.segmentPositions[2 * segmentIndex], animatorDelegate.segmentPositions[2 * segmentIndex + 1], displayedIndicatorWidth, displayedRoundedCornerRadius); } }
Example 5
Source File: HeaderDecorator.java From arcusandroid with Apache License 2.0 | 5 votes |
@Override public void onDraw(Canvas canvas, RecyclerView recyclerView, RecyclerView.State state) { initializeLocalVariables(recyclerView); createPinnedHeader(recyclerView); if (pinnedView != null) { final View v = recyclerView.findChildViewUnder(canvas.getWidth() / 2, pinnedHeaderEndAt + 1); pinnedViewTop = isHeaderView(recyclerView, v) ? (v.getTop() - pinnedHeaderHeight) : 0; canvasClippedBounds = canvas.getClipBounds(); canvasClippedBounds.top = pinnedViewTop + pinnedHeaderHeight; canvas.clipRect(canvasClippedBounds); } }
Example 6
Source File: WXSvgAbsComponent.java From Svg-for-Apache-Weex with Apache License 2.0 | 5 votes |
protected void setupDimensions(Canvas canvas) { Rect mCanvasClipBounds = canvas.getClipBounds(); mCanvasX = mCanvasClipBounds.left; mCanvasY = mCanvasClipBounds.top; mCanvasWidth = canvas.getWidth(); mCanvasHeight = canvas.getHeight(); }
Example 7
Source File: SlidingDotView.java From widgetlab with Apache License 2.0 | 5 votes |
@Override protected void onDraw(Canvas canvas) { paint.setColor(dotColor); canvas.getClipBounds(clipRect); clipRect.inset(-parentLeft + targetLeft, 0); canvas.save(); canvas.translate(-parentLeft + targetLeft, 0); canvas.drawCircle(centerX, centerY, radius, paint); canvas.restore(); }
Example 8
Source File: SlidingUpPanelLayout.java From mobile-manager-tool with MIT License | 5 votes |
@Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean result; final int save = canvas.save(Canvas.CLIP_SAVE_FLAG); if (isSlidingEnabled() && mSlideableView != child) { // Clip against the slider; no sense drawing what will immediately be covered, // Unless the panel is set to overlay content if (!mOverlayContent) { canvas.getClipBounds(mTmpRect); if (mIsSlidingUp) { mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop()); } else { mTmpRect.top = Math.max(mTmpRect.top, mSlideableView.getBottom()); } canvas.clipRect(mTmpRect); } } result = super.drawChild(canvas, child, drawingTime); canvas.restoreToCount(save); if (mCoveredFadeColor != 0 && mSlideOffset > 0) { final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24; final int imag = (int) (baseAlpha * mSlideOffset); final int color = imag << 24 | (mCoveredFadeColor & 0xffffff); mCoveredFadePaint.setColor(color); canvas.drawRect(mTmpRect, mCoveredFadePaint); } return result; }
Example 9
Source File: DidiLayout.java From DidiLayout with Apache License 2.0 | 5 votes |
@Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean result; final int save = canvas.save(); if (mSlideableView != null && mSlideableView != child) { // if main view // Clip against the slider; no sense drawing what will immediately be covered, // Unless the panel is set to overlay content canvas.getClipBounds(mTmpRect); if (!mOverlayContent) { if (mIsSlidingUp) { mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop()); } else { mTmpRect.top = Math.max(mTmpRect.top, mSlideableView.getBottom()); } } if (mClipPanel) { canvas.clipRect(mTmpRect); } result = super.drawChild(canvas, child, drawingTime); if (mCoveredFadeColor != 0 && mSlideOffset > 0) { final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24; final int imag = (int) (baseAlpha * mSlideOffset); final int color = imag << 24 | (mCoveredFadeColor & 0xffffff); mCoveredFadePaint.setColor(color); canvas.drawRect(mTmpRect, mCoveredFadePaint); } } else { result = super.drawChild(canvas, child, drawingTime); } canvas.restoreToCount(save); return result; }
Example 10
Source File: SlidingUpPanelLayout.java From Tweetin with Apache License 2.0 | 5 votes |
@Override protected boolean drawChild(Canvas canvas, View child, long drawingTime) { boolean result; final int save = canvas.save(Canvas.CLIP_SAVE_FLAG); if (mSlideableView != child) { // if main view // Clip against the slider; no sense drawing what will immediately be covered, // Unless the panel is set to overlay content canvas.getClipBounds(mTmpRect); if (!mOverlayContent) { if (mIsSlidingUp) { mTmpRect.bottom = Math.min(mTmpRect.bottom, mSlideableView.getTop()); } else { mTmpRect.top = Math.max(mTmpRect.top, mSlideableView.getBottom()); } } canvas.clipRect(mTmpRect); result = super.drawChild(canvas, child, drawingTime); if (mCoveredFadeColor != 0 && mSlideOffset > 0) { final int baseAlpha = (mCoveredFadeColor & 0xff000000) >>> 24; final int imag = (int) (baseAlpha * mSlideOffset); final int color = imag << 24 | (mCoveredFadeColor & 0xffffff); mCoveredFadePaint.setColor(color); canvas.drawRect(mTmpRect, mCoveredFadePaint); } } else { result = super.drawChild(canvas, child, drawingTime); } canvas.restoreToCount(save); return result; }
Example 11
Source File: RangeView.java From Android-nRF-Mesh-Library with BSD 3-Clause "New" or "Revised" License | 5 votes |
@NonNull private Rect getRegion(@NonNull final Canvas canvas, final int lowAddress, final int highAddress, final int lowerBound, final int upperBound) { final Rect mRect = canvas.getClipBounds(); final float unit = (mRect.width() / (float) (upperBound - lowerBound)); final int x = (int) ((lowAddress - lowerBound) * unit); final int right = (int) ((highAddress - lowerBound) * unit); return new Rect(x, 0, right, mRect.height()); }
Example 12
Source File: ColorPickerView.java From DoraemonKit with Apache License 2.0 | 5 votes |
private Bitmap createGridBitmap(int pixInterval, Canvas canvas) { int width = getWidth(); int height = getHeight(); canvas.getClipBounds(mGridRect); Bitmap gridBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas gridCanvas = new Canvas(gridBitmap); if (pixInterval >= 4) { int alpha = Math.min(pixInterval * 36, 255); mGridPaint.setAlpha(alpha); mGridShadowPaint.setAlpha(alpha); float value; float start; float end; gridCanvas.save(); for (int i = 0; i <= getWidth(); i += pixInterval) { value = (float) (i - 1); start = 0f; end = (float) height; gridCanvas.drawLine(value, start, value, end, this.mGridPaint); value = (float) i; gridCanvas.drawLine(value, start, value, end, this.mGridShadowPaint); } for (int i = 0; i <= getHeight(); i += pixInterval) { value = (float) (i - 1); start = 0f; end = (float) width; gridCanvas.drawLine(start, value, end, value, this.mGridPaint); value = (float) i; gridCanvas.drawLine(start, value, end, value, this.mGridShadowPaint); } gridCanvas.restore(); } return gridBitmap; }
Example 13
Source File: HtmlViewGroup.java From HtmlView2 with Apache License 2.0 | 4 votes |
private void drawBackgroud(Canvas canvas, LayoutParams layoutParams, int left, int top, int right, int bottom) { Paint bg = layoutParams.getBackgroundPaint(); if (bg != null) { canvas.drawRect(left + 1, top + 1, right, bottom, bg); } Bitmap img = layoutParams.getBackgroundBitmap(); if (img != null) { CssStyleDeclaration style = layoutParams.style(); Rect clipBounds = new Rect(); canvas.getClipBounds(clipBounds); canvas.save(); canvas.clipRect(left + 1, top + 1, right, bottom); CssEnum repeat = style.getEnum(CssProperty.BACKGROUND_REPEAT); int bgX = layoutParams.getBackgroundOffset(true, right - left, img.getWidth(), repeat); int bgY = layoutParams.getBackgroundOffset( false, bottom - top, img.getHeight(), repeat); canvas.clipRect(left + 1, top + 1, right, bottom); if (repeat == CssEnum.REPEAT_Y || repeat == CssEnum.REPEAT) { do { if (repeat == CssEnum.REPEAT) { int currentBgX = bgX; do { canvas.drawBitmap(img, left + 1 + currentBgX, top + 1 + bgY, null); currentBgX += img.getWidth(); } while (currentBgX < right - left); } else { canvas.drawBitmap(img, left + 1 + bgX, top + 1 + bgY, null); } bgY += img.getHeight(); } while (bgY < bottom - top); } else if (repeat == CssEnum.REPEAT_X) { do { canvas.drawBitmap(img, left + 1 + bgX, top + 1 + bgY, null); bgX += img.getWidth(); } while (bgX < right - left); } else { canvas.drawBitmap(img, left + 1 + bgX, top + 1 + bgY, null); } canvas.restore(); } }
Example 14
Source File: CropHighlightView.java From CVScanner with GNU General Public License v3.0 | 4 votes |
private void drawEdges(Canvas canvas) { final float[] p = mTrapezoid.getScreenPoints(getMatrix()); Path path = new Path(); path.moveTo((int) p[0], (int) p[1]); path.lineTo((int) p[2], (int) p[3]); path.lineTo((int) p[4], (int) p[5]); path.lineTo((int) p[6], (int) p[7]); path.close(); path.computeBounds(mPathBounds, false); mPathBounds.round(mPathBoundsRounded); canvas.getClipBounds(mCanvasCLipRect); mContext.getDrawingRect(mViewDrawingRect); mTopRect.set(0, 0, mViewDrawingRect.right, getDrawRect().top); mRightRect.set(0, getDrawRect().top, getDrawRect().left, getDrawRect().bottom); mLeftRect.set(getDrawRect().right, getDrawRect().top, mViewDrawingRect.right, getDrawRect().bottom); mBottomRect.set(0, getDrawRect().bottom, mViewDrawingRect.right, mViewDrawingRect.bottom); canvas.drawRect(mTopRect, mFocusPaint); canvas.drawRect(mRightRect, mFocusPaint); canvas.drawRect(mLeftRect, mFocusPaint); canvas.drawRect(mBottomRect, mFocusPaint); if (mCanvasCLipRect.contains(mPathBoundsRounded)) { canvas.save(); canvas.clipRect(getDrawRect()); path.setFillType(Path.FillType.INVERSE_EVEN_ODD); canvas.drawPath(path, mFocusPaint); canvas.restore(); } canvas.drawLine(p[0], p[1], p[2], p[3], mOutlinePaint); canvas.drawLine(p[2], p[3], p[4], p[5], mOutlinePaint); canvas.drawLine(p[4], p[5], p[6], p[7], mOutlinePaint); canvas.drawLine(p[0], p[1], p[6], p[7], mOutlinePaint); canvas.drawCircle(p[0], p[1], mCropCornerHandleRadius, mOutlinePaint); canvas.drawCircle(p[2], p[3], mCropCornerHandleRadius, mOutlinePaint); canvas.drawCircle(p[4], p[5], mCropCornerHandleRadius, mOutlinePaint); canvas.drawCircle(p[6], p[7], mCropCornerHandleRadius, mOutlinePaint); float x = (p[0] + p[2]) / 2; float y = (p[1] + p[3]) / 2; canvas.drawCircle(x, y, mCropEdgeHandleRadius, mOutlinePaint); x = (p[2] + p[4]) / 2; y = (p[3] + p[5]) / 2; canvas.drawCircle(x, y, mCropEdgeHandleRadius, mOutlinePaint); x = (p[4] + p[6]) / 2; y = (p[5] + p[7]) / 2; canvas.drawCircle(x, y, mCropEdgeHandleRadius, mOutlinePaint); x = (p[0] + p[6]) / 2; y = (p[1] + p[7]) / 2; canvas.drawCircle(x, y, mCropEdgeHandleRadius, mOutlinePaint); }
Example 15
Source File: DegreeSeekBar.java From imsdk-android with MIT License | 4 votes |
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.getClipBounds(mCanvasClipBounds); int zeroIndex = mPointCount / 2 + (0 - mCurrentDegrees) / 2; mPointPaint.setColor(mPointColor); for (int i = 0; i < mPointCount; i++) { if (i > zeroIndex - Math.abs(mMinReachableDegrees) / 2 && i < zeroIndex + Math.abs(mMaxReachableDegrees) / 2 && mScrollStarted) { mPointPaint.setAlpha(255); } else { mPointPaint.setAlpha(100); } if (i > mPointCount / 2 - 8 && i < mPointCount / 2 + 8 && i > zeroIndex - Math.abs(mMinReachableDegrees) / 2 && i < zeroIndex + Math.abs(mMaxReachableDegrees) / 2) { if (mScrollStarted) { mPointPaint.setAlpha(Math.abs(mPointCount / 2 - i) * 255 / 8); } else { mPointPaint.setAlpha(Math.abs(mPointCount / 2 - i) * 100 / 8); } } canvas.drawPoint(mCanvasClipBounds.centerX() + (i - mPointCount / 2) * mPointMargin, mCanvasClipBounds.centerY(), mPointPaint); if (mCurrentDegrees != 0 && i == zeroIndex) { if (mScrollStarted) { mTextPaint.setAlpha(255); } else { mTextPaint.setAlpha(192); } mPointPaint.setStrokeWidth(4); canvas.drawPoint((mCanvasClipBounds.centerX() + (i - mPointCount / 2) * mPointMargin), mCanvasClipBounds.centerY(), mPointPaint); mPointPaint.setStrokeWidth(2); mTextPaint.setAlpha(100); } } for (int i = -180; i <= 180; i += 15) { if (i >= mMinReachableDegrees && i <= mMaxReachableDegrees) { drawDegreeText(i, canvas, true); } else { drawDegreeText(i, canvas, false); } } mTextPaint.setTextSize(28f); mTextPaint.setAlpha(255); mTextPaint.setColor(mCenterTextColor); if (mCurrentDegrees >= 10) { canvas.drawText(mCurrentDegrees + suffix, getWidth() / 2 - mTextWidths[0], mBaseLine, mTextPaint); } else if (mCurrentDegrees <= -10) { canvas.drawText(mCurrentDegrees + suffix, getWidth() / 2 - mTextWidths[0] / 2 * 3, mBaseLine, mTextPaint); } else if (mCurrentDegrees < 0) { canvas.drawText(mCurrentDegrees + suffix, getWidth() / 2 - mTextWidths[0], mBaseLine, mTextPaint); } else { canvas.drawText(mCurrentDegrees + suffix, getWidth() / 2 - mTextWidths[0] / 2, mBaseLine, mTextPaint); } mTextPaint.setAlpha(100); mTextPaint.setTextSize(24f); mTextPaint.setColor(mTextColor); //画中心三角 mCirclePaint.setColor(mCenterTextColor); canvas.drawPath(mIndicatorPath, mCirclePaint); mCirclePaint.setColor(mCenterTextColor); }
Example 16
Source File: FreeScrollingTextField.java From CodeEditor with Apache License 2.0 | 4 votes |
/** * The last row of text to paint, which may be partially visible. * Deduced from the clipping rectangle given to onDraw() */ private int getEndPaintRow(Canvas canvas) { //clip top and left are inclusive; bottom and right are exclusive Rect bounds = canvas.getClipBounds(); return (bounds.bottom - 1) / rowHeight(); }
Example 17
Source File: GitHubContributionsView.java From mvvm-template with GNU General Public License v3.0 | 4 votes |
private void drawPlaceholder(Canvas canvas) { if (!isInEditMode()) return; canvas.getClipBounds(rect); int width = rect.width(); int verticalBlockNumber = 7; int horizontalBlockNumber = getHorizontalBlockNumber(lastWeeks * 7, verticalBlockNumber); float marginBlock = (1.0F - 0.1F); float blockWidth = width / (float) horizontalBlockNumber * marginBlock; float spaceWidth = width / (float) horizontalBlockNumber - blockWidth; float monthTextHeight = (displayMonth) ? blockWidth * 1.5F : 0; float topMargin = (displayMonth) ? 7f : 0; monthTextPaint.setTextSize(monthTextHeight); int height = (int) ((blockWidth + spaceWidth) * 7 + topMargin + monthTextHeight); // Background blockPaint.setColor(backgroundBaseColor); canvas.drawRect(0, (topMargin + monthTextHeight), width, height + monthTextHeight, blockPaint); float x = 0; float y = 0 * (blockWidth + spaceWidth) + (topMargin + monthTextHeight); for (int i = 1; i < (lastWeeks * 7) + 1; i++) { blockPaint.setColor(ColorsUtils.calculateLevelColor(baseColor, baseEmptyColor, 0)); canvas.drawRect(x, y, x + blockWidth, y + blockWidth, blockPaint); if (i % 7 == 0) { // another column x += blockWidth + spaceWidth; y = topMargin + monthTextHeight; } else { y += blockWidth + spaceWidth; } } // Resize component ViewGroup.LayoutParams ll = getLayoutParams(); ll.height = height; setLayoutParams(ll); }
Example 18
Source File: PXShapeView.java From pixate-freestyle-android with Apache License 2.0 | 4 votes |
@Override protected void onDraw(Canvas canvas) { canvas.getClipBounds(bounds); Picture picture = renderToImage(bounds); canvas.drawPicture(picture); }
Example 19
Source File: ActivityEventView.java From arcusandroid with Apache License 2.0 | 4 votes |
protected void drawHours(Canvas canvas) { hoursCalendar.setTimeInMillis(startTime); hoursCalendar.set(Calendar.MINUTE, getMinutesInIntervalOf30(hoursCalendar.get(Calendar.MINUTE), false)); hoursCalendar.set(Calendar.SECOND, 0); hoursCalendar.set(Calendar.MILLISECOND, 0); int minute = hoursCalendar.get(Calendar.MINUTE); int hourOfDay = hoursCalendar.get(Calendar.HOUR_OF_DAY); long next = hoursCalendar.getTimeInMillis(); long first = hoursCalendar.getTimeInMillis(); final Rect clipBounds = canvas.getClipBounds(); float starSkew = textPaintBrush.measureText(STAR_TEXT); // Ends up drawing an extra to the right but the last 'vanishing' axis label goes away. for (long i = next; i < (endTime + (THIRTY_MIN_IN_MILLIS - 1000)); i += THIRTY_MIN_IN_MILLIS) { float x = getNextX(i); int h24 = hourOfDay % 24; int h12 = hourOfDay % 12; if (h12 == 0) h12 = 12; String normal = Integer.toString(h12); String supScript; if (minute == 0) { supScript = h24 >= 12 ? PM : AM; } else { supScript = Integer.toString(minute); } textPaintBrush.getTextBounds(normal, 0, normal.length(), normalTextBounds); int halfLineHeight = (int) (normalTextBounds.height() * (1 - superScriptScaleFactor)); int top = canvasHeight - (axisSizePX / 3) + (normalTextBounds.height() / 2); float skew = textPaintBrush.measureText(normal); if (i == first) { textPaintBrush.setAlpha(sixtyPercent); canvas.drawText(normal, x, canvasHeight, textPaintBrush); textPaintBrush.setTextSize(textSPHeight * superScriptScaleFactor); textPaintBrush.setAlpha(fortyPercent); canvas.drawText(supScript, x + skew, top, textPaintBrush); textPaintBrush.setTextSize(textSPHeight); } else if (i == (TimeUnit.HOURS.toMillis(2) + first)) { // Shift the last entry a touch more inside so it doesn't get cut off. // Measure the width of the hour + minute (or am pm text) and inset from right that much. float offsetLeft = clipBounds.right - textPaintBrush.measureText(normal); textPaintBrush.setTextSize(textSPHeight * superScriptScaleFactor); offsetLeft -= textPaintBrush.measureText(supScript); textPaintBrush.setTextSize(textSPHeight); textPaintBrush.setAlpha(sixtyPercent); canvas.drawText(normal, offsetLeft, canvasHeight, textPaintBrush); textPaintBrush.setTextSize(textSPHeight * superScriptScaleFactor); textPaintBrush.setAlpha(fortyPercent); canvas.drawText(supScript, offsetLeft + skew, top, textPaintBrush); textPaintBrush.setTextSize(textSPHeight); } else { canvas.drawText(STAR_TEXT, x + starSkew, canvasHeight, textPaintBrush); } minute += 30; if (minute == 60) { hourOfDay++; minute = 0; } } }
Example 20
Source File: FreeScrollingTextField.java From lua-for-android with BSD 3-Clause "New" or "Revised" License | 4 votes |
/** * The first row of text to paint, which may be partially visible. * Deduced from the clipping rectangle given to onDraw() */ private int getBeginPaintRow(Canvas canvas) { Rect bounds = canvas.getClipBounds(); return bounds.top / rowHeight(); }