/** Resets the dirty region when the motion event occurs. */ private void resetDirtyRect(float eventX, float eventY) { // The lastTouchX and lastTouchY were set when the ACTION_DOWN // motion event occurred. dirtyRect.left = Math.min(lastTouchX, eventX); dirtyRect.right = Math.max(lastTouchX, eventX); dirtyRect.top = Math.min(lastTouchY, eventY); dirtyRect.bottom = Math.max(lastTouchY, eventY); }
private void adjustFirstCellsAndScroll() { int values[]; // values = adjustFirstCellsAndScroll(scrollX, firstColumn, widths); // scrollX = values[0]; // firstColumn = values[1]; // 切换应用导致重新布局时横向滚到最前端 scrollX = 0; firstColumn = 0; sumX = 0; values = adjustFirstCellsAndScroll(scrollY, firstRow, heights); scrollY = values[0]; firstRow = values[1]; if (lineView != null) { int maxY = lineView.getBottom() - height + lineView.getTop(); // data变化后maxY发生变化,而之前存的sumY也失效了,滑动时以现在的maxY为准 if (maxY < 0) { maxY = 0; } sumY = Math.min(maxY, sumY); lineView.scrollBy(0, sumY); } }
@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); // Logger.d(TAG, String.format("onSizeChanged, w: %s, h: %s, oldw: %s, oldh: %s", w, h, // oldw, oldh)); if (isFirst && w > 0 && h > 0) { isFirst = false; originWidth = w; originHeight = h; originRadius = Math.min(originWidth, originHeight) / 2; curRadius = originRadius; touchedPointRadius = originRadius; maxMoveLength = ABAppUtil.getDeviceHeight(context) / 6; refreshStartPoint(); ViewGroup.LayoutParams lp = this.getLayoutParams(); if (RelativeLayout.LayoutParams.class.isAssignableFrom(lp.getClass())) { originLp = (RelativeLayout.LayoutParams) lp; } newLp = new RelativeLayout.LayoutParams(lp.width, lp.height); } }
private void computePath(Rect bounds) { final float currentScale = mCurrentScale; final Path path = mPath; final RectF rect = mRect; final Matrix matrix = mMatrix; path.reset(); int totalSize = Math.min(bounds.width(), bounds.height()); float initial = mClosedStateSize; float destination = totalSize; float currentSize = initial + (destination - initial) * currentScale; float halfSize = currentSize / 2f; float inverseScale = 1f - currentScale; float cornerSize = halfSize * inverseScale; float[] corners = new float[] { halfSize, halfSize, halfSize, halfSize, halfSize, halfSize, cornerSize, cornerSize }; rect.set(bounds.left, bounds.top, bounds.left + currentSize, bounds.top + currentSize); path.addRoundRect(rect, corners, Path.Direction.CCW); matrix.reset(); matrix.postRotate(-45, bounds.left + halfSize, bounds.top + halfSize); matrix.postTranslate((bounds.width() - currentSize) / 2, 0); float hDiff = (bounds.bottom - currentSize - mExternalOffset) * inverseScale; matrix.postTranslate(0, hDiff); path.transform(matrix); }
// Grows the cropping rectange by (dx, dy) in image space. void moveBy(float dx, float dy) { Rect invalRect = new Rect(mDrawRect); mCropRect.offset(dx, dy); // Put the cropping rectangle inside image rectangle. mCropRect.offset( Math.max(0, mImageRect.left - mCropRect.left), Math.max(0, mImageRect.top - mCropRect.top)); mCropRect.offset( Math.min(0, mImageRect.right - mCropRect.right), Math.min(0, mImageRect.bottom - mCropRect.bottom)); mDrawRect = computeLayout(); invalRect.union(mDrawRect); invalRect.inset(-10, -10); mContext.invalidate(invalRect); }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int measuredWidth = measure(widthMeasureSpec); int measuredHeight = measure(heightMeasureSpec); int d = Math.min(measuredWidth, measuredHeight); setMeasuredDimension(d, d); }
/** * @param velocity * @return the page you should "land" on */ private int getNextPage(int velocity) { int nextPage; if (velocity > mMinimumVelocity) { nextPage = getCurrentPageFloor(); } else if (velocity < -mMinimumVelocity) { nextPage = getCurrentPageCeil(); } else { nextPage = getCurrentPageRound(); } return Math.min(Math.max(nextPage, 0), mPageCount - 1); }
public static int calculateInSampleSize( BitmapFactory.Options options, int reqWidth, int reqHeight) { int h = options.outHeight; int w = options.outWidth; int inSampleSize = 0; if (h > reqHeight || w > reqWidth) { float ratioW = (float) w / reqWidth; float ratioH = (float) h / reqHeight; inSampleSize = (int) Math.min(ratioH, ratioW); } inSampleSize = Math.max(1, inSampleSize); return inSampleSize; }
private void shadowsVisibility() { final int actualScrollX = getActualScrollX(); final int actualScrollY = getActualScrollY(); final int[] remainPixels = { actualScrollX, actualScrollY, getMaxScrollX() - actualScrollX, getMaxScrollY() - actualScrollY, }; for (int i = 0; i < shadows.length; i++) { setAlpha(shadows[i], Math.min(remainPixels[i] / (float) shadowSize, 1)); } }
private int scrollBounds(int desiredScroll, int firstCell, int sizes[], int viewSize) { if (desiredScroll == 0) { // no op } else if (desiredScroll < 0) { desiredScroll = Math.max(desiredScroll, -sumArray(sizes, 1, firstCell)); } else { desiredScroll = Math.min( desiredScroll, Math.max( 0, sumArray(sizes, firstCell + 1, sizes.length - 1 - firstCell) + sizes[0] - viewSize)); } return desiredScroll; }
/** * Determines the specs for the onMeasure function. Calculates the width or height depending on * the mode. * * @param measureSpecMode The mode of the measured width or height. * @param measureSpecSize The size of the measured width or height. * @param desiredSize The desired size of the measured width or height. * @return The final size of the width or height. */ private static int getOnMeasureSpec(int measureSpecMode, int measureSpecSize, int desiredSize) { // Measure Width int spec; if (measureSpecMode == MeasureSpec.EXACTLY) { // Must be this size spec = measureSpecSize; } else if (measureSpecMode == MeasureSpec.AT_MOST) { // Can't be bigger than...; match_parent value spec = Math.min(desiredSize, measureSpecSize); } else { // Be whatever you want; wrap_content spec = desiredSize; } return spec; }
private void drawSky(Canvas canvas) { Matrix matrix = mMatrix; matrix.reset(); int y = Math.max(0, mTop - mTotalDragDistance); // 0 ~ 1 float dragPercent = Math.min(1f, Math.abs(mPercent)); /** * Change skyScale between {@link #SKY_INITIAL_SCALE} and 1.0f depending on {@link #mPercent} */ // 根据拖动的比例改变天空的大小 float skyScale; float scalePercentDelta = dragPercent - SCALE_START_PERCENT; /** less than {@link SCALE_START_PERCENT} will be {@link SKY_INITIAL_SCALE} */ // 拖动比例小于0.3时使用默认的大小1.05 if (scalePercentDelta > 0) { /** will change from 0 ~ 1 * */ // skyScale = SKY_INITIAL_SCALE + (dragPercent - SCALE_START_PERCENT); float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT); skyScale = SKY_INITIAL_SCALE - (SKY_INITIAL_SCALE - 1.0f) * scalePercent; } else { skyScale = SKY_INITIAL_SCALE; } float offsetX = -(mScreenWidth * skyScale - mScreenWidth) / 2.0f; float offsetY = y + 50 + mSkyTopOffset // Offset canvas moving, goes lower when goes down - mSkyHeight * (skyScale - 1.0f) / 2 // Offset sky scaling, lower than 0, will go greater when goes down + mSkyMoveOffset * dragPercent; // Give it a little move top -> bottom // will go greater when goes // down matrix.postScale(skyScale, skyScale); matrix.postTranslate(offsetX, offsetY); canvas.drawBitmap(mSky, matrix, null); }
private void drawTown(Canvas canvas) { Matrix matrix = mMatrix; matrix.reset(); int y = Math.max(0, mTop - mTotalDragDistance); float dragPercent = Math.min(1f, Math.abs(mPercent)); float townScale; float townTopOffset; float townMoveOffset; float scalePercentDelta = dragPercent - SCALE_START_PERCENT; if (scalePercentDelta > 0) { /** * Change townScale between {@link #TOWN_INITIAL_SCALE} and {@link #TOWN_FINAL_SCALE} * depending on {@link #mPercent} Change townTopOffset between {@link #mTownInitialTopOffset} * and {@link #mTownFinalTopOffset} depending on {@link #mPercent} */ float scalePercent = scalePercentDelta / (1.0f - SCALE_START_PERCENT); townScale = TOWN_INITIAL_SCALE + (TOWN_FINAL_SCALE - TOWN_INITIAL_SCALE) * scalePercent; townTopOffset = mTownInitialTopOffset - (mTownFinalTopOffset - mTownInitialTopOffset) * scalePercent; townMoveOffset = mTownMoveOffset * (1.0f - scalePercent); } else { float scalePercent = dragPercent / SCALE_START_PERCENT; townScale = TOWN_INITIAL_SCALE; townTopOffset = mTownInitialTopOffset; townMoveOffset = mTownMoveOffset * scalePercent; } float offsetX = -(mScreenWidth * townScale - mScreenWidth) / 2.0f; // float offsetY = (1.0f - dragPercent) * mTotalDragDistance // Offset canvas moving float offsetY = y + +townTopOffset - mTownHeight * (townScale - 1.0f) / 2 // Offset town scaling + townMoveOffset; // Give it a little move matrix.postScale(townScale, townScale); // 移动建筑的中心点 matrix.postTranslate(offsetX, offsetY); canvas.drawBitmap(mTown, matrix, null); }
@Override public void draw(Canvas canvas) { super.draw(canvas); Rect r = getBounds(); // draw border if (borderThickness > 0) { drawBorder(canvas); } int count = canvas.save(); canvas.translate(r.left, r.top); // draw text int width = this.width < 0 ? r.width() : this.width; int height = this.height < 0 ? r.height() : this.height; int fontSize = this.fontSize < 0 ? (Math.min(width, height) / 2) : this.fontSize; textPaint.setTextSize(fontSize); canvas.drawText( text, width / 2, height / 2 - ((textPaint.descent() + textPaint.ascent()) / 2), textPaint); canvas.restoreToCount(count); }
private void dataSetChanged() { final int currentPage = mCurrentPageIndex; int newPosition = currentPage; // if the adapter has stable ids, try to keep the page currently on // stable. if (mAdapter.hasStableIds() && currentPage != INVALID_PAGE_POSITION) { newPosition = getNewPositionOfCurrentPage(); } else if (currentPage == INVALID_PAGE_POSITION) { newPosition = 0; } // remove all the current views recycleActiveViews(); mRecycler.setViewTypeCount(mAdapter.getViewTypeCount()); mRecycler.invalidateScraps(); mPageCount = mAdapter.getCount(); // put the current page within the new adapter range newPosition = Math.min(mPageCount - 1, newPosition == INVALID_PAGE_POSITION ? 0 : newPosition); if (newPosition != INVALID_PAGE_POSITION) { // TODO pretty confusing // this will be correctly set in setFlipDistance method mCurrentPageIndex = INVALID_PAGE_POSITION; mFlipDistance = INVALID_FLIP_DISTANCE; flipTo(newPosition); } else { mFlipDistance = INVALID_FLIP_DISTANCE; mPageCount = 0; setFlipDistance(0); } updateEmptyStatus(); }
public void build(DialogWireframe description, int w, int h, TelegramApplication application) { layoutH = h; layoutW = w; if (description.getPeerType() == PeerType.PEER_USER) { if (description.getPeerId() == 333000) { isHighlighted = false; } else { User user = description.getDialogUser(); isHighlighted = user.getLinkType() == LinkType.FOREIGN; } isGroup = false; isEncrypted = false; } else if (description.getPeerType() == PeerType.PEER_CHAT) { isHighlighted = false; isGroup = true; isEncrypted = false; } else if (description.getPeerType() == PeerType.PEER_USER_ENCRYPTED) { isHighlighted = false; isGroup = false; isEncrypted = true; } isBodyHighlighted = description.getContentType() != ContentType.MESSAGE_TEXT; if (description.getUnreadCount() != 0 && !description.isMine()) { isUnreadIn = true; } else { isUnreadIn = false; } time = org.telegram.android.ui.TextUtil.formatDate(description.getDate(), application); isRtl = application.isRTL(); if (IS_LARGE) { layoutAvatarWidth = px(64); layoutPadding = application.getResources().getDimensionPixelSize(R.dimen.dialogs_padding); layoutBodyPadding = layoutAvatarWidth + layoutPadding + px(12); layoutAvatarTop = px(8); layoutTitleTop = px(34); layoutMainTop = px(60); layoutTimeTop = px(34); layoutMarkTop = px(44); layoutMarkBottom = layoutMarkTop + px(22); layoutMarkTextTop = layoutMarkTop + px(18); } else { layoutAvatarWidth = px(54); layoutPadding = application.getResources().getDimensionPixelSize(R.dimen.dialogs_padding); layoutBodyPadding = layoutAvatarWidth + layoutPadding + px(12); layoutAvatarTop = px(8); layoutTitleTop = px(30); layoutMainTop = px(54); layoutTimeTop = px(30); layoutMarkTop = px(38); layoutMarkBottom = layoutMarkTop + px(22); layoutMarkTextTop = layoutMarkTop + px(18); } layoutMainContentTop = (int) (layoutMainTop + bodyPaint.getFontMetrics().ascent); layoutTitleLayoutTop = (int) (layoutTitleTop + titlePaint.getFontMetrics().ascent); layoutStateTop = layoutTimeTop - px(10); layoutClockTop = layoutTimeTop - px(12); layoutEncryptedTop = layoutTimeTop - px(14); if (isRtl) { layoutAvatarLeft = w - layoutPadding - layoutAvatarWidth; } else { layoutAvatarLeft = layoutPadding; } int timeWidth = (int) unreadClockPaint.measureText(time); if (isRtl) { layoutTimeLeft = layoutPadding; layoutStateLeftDouble = layoutPadding + timeWidth + px(2); layoutStateLeft = layoutStateLeftDouble + px(6); layoutClockLeft = layoutPadding + timeWidth + px(2); } else { layoutTimeLeft = w - layoutPadding - timeWidth; layoutClockLeft = w - layoutPadding - timeWidth - px(14); layoutStateLeft = w - layoutPadding - timeWidth - px(16); layoutStateLeftDouble = w - layoutPadding - timeWidth - px(6 + 16); } layoutMarkRadius = px(2); if (description.isErrorState() || (description.getMessageState() == MessageState.FAILURE && description.isMine())) { layoutMarkWidth = px(22); if (isRtl) { layoutMarkLeft = layoutPadding; // getMeasuredWidth() - layoutMarkWidth - getPx(80); } else { layoutMarkLeft = w - layoutMarkWidth - layoutPadding; } } else { if (description.getUnreadCount() > 0) { if (description.getUnreadCount() >= 1000) { unreadCountText = I18nUtil.getInstance().correctFormatNumber(description.getUnreadCount() / 1000) + "K"; } else { unreadCountText = I18nUtil.getInstance().correctFormatNumber(description.getUnreadCount()); } int width = (int) counterTitlePaint.measureText(unreadCountText); Rect r = new Rect(); counterTitlePaint.getTextBounds(unreadCountText, 0, unreadCountText.length(), r); layoutMarkTextTop = layoutMarkTop + (layoutMarkBottom - layoutMarkTop + r.top) / 2 - r.top; if (width < px(22 - 14)) { layoutMarkWidth = px(22); } else { layoutMarkWidth = px(14) + width; } layoutMarkTextLeft = (layoutMarkWidth - width) / 2; if (isRtl) { layoutMarkLeft = layoutPadding; // getMeasuredWidth() - layoutMarkWidth - getPx(80); } else { layoutMarkLeft = w - layoutMarkWidth - layoutPadding; } } else { layoutMarkLeft = 0; layoutMarkWidth = 0; } } layoutMarkRect.set( layoutMarkLeft, layoutMarkTop, layoutMarkLeft + layoutMarkWidth, layoutMarkBottom); if (description.getPeerType() == PeerType.PEER_USER_ENCRYPTED) { if (isRtl) { if (description.isMine()) { layoutTitleLeft = timeWidth + px(16) + px(16); } else { layoutTitleLeft = timeWidth + px(12); } layoutTitleWidth = w - layoutTitleLeft - layoutBodyPadding - px(14) - px(6); layoutEncryptedLeft = w - layoutBodyPadding - px(12); } else { layoutTitleLeft = layoutBodyPadding + px(16); if (description.isMine()) { layoutTitleWidth = w - layoutTitleLeft - timeWidth - px(24); } else { layoutTitleWidth = w - layoutTitleLeft - timeWidth - px(12); } layoutEncryptedLeft = layoutBodyPadding + px(2); } } else { if (isRtl) { if (description.isMine()) { layoutTitleLeft = timeWidth + px(16) + px(16); } else { layoutTitleLeft = timeWidth + px(12); } layoutTitleWidth = w - layoutTitleLeft - layoutBodyPadding; } else { layoutTitleLeft = layoutBodyPadding; if (description.isMine()) { layoutTitleWidth = w - layoutTitleLeft - timeWidth - px(24) - px(12); } else { layoutTitleWidth = w - layoutTitleLeft - timeWidth - px(12); } } } layoutMainWidth = w - layoutBodyPadding - layoutPadding; if (isRtl) { layoutMainLeft = w - layoutMainWidth - layoutBodyPadding; if (layoutMarkWidth != 0) { layoutMainLeft += layoutMarkWidth + px(8); layoutMainWidth -= layoutMarkWidth + px(8); } } else { layoutMainLeft = layoutBodyPadding; if (layoutMarkWidth != 0) { layoutMainWidth -= layoutMarkWidth + px(8); } } avatarRect.set( layoutAvatarLeft, layoutAvatarTop, layoutAvatarLeft + layoutAvatarWidth, layoutAvatarTop + layoutAvatarWidth); // Building text layouts { String message = description.getMessage(); if (message.length() > 150) { message = message.substring(0, 150) + "..."; } message = message.replace("\n", " "); TextPaint bodyTextPaint; if (isBodyHighlighted) { bodyTextPaint = bodyHighlightPaint; } else { if (HIGHLIGHT_UNDEAD) { if (isUnreadIn) { bodyTextPaint = bodyUnreadPaint; } else { bodyTextPaint = bodyPaint; } } else { bodyTextPaint = bodyPaint; } } int nameLength = 0; if (description.getContentType() != ContentType.MESSAGE_SYSTEM) { if (description.isMine()) { String name = application.getResources().getString(R.string.st_dialog_you); nameLength = BidiFormatter.getInstance().unicodeWrap(name).length(); message = BidiFormatter.getInstance().unicodeWrap(name) + ": " + BidiFormatter.getInstance().unicodeWrap(message); } else { if (isGroup) { User user = description.getSender(); nameLength = BidiFormatter.getInstance().unicodeWrap(user.getFirstName()).length(); message = BidiFormatter.getInstance().unicodeWrap(user.getFirstName().replace("\n", " ")) + ": " + BidiFormatter.getInstance().unicodeWrap(message); } } } String preSequence = TextUtils.ellipsize(message, bodyTextPaint, layoutMainWidth, TextUtils.TruncateAt.END) .toString(); // Spannable sequence = // application.getEmojiProcessor().processEmojiCutMutable(preSequence, // EmojiProcessor.CONFIGURATION_DIALOGS); // if (nameLength != 0) { // sequence.setSpan(new ForegroundColorSpan(HIGHLIGHT_COLOR), 0, // Math.min(nameLength, sequence.length()), Spanned.SPAN_EXCLUSIVE_INCLUSIVE); // } // CharSequence resSequence = TextUtils.ellipsize(sequence, bodyTextPaint, // layoutMainWidth, TextUtils.TruncateAt.END); // bodyLayout = new StaticLayout(resSequence, bodyTextPaint, layoutMainWidth, // Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); // bodyString = null; Spannable sequence = application .getEmojiProcessor() .processEmojiCutMutable(preSequence, EmojiProcessor.CONFIGURATION_DIALOGS); if (nameLength != 0) { sequence.setSpan( new ForegroundColorSpan(HIGHLIGHT_COLOR), 0, Math.min(nameLength, sequence.length()), Spanned.SPAN_EXCLUSIVE_INCLUSIVE); } CharSequence resSequence = TextUtils.ellipsize(sequence, bodyTextPaint, layoutMainWidth, TextUtils.TruncateAt.END); bodyLayout = new StaticLayout( resSequence, bodyTextPaint, layoutMainWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); bodyString = null; // if (EmojiProcessor.containsEmoji(message)) { // Spannable sequence = // application.getEmojiProcessor().processEmojiCutMutable(preSequence, // EmojiProcessor.CONFIGURATION_DIALOGS); // if (nameLength != 0) { // sequence.setSpan(new ForegroundColorSpan(HIGHLIGHT_COLOR), 0, // Math.min(nameLength, sequence.length()), Spanned.SPAN_EXCLUSIVE_INCLUSIVE); // } // // CharSequence resSequence = TextUtils.ellipsize(sequence, // bodyTextPaint, layoutMainWidth, TextUtils.TruncateAt.END); // bodyLayout = new StaticLayout(resSequence, bodyTextPaint, // layoutMainWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); // bodyString = null; // } else { // bodyString = preSequence; // bodyLayout = null; // } } // Title { String title = description.getDialogTitle(); if (title.length() > 150) { title = title.substring(150) + "..."; } title = title.replace("\n", " "); TextPaint paint = isEncrypted ? titleEncryptedPaint : (isHighlighted ? titleHighlightPaint : titlePaint); // Spannable preSequence = // application.getEmojiProcessor().processEmojiCutMutable(title, // EmojiProcessor.CONFIGURATION_DIALOGS); // CharSequence sequence = TextUtils.ellipsize(preSequence, paint, // layoutTitleWidth, TextUtils.TruncateAt.END); // titleLayout = new StaticLayout(sequence, paint, layoutTitleWidth, // Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); // titleString = null; if (EmojiProcessor.containsEmoji(title)) { Spannable preSequence = application .getEmojiProcessor() .processEmojiCutMutable(title, EmojiProcessor.CONFIGURATION_DIALOGS); CharSequence sequence = TextUtils.ellipsize(preSequence, paint, layoutTitleWidth, TextUtils.TruncateAt.END); titleLayout = new StaticLayout( sequence, paint, layoutTitleWidth, Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); titleString = null; } else { titleString = TextUtils.ellipsize(title, paint, layoutTitleWidth, TextUtils.TruncateAt.END) .toString(); titleLayout = null; } } // Placeholder placeHolderName = description.getDialogName(); placeHolderColor = Placeholders.getBgColor(description.getPeerId()); if (placeHolderName.length() > 0) { usePlaceholder = true; placeholderLeft = layoutAvatarLeft + layoutAvatarWidth / 2; Rect rect = new Rect(); placeholderTextPaint.getTextBounds(placeHolderName, 0, placeHolderName.length(), rect); placeholderTop = layoutAvatarTop + (layoutAvatarWidth / 2 + ((rect.bottom - rect.top) / 2)); } else { usePlaceholder = false; } }
@SuppressLint("DrawAllocation") @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { if (needRelayout || changed) { needRelayout = false; resetTable(); if (adapter != null) { width = r - l; height = b - t; int left, top, right, bottom; right = Math.min(width, sumArray(widths)); bottom = Math.min(height, sumArray(heights)); addShadow(shadows[0], widths[0], 0, widths[0] + shadowSize, bottom); addShadow(shadows[1], 0, heights[0], right, heights[0] + shadowSize); addShadow(shadows[2], right - shadowSize, 0, right, bottom); addShadow(shadows[3], 0, bottom - shadowSize, right, bottom); addLineView( widths[0], heights[0], sumArray(widths, 1, widths.length - 1), sumArray(heights, 1, heights.length - 1)); headView = makeAndSetup(-1, -1, 0, 0, widths[0], heights[0]); scrollBounds(); adjustFirstCellsAndScroll(); left = widths[0] - scrollX; for (int i = firstColumn; i < columnCount && left < width; i++) { right = left + widths[i + 1]; final View view = makeAndSetup(-1, i, left, 0, right, heights[0]); rowViewList.add(view); left = right; } top = heights[0] - scrollY; for (int i = firstRow; i < rowCount && top < height; i++) { bottom = top + heights[i + 1]; final View view = makeAndSetup(i, -1, 0, top, widths[0], bottom); columnViewList.add(view); top = bottom; } top = heights[0] - scrollY; for (int i = firstRow; i < rowCount && top < height; i++) { bottom = top + heights[i + 1]; left = widths[0] - scrollX; List<View> list = new ArrayList<View>(); for (int j = firstColumn; j < columnCount && left < width; j++) { right = left + widths[j + 1]; final View view = makeAndSetup(i, j, left, top, right, bottom); list.add(view); left = right; } bodyViewTable.add(list); top = bottom; } shadowsVisibility(); } } }
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int widthMode = MeasureSpec.getMode(widthMeasureSpec); final int heightMode = MeasureSpec.getMode(heightMeasureSpec); final int widthSize = MeasureSpec.getSize(widthMeasureSpec); final int heightSize = MeasureSpec.getSize(heightMeasureSpec); final int w; final int h; if (adapter != null) { this.rowCount = adapter.getRowCount(); this.columnCount = adapter.getColumnCount(); widths = new int[columnCount + 1]; for (int i = -1; i < columnCount; i++) { widths[i + 1] += adapter.getWidth(i); } heights = new int[rowCount + 1]; for (int i = -1; i < rowCount; i++) { heights[i + 1] += adapter.getHeight(i); } if (widthMode == MeasureSpec.AT_MOST) { w = Math.min(widthSize, sumArray(widths)); } else if (widthMode == MeasureSpec.UNSPECIFIED) { w = sumArray(widths); } else { w = widthSize; int sumArray = sumArray(widths); if (sumArray < widthSize) { final float factor = widthSize / (float) sumArray; for (int i = 1; i < widths.length; i++) { widths[i] = Math.round(widths[i] * factor); } widths[0] = widthSize - sumArray(widths, 1, widths.length - 1); } } if (heightMode == MeasureSpec.AT_MOST) { h = Math.min(heightSize, sumArray(heights)); } else if (heightMode == MeasureSpec.UNSPECIFIED) { h = sumArray(heights); } else { h = heightSize; } } else { if (heightMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) { w = 0; h = 0; } else { w = widthSize; h = heightSize; } } if (firstRow >= rowCount || getMaxScrollY() - getActualScrollY() < 0) { firstRow = 0; scrollY = Integer.MAX_VALUE; } if (firstColumn >= columnCount || getMaxScrollX() - getActualScrollX() < 0) { firstColumn = 0; scrollX = Integer.MAX_VALUE; } setMeasuredDimension(w, h); }
@Override protected void onDraw(Canvas canvas) { float ringWidth = textHeight + 4; int height = getMeasuredHeight(); int width = getMeasuredWidth(); int px = width / 2; int py = height / 2; Point center = new Point(px, py); int radius = Math.min(px, py) - 2; RectF boundingBox = new RectF(center.x - radius, center.y - radius, center.x + radius, center.y + radius); RectF innerBoundingBox = new RectF( center.x - radius + ringWidth, center.y - radius + ringWidth, center.x + radius - ringWidth, center.y + radius - ringWidth); float innerRadius = innerBoundingBox.height() / 2; RadialGradient borderGradient = new RadialGradient( px, py, radius, borderGradientColors, borderGradientPositions, TileMode.CLAMP); Paint pgb = new Paint(); pgb.setShader(borderGradient); Path outerRingPath = new Path(); outerRingPath.addOval(boundingBox, Direction.CW); canvas.drawPath(outerRingPath, pgb); LinearGradient skyShader = new LinearGradient( center.x, innerBoundingBox.top, center.x, innerBoundingBox.bottom, skyHorizonColorFrom, skyHorizonColorTo, TileMode.CLAMP); Paint skyPaint = new Paint(); skyPaint.setShader(skyShader); LinearGradient groundShader = new LinearGradient( center.x, innerBoundingBox.top, center.x, innerBoundingBox.bottom, groundHorizonColorFrom, groundHorizonColorTo, TileMode.CLAMP); Paint groundPaint = new Paint(); groundPaint.setShader(groundShader); float tiltDegree = pitch; while (tiltDegree > 90 || tiltDegree < -90) { if (tiltDegree > 90) tiltDegree = -90 + (tiltDegree - 90); if (tiltDegree < -90) tiltDegree = 90 - (tiltDegree + 90); } float rollDegree = roll; while (rollDegree > 180 || rollDegree < -180) { if (rollDegree > 180) rollDegree = -180 + (rollDegree - 180); if (rollDegree < -180) rollDegree = 180 - (rollDegree + 180); } Path skyPath = new Path(); skyPath.addArc(innerBoundingBox, -rollDegree, (180 + (2 * rollDegree))); canvas.rotate(-tiltDegree, px, py); canvas.drawOval(innerBoundingBox, groundPaint); canvas.drawPath(skyPath, skyPaint); canvas.drawPath(skyPath, markerPaint); int markWidth = radius / 3; int startX = center.x - markWidth; int endX = center.x + markWidth; Log.d("PAARV ", "Roll " + String.valueOf(rollDegree)); Log.d("PAARV ", "Pitch " + String.valueOf(tiltDegree)); double h = innerRadius * Math.cos(Math.toRadians(90 - tiltDegree)); double justTiltX = center.x - h; float pxPerDegree = (innerBoundingBox.height() / 2) / 45f; for (int i = 90; i >= -90; i -= 10) { double ypos = justTiltX + i * pxPerDegree; if ((ypos < (innerBoundingBox.top + textHeight)) || (ypos > innerBoundingBox.bottom - textHeight)) continue; canvas.drawLine(startX, (float) ypos, endX, (float) ypos, markerPaint); int displayPos = (int) (tiltDegree - i); String displayString = String.valueOf(displayPos); float stringSizeWidth = textPaint.measureText(displayString); canvas.drawText( displayString, (int) (center.x - stringSizeWidth / 2), (int) (ypos) + 1, textPaint); } markerPaint.setStrokeWidth(2); canvas.drawLine( center.x - radius / 2, (float) justTiltX, center.x + radius / 2, (float) justTiltX, markerPaint); markerPaint.setStrokeWidth(1); Path rollArrow = new Path(); rollArrow.moveTo(center.x - 3, (int) innerBoundingBox.top + 14); rollArrow.lineTo(center.x, (int) innerBoundingBox.top + 10); rollArrow.moveTo(center.x + 3, innerBoundingBox.top + 14); rollArrow.lineTo(center.x, innerBoundingBox.top + 10); canvas.drawPath(rollArrow, markerPaint); String rollText = String.valueOf(rollDegree); double rollTextWidth = textPaint.measureText(rollText); canvas.drawText( rollText, (float) (center.x - rollTextWidth / 2), innerBoundingBox.top + textHeight + 2, textPaint); canvas.restore(); canvas.save(); canvas.rotate(180, center.x, center.y); for (int i = -180; i < 180; i += 10) { if (i % 30 == 0) { String rollString = String.valueOf(i * -1); float rollStringWidth = textPaint.measureText(rollString); PointF rollStringCenter = new PointF(center.x - rollStringWidth / 2, innerBoundingBox.top + 1 + textHeight); canvas.drawText(rollString, rollStringCenter.x, rollStringCenter.y, textPaint); } else { canvas.drawLine( center.x, (int) innerBoundingBox.top, center.x, (int) innerBoundingBox.top + 5, markerPaint); } canvas.rotate(10, center.x, center.y); } canvas.restore(); canvas.save(); canvas.rotate(-1 * (bearing), px, py); double increment = 22.5; for (double i = 0; i < 360; i += increment) { CompassDirection cd = CompassDirection.values()[(int) (i / 22.5)]; String headString = cd.toString(); float headStringWidth = textPaint.measureText(headString); PointF headStringCenter = new PointF(center.x - headStringWidth / 2, boundingBox.top + 1 + textHeight); if (i % increment == 0) canvas.drawText(headString, headStringCenter.x, headStringCenter.y, textPaint); else canvas.drawLine( center.x, (int) boundingBox.top, center.x, (int) boundingBox.top + 3, markerPaint); canvas.rotate((int) increment, center.x, center.y); } canvas.restore(); RadialGradient glassShader = new RadialGradient( px, py, (int) innerRadius, glassGradientColors, glassGradientPositions, TileMode.CLAMP); Paint glassPaint = new Paint(); glassPaint.setShader(glassShader); canvas.drawOval(innerBoundingBox, glassPaint); canvas.drawOval(boundingBox, circlePaint); circlePaint.setStrokeWidth(2); canvas.drawOval(innerBoundingBox, circlePaint); canvas.restore(); }