Android 5.0 Lollipop中新的Activity过渡效果介绍

        一、概述

           Material Design 是Android5.0新出来的特征,相信大家都不陌生,下面就为大家讲讲Acivity切换的过渡效果。

     二、先决条件

          需要使用Android Studio开发工具  API 21中引入的新的RecyclerView控件       

展示item列表,main activity会使用Android Lollipop中引入的RecyclerView控件。 你需要做的第一件事,添加下面一行代码到你项目中build.grade文件的dependencies部分里,使得项目能向下兼容        compile'com.android.support:recyclerview-v7:+'

为了尽可能的简洁代码,我们不会为程序定义数据库或类似的数据源。 相反,我们会使用一个自定义类,Contact。 每一个item会有一个name、color和基本联系信息。 以下是Contact类的大体实现。

// The fields associated to the person
private final String mName, mPhone, mEmail, mCity, mColor;

Contact(String name, String color, String phone, String email, String city) {
    mName = name; mColor = color; mPhone = phone; mEmail = email; mCity = city;
}
// This method allows to get the item associated to a particular id,
// uniquely generated by the method getId defined below
public static Contact getItem(int id) {
    for (Contact item : CONTACTS) {
        if (item.getId() == id) {
            return item;
        }
    }
    return null;
}
// since mName and mPhone combined are surely unique,
// we don't need to add another id field
public int getId() {
    return mName.hashCode() + mPhone.hashCode();
}

public static enum Field {
    NAME, COLOR, PHONE, EMAIL, CITY
}
public String get(Field f) {
    switch (f) {
        case COLOR: return mColor;
        case PHONE: return mPhone;
        case EMAIL: return mEmail;
        case CITY: return mCity;
        case NAME: default: return mName;
    }
}
最终你会得到一个能够很好地承载你所关注信息的类。 但是我们需要填充一些数据。 在Contact类的起始位置,添加下面的代码以填充数据集。
定义数据为publicstatic,项目中每个类都可以读取该数据。 在现在的情况下,我们通过写进类中的代码来模拟从数据库获取数据的行为。 

 
 
public static final Contact[] CONTACTS = new Contact[] {
     new Contact( "John" , "#33b5e5" , "+01 123456789" , "john@example.com" , "Venice" ),
     new Contact( "Valter" , "#ffbb33" , "+01 987654321" , "valter@example.com" , "Bologna" ),
     new Contact( "Eadwine" , "#ff4444" , "+01 123456789" , "eadwin@example.com" , "Verona" ),
     new Contact( "Teddy" , "#99cc00" , "+01 987654321" , "teddy@example.com" , "Rome" ),
     new Contact( "Ives" , "#33b5e5" , "+01 11235813" , "ives@example.com" , "Milan" ),
     new Contact( "Alajos" , "#ffbb33" , "+01 123456789" , "alajos@example.com" , "Bologna" ),
     new Contact( "Gianluca" , "#ff4444" , "+01 11235813" , "me@gian.lu" , "Padova" ),
     new Contact( "Fane" , "#99cc00" , "+01 987654321" , "fane@example.com" , "Venice" ),
};

main activity的布局文件很简单,因为会把list列表数据填充满整个屏幕。 布局layout文件包含一个RelativeLayout相对布局作为根布局——使用LinearLayout一样可行——并且添加一个RecyclerView作为其唯一子布局。

< RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
     android:layout_width = "match_parent"
     android:layout_height = "match_parent"
     android:background = "#f5f5f5" >
 
     < android.support.v7.widget.RecyclerView
         android:layout_width = "match_parent"
         android:layout_height = "match_parent"
         android:id = "@+id/rv" />
 
</ RelativeLayout >
因为 RecyclerView 只管理其子元素,你则需要一个布局文件来定义item的布局。 我们想要contact联系人列表的item布局的左边是一个有色彩的圆,所以你需要先在drawable目录下定义 circle.xml
< shape
     xmlns:android = "http://schemas.android.com/apk/res/android"
     android:shape = "oval" >
     < solid
         android:color = "#000" />
     < size
         android:width = "32dp"
         android:height = "32dp" />
