实现Instagram的Material Design设计(2)- 评论窗口实现

实现Instagram的Material Design设计系列第一篇http://blog.csdn.net/tongsdroid/article/details/51567583

这篇文章是一个实现Instagram的Material Design设计系列的第二篇文章,今天,我们将实现主页和评论活动之间的过渡(在概念录像显示为9秒之间13)。我们将跳过按钮效果(涟漪,发送完成动画等),只关注发表评论的Acitvity进入和退出动画。

这是在今天的文章中描述(适用于Android5.0和之前的版本)的最终效果:

初始化配置

首先添加需要用到的开源库和一些重要的信息到之前已有的工程。我们需要添加:

  • Picasso 用于异步的图片加载(在评论列表中使用,用于加载用户头像)
  • AndroidMainfest.xml中声明带有主题的CommentsActivity.

此外,我们还应该为CommentsActivity创建布局。一切几乎就像·MainAcitvity·相同,除了添加评论的底部组件。我们再一次使用ToolbarRecyclerView和一些额外的素材。一切都很简单,没什么特别要说的。

activity_comments.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".CommentsActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        android:elevation="@dimen/default_elevation">

        <ImageView
            android:id="@+id/ivLogo"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center"
            android:scaleType="center"
            android:src="@drawable/img_toolbar_logo" />
    </android.support.v7.widget.Toolbar>

    <LinearLayout
        android:id="@+id/contentRoot"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/toolbar"
        android:background="@color/bg_comments"
        android:elevation="@dimen/default_elevation"
        android:orientation="vertical">

        <android.support.v7.widget.RecyclerView
            android:id="@+id/rvComments"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:scrollbars="none" />

        <LinearLayout
            android:id="@+id/llAddComment"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/bg_comments"
            android:elevation="@dimen/default_elevation">

            <EditText
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1" />

            <Button
                android:id="@+id/btnSendComment"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Send" />
        </LinearLayout>
    </LinearLayout>
</RelativeLayout>

接下来,创建评论列表项的布局:

item_comment.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:paddingBottom="8dp"
        android:paddingTop="8dp">

        <ImageView
            android:id="@+id/ivUserAvatar"
            android:layout_width="@dimen/comment_avatar_size"
            android:layout_height="@dimen/comment_avatar_size"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:background="@drawable/bg_comment_avatar" />

        <TextView
            android:id="@+id/tvComment"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_gravity="center_vertical"
            android:layout_marginRight="16dp"
            android:layout_weight="1"
            android:text="Lorem ipsum dolor sit amet" />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:layout_gravity="bottom"
        android:layout_marginLeft="88dp"
        android:background="#cccccc" />
</FrameLayout>

评论中的圆形头像代码片段:

bg_comment_avatar.xml

<?xml version="1.0" encoding="utf-8"?>
<!--drawable/bg_comment_avar.xml-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">
    <solid android:color="#999999" />
</shape>

接下来,让我们来处理feed item底部的点击进入CommentActivity界面的onClick事件吧。现在,我们使用整个的feed item的底部,之后会替换成真正的按钮,在这一步,我们要在RecyclerView的适配器中给feed item底部添加onClickListener,并且我们会创建简单的接口开将MainActivity和我们的适配器整合在一起,那么应该如何实现呢?

FeedAdapter.java(仅包含改变的部分)

//.. implements View.OnClickListener
public class FeedAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements View.OnClickListener {

    private OnFeedItemClickListener onFeedItemClickListener;

    //..

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        //...
        holder.ivFeedBottom.setOnClickListener(this);
        holder.ivFeedBottom.setTag(position);
    }

    //..

    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.ivFeedBottom) {
            if (onFeedItemClickListener != null) {
                onFeedItemClickListener.onCommentsClick(v, (Integer) v.getTag());
            }
        }
    }

    public void setOnFeedItemClickListener(OnFeedItemClickListener onFeedItemClickListener) {
        this.onFeedItemClickListener = onFeedItemClickListener;
    }

    //..

    public interface OnFeedItemClickListener {
        public void onCommentsClick(View v, int position);
    }
}

