Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Engaging User Experiences: Periscope Hearts And Chats

Lien
March 18, 2016

Engaging User Experiences: Periscope Hearts And Chats

Video:
https://www.youtube.com/watch?v=pr1juTDeEqI

Abstract:
Periscope is a live streaming video app that makes watching broadcasts a truly interactive experience. Viewers can participate in a live broadcast by sending hearts and chats to the broadcaster. This talk will provide insight into how the Android team built this core user experience. You’ll learn what gives hearts their light, airy feel, how they flutter on the screen, and how chats animate on and off the screen. We’ll describe the why, what, and how behind them, and walk through actual code that powers these custom views and animations.

Lien

March 18, 2016
Tweet

Other Decks in Programming

Transcript

  1. en·gag·ing
 adjective
 
 very attractive or pleasing in a way

    that holds your attention From: Merriam Webster
  2. // Draw the border
 Bitmap bitmap = Bitmap.createBitmap(border.getWidth(), 
 border.getHeight(),

    Bitmap.Config.ARGB_8888);
 Canvas canvas = new Canvas(bitmap);
 canvas.drawBitmap(border, 0, 0, paint);
 
 
 
 
 
 
 
 
 
 

  3. // Draw the border
 Bitmap bitmap = Bitmap.createBitmap(border.getWidth(), 
 border.getHeight(),

    Bitmap.Config.ARGB_8888);
 Canvas canvas = new Canvas(bitmap);
 canvas.drawBitmap(border, 0, 0, paint);
 
 // Set up the color
 color = ParticipantUtil.get(res, participantIndex);
 colorFilter = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP);
 paint.setColorFilter(colorFilter);
 
 
 
 

  4. // Draw the border
 Bitmap bitmap = Bitmap.createBitmap(border.getWidth(), 
 border.getHeight(),

    Bitmap.Config.ARGB_8888);
 Canvas canvas = new Canvas(bitmap);
 canvas.drawBitmap(border, 0, 0, paint);
 
 // Set up the color
 color = ParticipantUtil.get(res, participantIndex);
 colorFilter = new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP);
 paint.setColorFilter(colorFilter);
 
 // Draw the fill
 float x = (border.getWidth() - fill.getWidth()) / 2f;
 float y = (border.getHeight() - fill.getHeight()) / 2f;
 canvas.drawBitmap(fill, x, y, paint);
  5. “Objects are presented to the user without breaking the continuity

    of experience even as they transform and reorganize.” From Material Design Introduction
  6. The Path to our Hearts • Paths, for better control

    • cubic Bezier curves • randomization (weaving, length)
  7. // Bezier weaving randomization
 Random r = sRandom;
 int i

    = r.nextInt(mXRand);
 int i2 = r.nextInt(mXRand);
 
 int length =(HEART_COUNT_MULTIPLIER * heartCount.intValue()) + mLength * lengthMultiplier;
 
 // Length randomization
 length += r.nextInt(mLengthRand); HeartRenderer.java
  8. // Set up bezier curve
 ObjectAnimator curve = ObjectAnimator.ofFloat(view, View.X,


    View.Y, bezierPath);
 curve.setDuration(DURATION);
 
 // Set up rotation
 float rotationAmount = randomizedRotationAmount();
 ObjectAnimator rotation = ObjectAnimator.ofFloat(view,
 View.ROTATION, 0, rotationAmount);
 rotation.setDuration(DURATION);
 
 // Set up alpha
 ObjectAnimator alpha = ObjectAnimator.ofFloat(view,
 View.ALPHA, 1.0f, 0.0f);
 alpha.setDuration(DURATION); HeartRenderer.java
  9. // Play bezier path animation, rotation, and alpha
 List<Animator> items

    = new ArrayList<>();
 items.add(curve);
 items.add(rotation);
 items.add(alpha);
 AnimatorSet bezierSet = new AnimatorSet();
 bezierSet.setInterpolator(new LinearInterpolator());
 bezierSet.playTogether(items);
 bezierSet.start(); HeartRenderer.java
  10. LinearLayoutManager layout = new LinearLayoutManager(c) {
 @Override
 public boolean canScrollVertically()

    {
 return false;
 }
 };
 layout.setStackFromEnd(true); 
 ChatMessageContainerView.java
  11. LinearLayoutManager layout = new LinearLayoutManager(c) {
 @Override
 public boolean canScrollVertically()

    {
 return false;
 }
 };
 layout.setStackFromEnd(true);
 mRecyclerView.setLayoutManager(layout);
 mRecyclerView.setHasFixedSize(true); mRecyclerView.setItemAnimator(new ChatMessageAnimator()); ChatMessageContainerView.java
  12. public void addMessage(Message m) {
 mAdapter.queue(m);
 int lastPosition = mAdapter.getItemCount()

    - 1;
 mRecyclerView.scrollToPosition(lastPosition);
 }
 
 ChatMessageContainerView.java
  13. public void queue(Message m) {
 switch (m.type()) {
 case Join:

    {
 if (shouldMergeItem(m)) {
 mergeItem(m);
 return;
 }
 break;
 }
 }
 
 addItem(m);
 } ChatMessageAdapter.java
  14. private void mergeItem(Message m) {
 int index = mItems.size() -

    1;
 MessageItem lastItem = mItems.remove(index);
 MessageItem newItem = new MessageItem(m, lastItem.id);
 newItem.merge(lastItem.merge() + 1);
 mItems.add(newItem);
 mLastMsgType = m.type(); notifyItemChanged(index);
 } ChatMessageAdapter.java
  15. private void addItem(Message m) {
 mItems.add(new MessageItem(m, mId++));
 mLastMsgType =

    m.type();
 notifyItemInserted(mItems.size());
 } ChatMessageAdapter.java
  16. @Override public void onBindViewHolder(MessageItemHolder h,
 int position) {
 MessageItem item

    = getItem(position);
 switch (item.message.type()) {
 case Join: {
 bindItem((TextItemHolder) h, item);
 break;
 }
 }
 setUpFadeAnimation(h, item);
 } ChatMessageAdapter.java
  17. private void setUpFadeAnimation(MessageItemHolder h,
 MessageItem item) {
 // Remove any

    existing fade animation
 if (h.faderKey != null) {
 mChoreographer.remove(h.faderKey);
 }
 // Update alpha with remaining alpha
 h.itemView.setAlpha(item.remainingAlpha());
 
 // Get new fader
 Fader fader = new Fader(h.itemView, item);
 FaderKey key = FaderKey.create(h.getItemId(), item.merge());
 h.faderKey = key;
 
 // Update choreographer
 mChoreographer.put(key, fader);
 }
  18. ValueAnimator a = ValueAnimator.ofFloat(
 item.remainingAlpha(), 0.0f);
 a.setDuration(item.remainingTime());
 a.setInterpolator(new DecelerateInterpolator());
 a.addUpdateListener(


    new ValueAnimator.AnimatorUpdateListener() {
 @Override
 public void onAnimationUpdate(ValueAnimator anim) {
 float val = (float) anim.getAnimatedValue();
 item.remainingTime(
 (long) (val * CHAT_ALPHA_DURATION));
 item.remainingAlpha(val);
 itemView.setAlpha(val);
 }
 }); Fader.java