</ shape >
你现在已经集齐所有的元素,可以定义item的布局了。
< RelativeLayout xmlns:android = "http://schemas.android.com/apk/res/android"
     android:layout_width = "match_parent"
     android:layout_height = "82dp"
     android:padding = "@dimen/activity_horizontal_margin"
     android:background = "?android:selectableItemBackground"
     android:clickable = "true"
     android:focusable = "true"
     android:orientation = "vertical" >
     < View
         android:id = "@+id/CONTACT_circle"
         android:layout_width = "40dp"
         android:layout_height = "40dp"
         android:background = "@drawable/circle"
         android:layout_centerVertical = "true"
         android:layout_alignParentLeft = "true" />
     < LinearLayout
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:layout_centerVertical = "true"
         android:layout_toRightOf = "@+id/CONTACT_circle"
         android:layout_marginLeft = "@dimen/activity_horizontal_margin"
         android:orientation = "vertical" >
         < TextView
             android:id = "@+id/CONTACT_name"
             android:layout_width = "wrap_content"
             android:layout_height = "wrap_content"
             android:text = "Jonh Doe"
             android:textColor = "#000"
             android:textSize = "18sp" />
 
         < TextView
             android:id = "@+id/CONTACT_phone"
             android:layout_width = "wrap_content"
             android:layout_height = "wrap_content"
             android:text = "+01 123456789"
             android:textColor = "#9f9f9f"
             android:textSize = "15sp" />
 
     </ LinearLayout >
 
</ RelativeLayout >

我们就快完成本教程的第一部分了。 你还需要重写RecyclerView.ViewHolderRecyclerView.Adapter,并在main activity的onCreate方法中为view做相应绑定。 在此,RecyclerView.ViewHolder实现类需要能够处理点击事件,所以你需要添加一个点击事件的相应实现类。 下面让我们开始定义处理点击事件的实现类吧。

public class RecyclerClickListener implements RecyclerView.OnItemTouchListener {
 
     private OnItemClickListener mListener;
     GestureDetector mGestureDetector;
 
     public interface OnItemClickListener {
         public void onItemClick(View view, int position);
     }
 
     public RecyclerClickListener(Context context, OnItemClickListener listener) {
         mListener = listener;
         mGestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() {
             @Override public boolean onSingleTapUp(MotionEvent e) {
                 return true ;
             }
         });
     }
 
     @Override public boolean onInterceptTouchEvent(RecyclerView view, MotionEvent e) {
         View childView = view.findChildViewUnder(e.getX(), e.getY());
         if (childView != null && mListener != null && mGestureDetector.onTouchEvent(e)) {
             mListener.onItemClick(childView, view.getChildPosition(childView));
             return true ;
         }
         return false ;
     }
 
     @Override public void onTouchEvent(RecyclerView view, MotionEvent motionEvent) { }
 
}
RecyclerView.Adapter 实现类需要做详细的解析,这个类里我称之为 DataManager 。 这个类负责加载数据并将数据添加到视图列表里的视图中。 DataManager类也会包含 RecyclerView.ViewHolder 的属性。
public class DataManager extends RecyclerView.Adapter<DataManager.RecyclerViewHolder> {
 
     public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
 
         TextView mName, mPhone;
         View mCircle;
 
         RecyclerViewHolder(View itemView) {
             super (itemView);
             mName = (TextView) itemView.findViewById(R.id.CONTACT_name);
             mPhone = (TextView) itemView.findViewById(R.id.CONTACT_phone);
             mCircle = itemView.findViewById(R.id.CONTACT_circle);
         }
     }
 
     @Override
     public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
         View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.contact_item, viewGroup, false );
         return new RecyclerViewHolder(v);
     }
 
     @Override
     public void onBindViewHolder(RecyclerViewHolder viewHolder, int i) {
         // get the single element from the main array
         final Contact contact = Contact.CONTACTS[i];
         // Set the values
         viewHolder.mName.setText(contact.get(Contact.Field.NAME));
         viewHolder.mPhone.setText(contact.get(Contact.Field.PHONE));
         // Set the color of the shape
         GradientDrawable bgShape = (GradientDrawable) viewHolder.mCircle.getBackground();
         bgShape.setColor(Color.parseColor(contact.get(Contact.Field.COLOR)));
     }
 
     @Override
     public int getItemCount() {
         return Contact.CONTACTS.length;
     }
}
最后,在onCreate方法中的setContentView代码后添加以下代码。 至此,main activity就基本完成,可以展示列表数据了。
RecyclerView rv = (RecyclerView) findViewById(R.id.rv); // layout reference
 