MainActivity.java(仅包含改变部分)

//... implements FeedAdapter.OnFeedItemClickListener
public class MainActivity extends ActionBarActivity implements FeedAdapter.OnFeedItemClickListener {

    //...

    private void setupFeed() {
        //...
        feedAdapter.setOnFeedItemClickListener(this);
        //...
    }

    //...

    @Override
    public void onCommentsClick(View v, int position) {
    }
}

为防止我们遗漏什么东西,这里是本篇的全部代码onClick on item in RecyclerView adapter

。。。这就是所有初始化的配置了。

CommentActivity的跳转

基于概念视频,下面是我们希望实现的效果:

  • 静态的ToolBar :新的Activity打开时ToolBar不会移动,(我们用代码控制骗过用户,让用户以为他还在同一个窗口中)
  • 评论窗口应该从用户点击的位置扩展开来(不管用户从哪里点击)
  • 评论列表的item应该在扩展动画结束后一条接一条的显示

静态的ToolBar

这是本篇文章最简单的部分,因为MainActivity和CommentsActivity的`TollBar`是一样的。所以我们要做的就是禁用`Activity`之间跳转的默认动画。这样看起来就好像是静态的ToolBar的效果,代码如下:

public class MainActivity extends ActionBarActivity implements FeedAdapter.OnFeedItemClickListener {
    //...
    @Override
    public void onCommentsClick(View v, int position) {
        final Intent intent = new Intent(this, CommentsActivity.class);
        startActivity(intent);
        //Disable enter transition for new Acitvity
        overridePendingTransition(0, 0);
    }
}

通过调用overridePendingTransition(0, 0);我们可以禁用退出动画和进入动画。

从点击的位置扩展CommentsActivity

现在我们创建扩展动画(可以从屏幕任何地方开始)。包含两部分:

  • 扩展的背景
  • 显示评论列表

在我们编写动画代码之前,我们必须先使CommentsActivity半透明。否则扩展动画的背景将会是窗体的背景色,而不是MainActivity的背景色。这是因为每一个activity都有windowBackground属性,这个属性定义在当前使用的主题中。如果我们想禁用它是我们的Activity半透明,我们必须修改style.xml文件。代码如下:

style.xml

<?xml version="1.0" encoding="utf-8"?>
<!-- styles.xml-->
<resources>

    <!--...-->

    <style name="AppTheme.CommentsActivity" parent="AppTheme">
        <item name="android:windowBackground">@android:color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
    </style>
</resources>

两者的区别:

现在我们可以创建扩展的效果了,首先我们要获取到动画开始的Y坐标,在我们的例子中我们没必要知道点击的具体位置(动画速度非常快,用户不会注意到动画开始的位置不是点击的位置)。我们可以将点击位置的Y坐标传递给CommentsActivity:

public class MainActivity extends ActionBarActivity implements FeedAdapter.OnFeedItemClickListener {

    //...

    @Override
    public void onCommentsClick(View v, int position) {
        final Intent intent = new Intent(this, CommentsActivity.class);

        //Get location on screen for tapped view
        int[] startingLocation = new int[2];
        v.getLocationOnScreen(startingLocation);
        intent.putExtra(CommentsActivity.ARG_DRAWING_START_LOCATION, startingLocation[1]);

        startActivity(intent);
        overridePendingTransition(0, 0);
    }
}

接下来,在CommentsActivity中实现背景扩展动画。我们可以使用简单的ScaleAnimation(这时候没有任何可见的内容,因此没人知道我们是拉伸背景而不是扩展它)。

不要忘记使用setPivoY()方法设置正确的初始位置。

public class CommentsActivity extends ActionBarActivity {
    public static final String ARG_DRAWING_START_LOCATION = "arg_drawing_start_location";

