Professional Documents
Culture Documents
google.com/+RomainGuy google.com/+ChetHaase
@romainguy @chethaase
google.com/+RomainGuy google.com/+ChetHaase
@romainguy @chethaase
Shi ny !
@romainguy @chethaase
google.com/+RomainGuy google.com/+ChetHaase
Graphics
#DV13 #FilthyRichAndroid4
Thursday, November 14, 13
#DV13 #FilthyRichAndroid5
Thursday, November 14, 13
What is a shader?
A set of instructions that computes the source color of a pixel being drawn. Chet or Romain, just now
#DV13 #FilthyRichAndroid6
Thursday, November 14, 13
Example
#DV13 #FilthyRichAndroid7
Thursday, November 14, 13
Example
t s e l p Sim
r e v e r e d a sh
#DV13 #FilthyRichAndroid7
Thursday, November 14, 13
Android shaders
Similar to OpenGL fragment shaders Not programmable Subclasses of android.graphics.Shader
- BitmapShader - ComposeShader - LinearGradient - RadialGradient - SweepGradient
#DV13 #FilthyRichAndroid8
Thursday, November 14, 13
#DV13 #FilthyRichAndroid9
Thursday, November 14, 13
Back to images
Back to images
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
Back to images
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setShader(shader);
Back to images
BitmapShader shader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); Paint paint = new Paint(); paint.setAntiAlias(true); paint.setShader(shader); RectF rect = new RectF(0.0f, 0.0f, width, height); canvas.drawRoundRect(rect, radius, radius, paint);
e t t igne
No v ig ne t te
ComposeShader
LinearGradient
xfermode
BitmapShader
ComposeShader
Vignette
Vignette
RadialGradient vignette = new RadialGradient( mRect.centerX(), mRect.centerY(), radius, new int[] { 0, 0, 0x7f000000 }, new float[] { 0.0f, 0.7f, 1.0f }, Shader.TileMode.CLAMP);
Vignette
RadialGradient vignette = new RadialGradient( mRect.centerX(), mRect.centerY(), radius, new int[] { 0, 0, 0x7f000000 }, new float[] { 0.0f, 0.7f, 1.0f }, Shader.TileMode.CLAMP); Matrix oval = new Matrix(); oval.setScale(1.0f, 0.7f); vignette.setLocalMatrix(oval);
Vignette
RadialGradient vignette = new RadialGradient( mRect.centerX(), mRect.centerY(), radius, new int[] { 0, 0, 0x7f000000 }, new float[] { 0.0f, 0.7f, 1.0f }, Shader.TileMode.CLAMP); Matrix oval = new Matrix(); oval.setScale(1.0f, 0.7f); vignette.setLocalMatrix(oval); mPaint.setShader(new ComposeShader( mBitmapShader, vignette, PorterDuff.Mode.SRC_OVER));
14 #DV13 #FilthyRichAndroid Thursday, November 14, 13
drawCircle()
drawPath()
drawPath()
15 #DV13 #FilthyRichAndroid
Animation
Animation APIs
View properties: ViewPropertyAnimator
view.animate().alpha(0).translationX(-500);
Timing is Everything
Make those animations short! And non-linear
L i s tV
ie w A
nim a
t io n!
ListView Animations
Recycling containers are tricky
- Views != items
Shadowed Background
protected void onDraw(Canvas canvas) { if (mShowing) { if (mUpdateBounds) { mShadowedBackground.setBounds(0, 0, getWidth(), mOpenAreaHeight); } canvas.save(); canvas.translate(0, mOpenAreaTop); mShadowedBackground.draw(canvas); canvas.restore(); } }
21 #DV13 #FilthyRichAndroid Thursday, November 14, 13
Swiping: Move/Fade
public boolean onTouch(final View v, MotionEvent event) { switch (event.getAction()) { // skipping DOWN/CANCEL/UP events case MotionEvent.ACTION_MOVE: { if (!mSwiping) { if (deltaXAbs > mSwipeSlop) { mSwiping = true; mListView.requestDisallowInterceptTouchEvent(true); mBackgroundContainer.showBackground(v.getTop(), v.getHeight()); } } if (mSwiping) { v.setTranslationX((x - mDownX)); v.setAlpha(1 - deltaXAbs / v.getWidth()); } } break; } }
23 #DV13 #FilthyRichAndroid Thursday, November 14, 13
Animate out
v.animate().setDuration(duration). alpha(endAlpha).translationX(endX). withEndAction(new Runnable() { @Override public void run() { v.setAlpha(1); v.setTranslationX(0); animateRemoval(mListView, v); } });
C i rc u
lar R
e ve a
l!
Circular reveal
Circular reveal
Technique similar to images with rounded corners
- Uses a BitmapShader
The mask is not a vector shape Uses an ALPHA_8 bitmap as the mask
- Converted from any type of bitmap
Circular reveal
Bitmap texture
private static Bitmap createBitmap(View target) { Bitmap b = Bitmap.createBitmap( target.getWidth(), target.getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(b); target.draw(c); return b; }
private void createShader() { View target = getRootView().findViewById(mTargetId); mTargetBitmap = createBitmap(target); Shader targetShader = new BitmapShader(mTargetBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP); mPaint.setShader(targetShader); }
= = = = =
"maskX", leftPos); "maskScale", scale1); "maskX", centerX); "maskY", centerY); "maskScale", scale2);
Choreographing
Filter reveal
Same exact implementation as before Draws the spotlight on top of original photo Spots position depends on where you tapped the button
Google Now
No a n t i a l i a
Thursday, November 14, 13
s i ng!
40 #DV13 #FilthyRichAndroid
Path clipping
Path clipping
Pros
- Easy to implement - Uses less memory - Faster to setup (no Bitmap copy)
Cons
- Android 4.3+ only with hardware acceleration - No antialiasing - Can be very expensive - Increases overdraw
Ac t i v
i t y Tr
ansi t
io ns!
Grayscale thumbnails
ColorMatrix grayMatrix = new ColorMatrix(); grayMatrix.setSaturation(0); ColorMatrixColorFilter grayscaleFilter = new ColorMatrixColorFilter(grayMatrix); thumbnailDrawable.setColorFilter(grayscaleFilter);
Launch sub-activity
int[] screenLocation = new int[2]; v.getLocationOnScreen(screenLocation); PictureData info = mPicturesData.get(v); int orientation = getResources().getConfiguration().orientation; Intent subActivity = new Intent(ActivityAnimations.this, PictureDetailsActivity.class); subActivity.putExtra(PACKAGE + ".orientation", orientation). putExtra(PACKAGE + ".resourceId", info.resourceId). putExtra(PACKAGE + ".left", screenLocation[0]). putExtra(PACKAGE + ".top", screenLocation[1]). putExtra(PACKAGE + ".width", v.getWidth()). putExtra(PACKAGE + ".height", v.getHeight()). putExtra(PACKAGE + ".description", info.description); startActivity(subActivity); overridePendingTransition(0, 0);
51 #DV13 #FilthyRichAndroid Thursday, November 14, 13
Bundle bundle = getIntent().getExtras(); Bitmap bitmap = BitmapUtils.getBitmap(getResources(), bundle.getInt(PACKAGE_NAME + ".resourceId")); String description = bundle.getString(PACKAGE_NAME + ".description"); final int thumbnailTop = bundle.getInt(PACKAGE_NAME + ".top"); final int thumbnailLeft = bundle.getInt(PACKAGE_NAME + ".left"); final int thumbnailWidth = bundle.getInt(PACKAGE_NAME + ".width"); final int thumbnailHeight = bundle.getInt(PACKAGE_NAME + ".height"); mOriginalOrientation = bundle.getInt(PACKAGE_NAME + ".orientation");
Colorize thumbnail
ObjectAnimator colorizer = ObjectAnimator.ofFloat( PictureDetailsActivity.this, "saturation", 0, 1); colorizer.start(); public void setSaturation(float value) { colorizerMatrix.setSaturation(value); ColorMatrixColorFilter colorizerFilter = new ColorMatrixColorFilter(colorizerMatrix); mBitmapDrawable.setColorFilter(colorizerFilter); }
56 #DV13 #FilthyRichAndroid Thursday, November 14, 13
Fo ldi n
g L ay
ou t !
Fan Fare
M at r i x . s e tPo l yToPo
l y()
for (int x = 0; x < mNumberOfFolds; x++) { src = mFoldRectArray[x]; canvas.save(); canvas.concat(mMatrix[x]); canvas.clipRect(0, 0, src.width(), src.height()); canvas.translate(-src.left, 0); super.dispatchDraw(canvas); if (x % 2 == 0) { canvas.drawRect(0, 0, mFoldW, mFoldH, mSolidShadow); } else { canvas.drawRect(0, 0, mFoldW, mFoldH, mSoftShadow); } canvas.restore(); }
for (int x = 0; x < mNumberOfFolds; x++) { src = mFoldRectArray[x]; canvas.save(); canvas.concat(mMatrix[x]); canvas.clipRect(0, 0, src.width(), src.height()); canvas.translate(-src.left, 0); super.dispatchDraw(canvas); if (x % 2 == 0) { canvas.drawRect(0, 0, mFoldW, mFoldH, mSolidShadow); } else { canvas.drawRect(0, 0, mFoldW, mFoldH, mSoftShadow); } canvas.restore(); }
for (int x = 0; x < mNumberOfFolds; x++) { src = mFoldRectArray[x]; canvas.save(); canvas.concat(mMatrix[x]); canvas.clipRect(0, 0, src.width(), src.height()); canvas.translate(-src.left, 0); super.dispatchDraw(canvas); if (x % 2 == 0) { canvas.drawRect(0, 0, mFoldW, mFoldH, mSolidShadow); } else { canvas.drawRect(0, 0, mFoldW, mFoldH, mSoftShadow); } canvas.restore(); }
for (int x = 0; x < mNumberOfFolds; x++) { src = mFoldRectArray[x]; canvas.save(); canvas.concat(mMatrix[x]); canvas.clipRect(0, 0, src.width(), src.height()); canvas.translate(-src.left, 0); super.dispatchDraw(canvas); if (x % 2 == 0) { canvas.drawRect(0, 0, mFoldW, mFoldH, mSolidShadow); } else { canvas.drawRect(0, 0, mFoldW, mFoldH, mSoftShadow); } canvas.restore(); }
Color Filters
Can be used to modify a shader Subclasses of ColorFilter
- ColorMatrixColorFilter - LightingColorFilter - PorterDu!ColorFilter
Sepia Effect
ColorMatrix m1 = new ColorMatrix(); ColorMatrix m2 = new ColorMatrix(); m1.setSaturation(0.1f); m2.setScale(1f, 0.95f, 0.82f, 1.0f); m1.setConcat(m2, m1); mSepiaPaint.setColorFilter( new ColorMatrixColorFilter(m1));
Performance
Smoother is Better
Consistent frame rate Avoid hiccups Avoid large steps over few frames
Avoid Overdraw
Developer options -> Show Overdraw Window background vs. opaque containers vs. opaque views
clipPath
Not always the fastest way to clip to a path Doesnt support antialiasing Try BitmapShader
Chet
graphics-geek.blogspot.com google.com/+ChetHaase @chethaase
Q&A
75 #DV13 #FilthyRichAndroid