LinearLayoutManager llm = new LinearLayoutManager( this );
rv.setLayoutManager(llm);
rv.setHasFixedSize( true ); // to improve performance
 
rv.setAdapter( new DataManager()); // the data manager is assigner to the RV
rv.addOnItemTouchListener( // and the click is handled
     new RecyclerClickListener( this , new RecyclerClickListener.OnItemClickListener() {
         @Override public void onItemClick(View view, int position) {
             // STUB:
             // The click on the item must be handled
         }
     }));
编译后运行程序效果见下图。

第二个activity就简单多了。 通过contact联系人列表选中的ID来重新获取第一个activity没有展示的额外信息。

从view设计的角度来看,这个activity的布局很重要,因为它是应用程序中最重要的部分。 但是XML文件中要关注的并不太重要。 布局文件是一系列以合适方式排布的TextView实例,同时使用了RelativeLayoutLinearLayout。 以下是布局文件代码:

< LinearLayout 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"
     android:orientation = "vertical" >
     
     < ImageView
         android:layout_width = "match_parent"
         android:layout_height = "200dp"
         android:scaleType = "centerCrop"
         android:src = "@mipmap/material_wallpaper" />
 
     < RelativeLayout
         android:layout_width = "match_parent"
         android:layout_height = "82dp"
         android:padding = "@dimen/activity_vertical_margin" >
 
         < View
             android:id = "@+id/DETAILS_circle"
             android:layout_width = "48dp"
             android:layout_height = "48dp"
             android:background = "@drawable/circle"
             android:layout_centerVertical = "true"
             android:layout_alignParentLeft = "true" />
 
         < TextView
             android:id = "@+id/DETAILS_name"
             android:layout_width = "wrap_content"
             android:layout_height = "wrap_content"
             android:text = "Jonh Doe"
             android:layout_toRightOf = "@+id/DETAILS_circle"
             android:layout_marginLeft = "@dimen/activity_horizontal_margin"
             android:layout_centerVertical = "true"
             android:textColor = "#000"
             android:textSize = "25sp" />
 
     </ RelativeLayout >
 
     < LinearLayout
         android:layout_width = "wrap_content"
         android:layout_height = "wrap_content"
         android:layout_centerVertical = "true"
         android:padding = "@dimen/activity_horizontal_margin"
         android:orientation = "vertical" >
         
         < RelativeLayout
             android:layout_width = "match_parent"
             android:layout_height = "wrap_content" >
 
             < TextView
                 android:id = "@+id/DETAILS_phone_label"
                 android:layout_width = "wrap_content"
                 android:layout_height = "wrap_content"
                 android:text = "Phone:"
                 android:textColor = "#000"
                 android:textSize = "20sp" />
             
             < TextView
                 android:id = "@+id/DETAILS_phone"
                 android:layout_width = "wrap_content"
                 android:layout_height = "wrap_content"
                 android:layout_toRightOf = "@+id/DETAILS_phone_label"
                 android:layout_marginLeft = "@dimen/activity_horizontal_margin"
                 android:text = "+01 123456789"
                 android:textColor = "#9f9f9f"
                 android:textSize = "20sp" />
             
         </ RelativeLayout >
 
         < RelativeLayout
             android:layout_width = "match_parent"
             android:layout_height = "wrap_content"
             android:layout_marginTop = "@dimen/activity_vertical_margin" >
 
             < TextView
                 android:id = "@+id/DETAILS_email_label"
                 android:layout_width = "wrap_content"
                 android:layout_height = "wrap_content"
                 android:text = "Email:"
                 android:textColor = "#000"
                 android:textSize = "20sp" />
 
             < TextView
                 android:id = "@+id/DETAILS_email"
                 android:layout_width = "wrap_content"
                 android:layout_height = "wrap_content"
                 android:layout_toRightOf = "@+id/DETAILS_email_label"
                 android:layout_marginLeft = "@dimen/activity_horizontal_margin"
                 android:text = "jonh.doe@example.com"
                 android:textColor = "#9f9f9f"
                 android:textSize = "20sp" />
 
         </ RelativeLayout >
 
         < RelativeLayout
             android:layout_width = "match_parent"
             android:layout_height = "wrap_content"
             android:layout_marginTop = "@dimen/activity_vertical_margin" >
 
             < TextView
                 android:id = "@+id/DETAILS_city_label"
                 android:layout_width = "wrap_content"
                 android:layout_height = "wrap_content"
                 android:text = "City:"
                 android:textColor = "#000"
                 android:textSize = "20sp" />
 
             < TextView
                 android:id = "@+id/DETAILS_city"
                 android:layout_width = "wrap_content"
                 android:layout_height = "wrap_content"
                 android:layout_toRightOf = "@+id/DETAILS_city_label"
                 android:layout_marginLeft = "@dimen/activity_horizontal_margin"
                 android:text = "Rome"
                 android:textColor = "#9f9f9f"
                 android:textSize = "20sp" />
 
         </ RelativeLayout >
     </ LinearLayout >