    @InjectView(R.id.toolbar)
    Toolbar toolbar;
    @InjectView(R.id.contentRoot)
    LinearLayout contentRoot;
    @InjectView(R.id.rvComments)
    RecyclerView rvComments;
    @InjectView(R.id.llAddComment)
    LinearLayout llAddComment;

    private CommentsAdapter commentsAdapter;
    private int drawingStartLocation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //...

        drawingStartLocation = getIntent().getIntExtra(ARG_DRAWING_START_LOCATION, 0);
        if (savedInstanceState == null) {
            contentRoot.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    contentRoot.getViewTreeObserver().removeOnPreDrawListener(this);
                    startIntroAnimation();
                    return true;
                }
            });
        }
    }

    //...

    private void startIntroAnimation() {
        contentRoot.setScaleY(0.1f);
        contentRoot.setPivotY(drawingStartLocation);
        llAddComment.setTranslationY(100);

        contentRoot.animate()
                .scaleY(1)
                .setDuration(200)
                .setInterpolator(new AccelerateInterpolator())
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        animateContent();
                    }
                })
                .start();
    }

    private void animateContent() {
        commentsAdapter.updateItems();
        llAddComment.animate().translationY(0)
                .setInterpolator(new DecelerateInterpolator())
                .setDuration(200)
                .start();
    }

    //...

}

就像上面展示的动画只执行一次,在我们代开CommentsActivity之后,

多亏了onPreDrawListener ,我们才可以在view树完成测量并且分配空间而绘制过程还没有开始的时候播放动画。

在以上的代码中我们实现了背景的扩展和评论列表的显示,效果如下:

好像还遗漏了什么东西似的,不是吗?

现在我们必须为评论列表中的每个item创建动画,这也非常简单,但是我们必须在心中记下这些重要的事情:

  • 每一个item的动画应该延迟一点点,否则所有的动画将会在同一时刻执行,用户会看到items一起显示而不是一条一条的显示。
  • Adapter应该具备封锁动画的能力,因为我们不想再用户滑动list的时候还在执行动画。
  • 还有,我们必须提供方法用于暂时的解锁并执行动画(当添加新的评论时)

现在CommentsAdapter.java应该是这个样子

public class CommentsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

    private Context context;
    private int itemsCount = 0;
    private int lastAnimatedPosition = -1;
    private int avatarSize;

    private boolean animationsLocked = false;
    private boolean delayEnterAnimation = true;

    public CommentsAdapter(Context context) {
        this.context = context;
        avatarSize = context.getResources().getDimensionPixelSize(R.dimen.btn_fab_size);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        final View view = LayoutInflater.from(context).inflate(R.layout.item_comment, parent, false);
        return new CommentViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
        runEnterAnimation(viewHolder.itemView, position);
        CommentViewHolder holder = (CommentViewHolder) viewHolder;
        switch (position % 3) {
            case 0:
                holder.tvComment.setText("Lorem ipsum dolor sit amet, consectetur adipisicing elit.");
                break;
            case 1:
                holder.tvComment.setText("Cupcake ipsum dolor sit amet bear claw.");
                break;
            case 2:
                holder.tvComment.setText("Cupcake ipsum dolor sit. Amet gingerbread cupcake. Gummies ice cream dessert icing marzipan apple pie dessert sugar plum.");
                break;
        }

        Picasso.with(context)
                .load(R.drawable.ic_launcher)
                .centerCrop()
                .resize(avatarSize, avatarSize)
                .transform(new RoundedTransformation())
                .into(holder.ivUserAvatar);
    }

    private void runEnterAnimation(View view, int position) {
        if (animationsLocked) return;

        if (position > lastAnimatedPosition) {
            lastAnimatedPosition = position;
            view.setTranslationY(100);
            view.setAlpha(0.f);
            view.animate()
                    .translationY(0).alpha(1.f)
                    .setStartDelay(delayEnterAnimation ? 20 * (position) : 0)
                    .setInterpolator(new DecelerateInterpolator(2.f))
                    .setDuration(300)
                    .setListener(new AnimatorListenerAdapter() {
                        @Override
                        public void onAnimationEnd(Animator animation) {
                            animationsLocked = true;
                        }
                    })
                    .start();
        }
    }

    @Override
    public int getItemCount() {
        return itemsCount;
    }

    public void updateItems() {
        itemsCount = 10;
        notifyDataSetChanged();
    }

    public void addItem() {
        itemsCount++;
        notifyItemInserted(itemsCount - 1);
    }

    public void setAnimationsLocked(boolean animationsLocked) {
        this.animationsLocked = animationsLocked;
    }

    public void setDelayEnterAnimation(boolean delayEnterAnimation) {
        this.delayEnterAnimation = delayEnterAnimation;
    }

    public static class CommentViewHolder extends RecyclerView.ViewHolder {
        @InjectView(R.id.ivUserAvatar)
        ImageView ivUserAvatar;
        @InjectView(R.id.tvComment)
        TextView tvComment;

        public CommentViewHolder(View view) {
            super(view);
            ButterKnife.inject(this, view);
        }
    }
}