</ LinearLayout >

因为两个activity是通过intent连接的,你需要发送相应的信息给第二个activity来使其理解你需要显示哪个contact联系人的详情。

可以选择变量的位置作为引用。 item在视图列表中的位置和在数组中的位置是一致的,所以使用位置这个数字作为唯一的标识是可以的。

以上方法是可行的,但是如果你采用这种方式,出于某些原因,数据集在运行时是动态变化的,这时位置的引用关系就不会取到你想要的数据。 这就是为什么使用ID效果更好。 以下是Contact类中定义的getId方法。

按照下面的方法编辑onItemClick方法来处理列表项的点击事件。

@Override public void onItemClick(View view, int position) {
     Intent intent = new Intent(MainActivity. this , DetailsActivity. class );
     intent.putExtra(DetailsActivity.ID, Contact.CONTACTS[position].getId());
     startActivity(intent);
}
DetailsActivity 会通过 Intent 的extras属性收到信息,并且通过ID引用获得正确的数据来构造正确的对象。 详情见以下代码。

和之前RecyclerView中的onCreateViewHolder方法一样,这个view使用findViewById方法找到控件并使用setText方法填充数据。 如下,我们通过下面的方法来给展示姓名的TextView填充数据。

我们终于到了本教程的核心了,使用Lollipop中最新的共享元素过渡动画作为两个activity的过渡动画。 

首先你要做的是编辑values-v21目录下的style.xml文件。 通过这样的方法,你可以设置过渡动画的内容,并设置两个activity不共享view的进入和消失的方式。

请注意这时你的项目的最高支持版本(同时编译项目使用的最低版本)至少为Android API 21

这个动画在低于Lollipop版本的系统中会被忽略从而不执行。 不幸的是,由于性能上的原因,AppCompat兼容包没有为这些动画提供完全的向下兼容。

当你编辑了style.xml文件后,你需要指明视图间两个共享元素之间的关联。

在此示例中,共享的视图元素是包括contact联系人姓名、一个电话号码和一个彩色圆形的控件。 视图中的每个元素,你都要为其指定一个共同的变换名称。 出于这个原因,我们在再strings.xml资源文件中加入下列元素。

然后,在layout文件的三个元素中添加android:transitionName属性并赋予相对应的值。 下面是彩色圆元素的代码示例:

正是这个属性,Android系统会知道两个activity的视图间哪个view为共享元素并且在过渡中添加正确的动画。 另外两个view采取同样的处理。

从view视图的编码角度看,你需要在intent中绑定特定ActivityOptions对象。 你所需的方法是makeSceneTransitionAnimation,这个方法接受的参数为context上下文对象和共享元素数组。 在RecyclerViewonItemClick方法中,将之前的Intent修改为下面的实现:

@Override public void onItemClick(View view, int position) {
     Intent intent = new Intent(MainActivity. this , DetailsActivity. class );
     intent.putExtra(DetailsActivity.ID, Contact.CONTACTS[position].getId());
 
     ActivityOptionsCompat options = ActivityOptionsCompat.makeSceneTransitionAnimation(
             // the context of the activity
             MainActivity. this ,
 
             // For each shared element, add to this method a new Pair item,
             // which contains the reference of the view we are transitioning *from*,
             // and the value of the transitionName attribute
             new Pair<View, String>(view.findViewById(R.id.CONTACT_circle),
                     getString(R.string.transition_name_circle)),
             new Pair<View, String>(view.findViewById(R.id.CONTACT_name),
                     getString(R.string.transition_name_name)),
             new Pair<View, String>(view.findViewById(R.id.CONTACT_phone),
                     getString(R.string.transition_name_phone))
     );
     ActivityCompat.startActivity(MainActivity. this , intent, options.toBundle());
}

实现每个共享元素的动画,你需要在makeSceneTransitionAnimation方法中添加一个新的Pair对象。 每个Pair对象包含两个值,第一个值是动画起始view对象的引用,第二个值是该view对象transitionName属性的

导入Pair类的时候需要注意下。 你需要导入android.support.v4.util包下的Pair类,而不是android.util包下的Pair类。 同时,谨记使用ActivityCompat.startActivity方法而不是startActivity方法,否则,你的程序在API低于16的系统中无法运行。

完结。 撒花。 就是这么简单。

在本教程中,你学到了如何给共用一个或更多的元素的两个activity添加漂亮无缝的过渡动画,获得视觉上愉悦的同时展示了富有含义的持续动画。

你通过制作两个activity中第一个展示contact联系人列表的activity开始了本教程。 随后你又完成了第二个activity,设计它的布局,并且想办法实现了在两个activity间传递一个唯一引用。 最终,你了解了makeSceneTransitionAnimation方法的运作方式,XML文件中的transitionName属性简直是神来之笔。

注意:最后附上的项目是将style里面的样式注释掉了,如果有需要的可以放开,笔者觉得添加不是很漂亮!

源码地址:http://download.csdn.net/detail/plxiaopan/9436002

 

注意,切换方法overridePendingTransition只能在startActivity和finish方法之后调用。 第一个参数为第一个Activity离开时的动画,第二参数为所进入的Activity动画效果 淡入淡出效果 overridePendingTransition(R.anim.fade, R.anim.hold); 放大淡出效果 overridePendingTransition(R.anim.my_scale_action,R.anim.my_alpha_action); 转动淡出效果 overridePendingTransition(R.anim.scale_rotate,R.anim.my_alpha_action); 转动淡出效果 overridePendingTransition(R.anim.scale_translate_rotate,R.anim.my_alpha_action); 左上角展开淡出效果 overridePendingTransition(R.anim.scale_translate,R.anim.my_alpha_action); 压缩变小淡出效果 overridePendingTransition(R.anim.hyperspace_in,R.anim.hyperspace_out); 右往左推出效果 overridePendingTransition(R.anim.push_left_in,R.anim.push_left_out); 下往上推出效果 overridePendingTransition(R.anim.push_up_in,R.anim.push_up_out); 左右交错效果 overridePendingTransition(R.anim.slide_left,R.anim.slide_right); 放大淡出效果 overridePendingTransition(R.anim.wave_scale,R.anim.my_alpha_action); 缩小效果 overridePendingTransition(R.anim.zoom_enter,R.anim.zoom_exit); 上下交错效果 overridePendingTransition(R.anim.slide_up_in,R.anim.slide_down_out); Activity切换动画指的是从一个activity跳转到另外一个activity时的动画。 {它包括两个部分: 一部分是第一个activity退出时的动画; 另外一部分时第二个activity进入时的动画; 在Android的2.0版本之后,有了一个函数来帮我们实现这个动画。这个函数就是overridePendingTransition j这个函数有两个参数,一个参数是第一个activity退出时的动画,另外一个参数则是第二个activity进入时的动画
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值