为了显示头像,我们使用Picasso库和CircleTransformation.,感谢RecyclerView和它的适配器,我们可以使用notifyItemInserted()方法,他会为新添加的item执行默认的动画。剩下的代码非常简单。

CommentsActivity中的使用方法:

public class CommentsActivity extends ActionBarActivity {

    //...

    private void setupComments() {

        //...

        rvComments.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
                    commentsAdapter.setAnimationsLocked(true);
                }
            }
        });
    }

    @OnClick(R.id.btnSendComment)
    public void onSendCommentClick() {
        commentsAdapter.addItem();
        commentsAdapter.setAnimationsLocked(false);
        commentsAdapter.setDelayEnterAnimation(false);
        rvComments.smoothScrollBy(0, rvComments.getChildAt(0).getHeight() * commentsAdapter.getItemCount());
    }
}

items动画被封锁当用户开始滑动RecyclerView的时候,并且在添加item的时候短暂接触封锁。

进入的跳转到这里结束了。

退出跳转

最后一件事,我们还要实现退出的跳转,基于概念视频,没什么特别需要做的,我们必须创建滑出当前界面的跳转动画。我们还得记住我们不能移除ToolBar,这就是为什么我们再次使用

overridePendingTransition(0, 0);的缘故了。

public class CommentsActivity extends ActionBarActivity {

    //...

    @Override
    public void onBackPressed() {
        contentRoot.animate()
                .translationY(Utils.getScreenHeight(this))
                .setDuration(200)
                .setListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        CommentsActivity.super.onBackPressed();
                        overridePendingTransition(0, 0);
                    }
                })
                .start();
    }

}

OK,这篇文章到这里就结束了,我们完成了Material Design风格的Instagram的第二步,下一篇文章我想讲解我们现在遗漏的细节处理。

源代码

repository

作者:Miroslaw Stanek

作者Twitter:https://twitter.com/froger_mcs

原文链接:http://frogermcs.github.io/Instagram-with-Material-Design-concept-part-2-Comments-transition/

时间: 2024-12-25 08:12:41

实现Instagram的Material Design设计(2)- 评论窗口实现的相关文章

实现Instagram的Material Design概念设计

几个月前(这篇文章的日期是2014 年11月10日),google发布了app和web应用的Material Design设计准则之后,设计师Emmanuel Pacamalan在youtube上发布了一则概念视频,演示了Instagram如果做成Material风格会是什么样子: 视频地址 http://v.youku.com/v_show/id_XODg2NDQ1NDQ4.html 这仅仅是停留在图像上的设计,是美好的愿景,估计很多人都会问,能否使用相对简单的办法将它实现出来呢?答案是:ye

Instagram的Material Design概念设计文章分享

近期開始研究最新的Android 5 Material Design,一加氢OS公布后,非常快就有一大批支持Android5原生风格的手机出来了,你的App还是UI帮设计的吗?该考虑升级到 Material Design风格了, 这年头要与时俱进啊,不学习就out了. 这个系列的文章不错,给大家共享下 , 源代码Git地址  https://github.com/frogermcs/InstaMaterial InstaMaterial 概念设计(第九部分) - 图片的公布2015-05-09

Android Material Design-Creating Apps with Material Design(用 Material Design设计App)-(零)

转载请注明出处:http://blog.csdn.net/bbld_/article/details/40400031 翻译自:http://developer.android.com/training/material/index.html 前言 这篇文章是官方material design文档翻译的第一篇.关于material design须要了解的知识能够參阅这本中文版的译文电子书. Material design是一种跨平台的为了视觉.动作.交互设计的综合指南.要在你的Android应用

Material Design设计的开源代码

https://github.com/telly/FloatingAction http://www.csdn.net/article/2014-11-21/2822753-material-design-libs/1 android 属性动画的开源代码: http://www.jcodecraeer.com/a/opensource/2014/1012/1746.html

【Material Design视觉设计语言】应用样式设计

作者:郭孝星 微博:郭孝星的新浪微博 邮箱:[email protected] 博客:http://blog.csdn.net/allenwells Github:https://github.com/AllenWells [Material Design视觉设计语言]章节列表 [Material Design视觉设计语言]开篇 [Material Design视觉设计语言]Material Design设计概述 [Material Design视觉设计语言]应用布局设计 [Material D

谷歌设计师的MATERIAL DESIGN实践心得

前不久,我们发布了Google I/O 2014 APP,源代码已经发布,诸位感兴趣的话,可以去Github看看我们是怎样在这个App中实现Material Design的功能和设计细节.在这篇文章中,我将分享一些我们对于Material Design的一些设计性思考. 每年Google I/O完了后,我们都会更新Google I/O相关的APP,我们做这个APP 有2个目的.第一,让那些在家看直播.甚至没有机会到现场的人更身临其境的了解Google I/O大会.第二,我们用Material D

创建Material Design风格的Android应用--使用自定义动画

动画在Material Design设计中给用户反馈放用户点击时,并且在程序用户界面中提供连贯的视觉.Material主题为按钮(Button)和activity的转换提供了一些默认的动画,在android5.0(api 21)和更高的版本,你可以自定义这些动画和创建一个新动画: Touch feedback(触摸反馈) Circular Reveal(循环揭露效果) Activity transitions(Activity转换效果) Curved motion(曲线运动) View stat

Android群英传笔记——第十二章:Android5.X 新特性详解,Material Design UI的新体验

Android群英传笔记--第十二章:Android5.X 新特性详解,Material Design UI的新体验 第十一章为什么不写,因为我很早之前就已经写过了,有需要的可以去看 Android高效率编码-第三方SDK详解系列(二)--Bmob后端云开发,实现登录注册,更改资料,修改密码,邮箱验证,上传,下载,推送消息,缩略图加载等功能 这一章很多,但是很有趣,也是这书的最后一章知识点了,我现在还在考虑要不要写这个拼图和2048的案例,在此之前,我们先来玩玩Android5.X的新特性吧!

Android控件使用 — 12个Material Design风格控件的使用

项目在GitHub上的地址: https://github.com/Hebin320/MaterialDesignUse 1.AppBarLayout.ToolBar AppBarLayout 是继承LinerLayout实现的一个ViewGroup容器组件,它是为了Material Design设计的App Bar,支持手势滑动操作. AppBarLayout必须作为Toolbar的父布局容器,也可以配合CoordinatorLayout一起使用. ToolBar是谷歌新推出的代替Action