最新写项目的时候,看到搜索界面的跳转基本都是点击搜索然后跳转到下个页面,android 微信上则是 类似toolbar的效果,而ios 上则是一个搜索框上移然后显示新界面的一个效果。仔细研究了下发现和android 的 共享元素的过渡实现 的效果很像,所以在此模仿下。但是 共享元素的过渡实现 是5.0以后才有的,兼容5.0一下需要自定义动画效果,查了些资料发现也是可以实现的。下面是效果图:


1.实现思路:

实现的思路也比较简单,大概的步骤如下:

1.确定第一个界面的共享元素,将其信息传递个第二个界面
2.第二个界面接收信息,开始的时候将界面设置为透明,并只显示共享元素。
3.将第二个界面的共享元素进行动画处理。

2.获取共享元素位置信息:

在第一个界面的xml 里面,搜索框直接用一个自定义的imageView 代替

2.1 自定义imageView

import android.content.Context;import android.util.AttributeSet;import android.widget.ImageView;/** * 自定义image,用于在4.x上实现仿5.0上分享元素的动画 * Created by lh on 2016/11/4. */public class CustomImage extends ImageView {    private int mResId;    public CustomImage(Context context) {        this(context, null, 0);    }    public CustomImage(Context context, AttributeSet attrs) {        this(context, attrs, 0);    }    public CustomImage(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        if (attrs != null) {            String namespace = "http://schemas.android.com/apk/res/android";            String attribute = "src";            mResId = attrs.getAttributeResourceValue(namespace, attribute, 0);        }    }    public int getImageId() {        return mResId;    }    @Override    public void setImageResource(int resId) {        super.setImageResource(resId);        mResId = resId;    }}

2.2 点击事件的处理

在第一个界面中,我们需要获取到共享元素的位置信息,并将其传递给下一个界面。

private void showShareAnimation(View view) {        Intent intent = new Intent(instance, SearchActivity.class);        //创建一个rect 对象来存储共享元素的位置信息        Rect rect = new Rect();        //获取元素的位置信息        view.getGlobalVisibleRect(rect);        //将位置信息附加到intent 上        intent.setSourceBounds(rect);        CustomImage customImage = (CustomImage) view;        intent.putExtra(ChooseCountry.EXTRA_SEARCH_SHAREIMAGE, customImage.getImageId());        startActivity(intent);        //用于屏蔽 activity 默认的转场动画效果        overridePendingTransition(0, 0);    }

其中,getGlobalVisibleRect() 方法的含义是,获取 可见的状态栏高度+可见的标题栏高度+Rect左上角到标题栏底部的距离,如果标题栏被隐藏了,那么可见标题栏高度为0。

接下来,就在在第二个界面接收位置信息并将该图片展示出来了。


3.模拟转场动画:

在第二个界面中,我们需要做如下的操作:

1.获取上共享元素信息。
2.计算共享元素缩放比例和位移距离。
3.调用动画,完成模拟转场效果。
4.隐藏搜索的图片,转变为可编辑的editText


/**     * 初始化场景     */    private void initial() {        // 获取上一个界面传入的信息        mRect = getIntent().getSourceBounds();        //图片资源 ID        int mRescourceId = getIntent().getExtras().getInt(ChooseCountry.EXTRA_SEARCH_SHAREIMAGE);        // 获取上一个界面中,图片的宽度和高度        mOriginWidth = mRect.right - mRect.left;        mOriginHeight = mRect.bottom - mRect.top;        // 设置 ImageView 的位置,使其和上一个界面中图片的位置重合        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mOriginWidth, mOriginHeight);        params.setMargins(mRect.left, mRect.top - getStatusBarHeight(), mRect.right, mRect.bottom);        mImageView.setLayoutParams(params);        // 设置 ImageView 的图片和缩放类型        mImageView.setImageResource(mRescourceId);        mImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);        // 根据上一个界面传入的图片资源 ID,获取图片的 Bitmap 对象。        BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(mRescourceId);        Bitmap bitmap = bitmapDrawable.getBitmap();        // 计算图片缩放比例和位移距离        getBundleInfo(bitmap);    }/**     * 计算图片缩放比例,以及位移距离     */    private void getBundleInfo(Bitmap bitmap) {        // 计算图片缩放比例,并存储在 bundle 中        if (bitmap.getWidth() >= bitmap.getHeight()) {            mScaleBundle.putFloat(SCALE_WIDTH, (float) mScreenWidth / mOriginWidth);            mScaleBundle.putFloat(SCALE_HEIGHT, (float) bitmap.getHeight() / mOriginHeight);        } else {            mScaleBundle.putFloat(SCALE_WIDTH, (float) bitmap.getWidth() / mOriginWidth);            mScaleBundle.putFloat(SCALE_HEIGHT, (float) mScreenHeight / mOriginHeight);        }        // 计算位移距离,并将数据存储到 bundle 中        mTransitionBundle.putFloat(TRANSITION_X, mScreenWidth / 2 - (mRect.left + (mRect.right - mRect.left) / 2));//        mTransitionBundle.putFloat(TRANSITION_Y, mScreenHeight / 2 - (mRect.top + (mRect.bottom - mRect.top) / 2));        mTransitionBundle.putFloat(TRANSITION_Y, -(mRect.top-getStatusBarHeight()));    }

我们要将 Rect.top 的值减去状态栏的高度,这样才是相对于屏幕的绝对位置。

入场以及退场动画

/**     * 模拟入场动画     */    private void runEnterAnim() {        mImageView.animate()                .setInterpolator(DEFAULT_INTERPOLATOR)                .setDuration(DURATION)                .scaleX(mScaleBundle.getFloat(SCALE_WIDTH))                .scaleY(mScaleBundle.getFloat(SCALE_HEIGHT))                .translationX(mTransitionBundle.getFloat(TRANSITION_X))                .translationY(mTransitionBundle.getFloat(TRANSITION_Y))                .start();        mImageView.setVisibility(View.VISIBLE);        //add 作用隐藏原来的图片,显示为可编辑的editText        mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_EDIT,DURATION);        mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_KEYBOARD,DURATION*2);    }    /**     * 模拟退场动画     */    @SuppressWarnings("NewApi")    private void runExitAnim() {        //add        searchLine.setVisibility(View.GONE);        searchTop.setVisibility(View.GONE);        mImageView.setVisibility(View.VISIBLE);        mImageView.animate()                .setInterpolator(DEFAULT_INTERPOLATOR)                .setDuration(DURATION)                .scaleX(1)                .scaleY(1)                .translationX(0)                .translationY(0)                .withEndAction(new Runnable() {                    @Override                    public void run() {                        finish();                        overridePendingTransition(0, 0);                    }                })                .start();    }private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch (msg.what){                case MESSAGE_SHOW_KEYBOARD:                    CommonUtil.showKeyboard(instance, searchEdit);                    break;                case MESSAGE_SHOW_EDIT:                    mImageView.setVisibility(View.GONE);                    searchTop.setVisibility(View.VISIBLE);                    searchLine.setVisibility(View.VISIBLE);                    searchEdit.requestFocus();                    break;            }        }    };

4.完整代码

4.1 界面一:

xml

"@+id/search_total_view"            android:layout_width="match_parent"            android:layout_height="wrap_content"            android:src="@drawable/search_totla_view"            android:scaleType="centerInside"/>

activity
该id的点击事件调用的方法如下,获取共享元素的位置信息

private void showShareAnimation(View view) {        Intent intent = new Intent(instance, SearchActivity.class);        //创建一个rect 对象来存储共享元素的位置信息        Rect rect = new Rect();        //获取元素的位置信息        view.getGlobalVisibleRect(rect);        //将位置信息附加到intent 上        intent.setSourceBounds(rect);        CustomImage customImage = (CustomImage) view;        intent.putExtra(ChooseCountry.EXTRA_SEARCH_SHAREIMAGE, customImage.getImageId());        startActivity(intent);        //用于屏蔽 activity 默认的转场动画效果        overridePendingTransition(0, 0);    }

4.2 界面二:

xml

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"              android:orientation="vertical"              android:layout_width="match_parent"              android:layout_height="match_parent"              android:fitsSystemWindows="true"              android:background="@color/common_view_bg">    <com.accounttools.app.views.customviews.CustomImage            android:id="@+id/activity_search_img"            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:scaleType="centerInside"            android:visibility="invisible"/>    <RelativeLayout            android:id="@+id/activity_search_top"            android:layout_width="match_parent"            android:layout_height="45dp"            android:background="@color/status_bar_color"            android:orientation="horizontal"            android:paddingLeft="10dp"            android:paddingRight="10dp"            android:visibility="gone">        <LinearLayout                android:id="@+id/search_top_cancel"                android:layout_width="50dp"                android:layout_height="match_parent"                android:orientation="vertical"                android:gravity="center"                android:layout_alignParentRight="true">            <TextView                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:textSize="18sp"                    android:textColor="@color/common_red"                    android:text="@string/cancel"/>        LinearLayout>        <LinearLayout                android:layout_toLeftOf="@id/search_top_cancel"                android:layout_marginRight="10dp"                android:layout_width="match_parent"                android:layout_height="28dp"                android:orientation="horizontal"                android:paddingLeft="8dp"                android:paddingRight="8dp"                android:background="@drawable/drawable_search_layout"                android:layout_centerVertical="true"                android:gravity="center_vertical">            <ImageView                    android:layout_width="wrap_content"                    android:layout_height="wrap_content"                    android:src="@drawable/search_icon"/>            <EditText                    android:id="@+id/search_content"                    android:layout_width="match_parent"                    android:layout_height="match_parent"                    android:layout_marginLeft="5dp"                    android:background="@color/transparent"                    android:textSize="15sp"                    android:hint="@string/search"                    android:textCursorDrawable="@drawable/drawable_search_cursor"/>        LinearLayout>    RelativeLayout>    <TextView            android:id="@+id/activity_search_line"            android:layout_width="match_parent"            android:layout_height="0.5dp"            android:background="@color/common_line_color"            android:visibility="gone"/>LinearLayout>

activity
handler 的作用是当第二个界面显示动画结束后,隐藏imageView,显示可编辑的editText

/** * 搜索界面 * Created by lh on 2016/11/3. */public class SearchActivity extends BaseActivity {    private static final int MESSAGE_SHOW_KEYBOARD = 1;    private static final int MESSAGE_SHOW_EDIT = 2;    public static final int DURATION = 300;    private static final AccelerateDecelerateInterpolator DEFAULT_INTERPOLATOR = new AccelerateDecelerateInterpolator();    private static final String SCALE_WIDTH = "SCALE_WIDTH";    private static final String SCALE_HEIGHT = "SCALE_HEIGHT";    private static final String TRANSITION_X = "TRANSITION_X";    private static final String TRANSITION_Y = "TRANSITION_Y";    private Activity instance = SearchActivity.this;    /**     * 存储图片缩放比例和位移距离     */    private Bundle mScaleBundle = new Bundle();    private Bundle mTransitionBundle = new Bundle();    /**     * 屏幕宽度和高度     */    private int mScreenWidth;    private int mScreenHeight;    /**     * 上一个界面图片的宽度和高度     */    private int mOriginWidth;    private int mOriginHeight;    /**     * 上一个界面图片的位置信息     */    private Rect mRect;    private CustomImage mImageView;    private EditText searchEdit;    private RelativeLayout searchTop;    private TextView searchLine;    @Override    public void onBackPressed() {        // 使用退场动画        runExitAnim();    }    @Override    protected int getLayoutResId() {        return R.layout.activity_search_layout;    }    @Override    protected void initView() {        // 获得屏幕尺寸        getScreenSize();        // 初始化界面        mImageView = (CustomImage) findViewById(R.id.activity_search_img);        searchEdit = (EditText)findViewById(R.id.search_content);        searchTop = (RelativeLayout)findViewById(R.id.activity_search_top);        searchLine = (TextView)findViewById(R.id.activity_search_line);        // 初始化场景        initial();        // 设置入场动画        runEnterAnim();        //动态显示搜索结果        showSearchResult();    }    private Handler mHandler = new Handler(){        @Override        public void handleMessage(Message msg) {            switch (msg.what){                case MESSAGE_SHOW_KEYBOARD:                    CommonUtil.showKeyboard(instance, searchEdit);                    break;                case MESSAGE_SHOW_EDIT:                    mImageView.setVisibility(View.GONE);                    searchTop.setVisibility(View.VISIBLE);                    searchLine.setVisibility(View.VISIBLE);                    searchEdit.requestFocus();                    break;            }        }    };    /**     * 初始化场景     */    private void initial() {        // 获取上一个界面传入的信息        mRect = getIntent().getSourceBounds();        //图片资源 ID        int mRescourceId = getIntent().getExtras().getInt(ChooseCountry.EXTRA_SEARCH_SHAREIMAGE);        // 获取上一个界面中,图片的宽度和高度        mOriginWidth = mRect.right - mRect.left;        mOriginHeight = mRect.bottom - mRect.top;        // 设置 ImageView 的位置,使其和上一个界面中图片的位置重合        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(mOriginWidth, mOriginHeight);        params.setMargins(mRect.left, mRect.top - getStatusBarHeight(), mRect.right, mRect.bottom);        mImageView.setLayoutParams(params);        // 设置 ImageView 的图片和缩放类型        mImageView.setImageResource(mRescourceId);        mImageView.setScaleType(ImageView.ScaleType.CENTER_INSIDE);        // 根据上一个界面传入的图片资源 ID,获取图片的 Bitmap 对象。        BitmapDrawable bitmapDrawable = (BitmapDrawable) getResources().getDrawable(mRescourceId);        Bitmap bitmap = bitmapDrawable.getBitmap();        // 计算图片缩放比例和位移距离        getBundleInfo(bitmap);    }    /**     * 计算图片缩放比例,以及位移距离     */    private void getBundleInfo(Bitmap bitmap) {        // 计算图片缩放比例,并存储在 bundle 中        if (bitmap.getWidth() >= bitmap.getHeight()) {            mScaleBundle.putFloat(SCALE_WIDTH, (float) mScreenWidth / mOriginWidth);            mScaleBundle.putFloat(SCALE_HEIGHT, (float) bitmap.getHeight() / mOriginHeight);        } else {            mScaleBundle.putFloat(SCALE_WIDTH, (float) bitmap.getWidth() / mOriginWidth);            mScaleBundle.putFloat(SCALE_HEIGHT, (float) mScreenHeight / mOriginHeight);        }        // 计算位移距离,并将数据存储到 bundle 中        mTransitionBundle.putFloat(TRANSITION_X, mScreenWidth / 2 - (mRect.left + (mRect.right - mRect.left) / 2));//        mTransitionBundle.putFloat(TRANSITION_Y, mScreenHeight / 2 - (mRect.top + (mRect.bottom - mRect.top) / 2));        mTransitionBundle.putFloat(TRANSITION_Y, -(mRect.top-getStatusBarHeight()));    }    /**     * 模拟入场动画     */    private void runEnterAnim() {        mImageView.animate()                .setInterpolator(DEFAULT_INTERPOLATOR)                .setDuration(DURATION)                .scaleX(mScaleBundle.getFloat(SCALE_WIDTH))                .scaleY(mScaleBundle.getFloat(SCALE_HEIGHT))                .translationX(mTransitionBundle.getFloat(TRANSITION_X))                .translationY(mTransitionBundle.getFloat(TRANSITION_Y))                .start();        mImageView.setVisibility(View.VISIBLE);        //add        mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_EDIT,DURATION);        mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_KEYBOARD,DURATION*2);    }    /**     * 模拟退场动画     */    @SuppressWarnings("NewApi")    private void runExitAnim() {        //add        searchLine.setVisibility(View.GONE);        searchTop.setVisibility(View.GONE);        mImageView.setVisibility(View.VISIBLE);        mImageView.animate()                .setInterpolator(DEFAULT_INTERPOLATOR)                .setDuration(DURATION)                .scaleX(1)                .scaleY(1)                .translationX(0)                .translationY(0)                .withEndAction(new Runnable() {                    @Override                    public void run() {                        finish();                        overridePendingTransition(0, 0);                    }                })                .start();    }    /**     * 获取屏幕尺寸     */    private void getScreenSize() {        Display display = getWindowManager().getDefaultDisplay();        Point size = new Point();        display.getSize(size);        mScreenWidth = size.x;        mScreenHeight = size.y;    }    /**     * 获取状态栏高度     */    private int getStatusBarHeight() {        //获取status_bar_height资源的ID        int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");        if (resourceId > 0) {            //根据资源ID获取响应的尺寸值            return getResources().getDimensionPixelSize(resourceId);        }        return -1;    }    private void showSearchResult(){        searchEdit.addTextChangedListener(new TextWatcher() {            @Override            public void beforeTextChanged(CharSequence s, int start, int count, int after) {            }            @Override            public void onTextChanged(CharSequence s, int start, int before, int count) {            }            @Override            public void afterTextChanged(Editable s) {                //搜索的匹配算法                Log.d("SearchActivity"," afterTextChanged 调用了 s="+s.toString());            }        });    }}

5.参考资料:

1.Android 中的转场动画及兼容处理

2.https://github.com/wl9739/UITransitionDemo

3.用 Transition 完成 Fragment 共享元素的切换

https://github.com/hehonghui/android-tech-frontier/blob/master/issue-35/%E7%94%A8Transition%E5%AE%8C%E6%88%90Fragment%E5%85%B1%E4%BA%AB%E5%85%83%E7%B4%A0%E7%9A%84%E5%88%87%E6%8D%A2.md

更多相关文章

  1. android studio 适配android7.0 android 6.0拍照调用系统裁剪工
  2. Android(安卓)用户界面---操作栏(Action Bar 五)
  3. android 自定义ScrollView实现背景图片伸缩的实现代码及思路
  4. Android实现一个天气界面竟然如此简单?
  5. Android图片滚动,加入自动播放功能,使用自定义属性实现,霸气十足!
  6. Android(安卓)系统拍照及打开系统相册 完美适配 Android(安卓)4
  7. Android短信会话(查看会话记录以及会话详情界面)---短信管家3
  8. Android图文混排(一)-实现EditText图文混合插入上传
  9. 做了个拼图游戏

随机推荐

  1. android 输入法弹出 标题栏不被顶出去
  2. Android系列之Android(安卓)命令行手动编
  3. android横竖屏切换参数
  4. ListView点击效果设置
  5. 安卓中的布局属性详解
  6. Android(安卓)技术要点
  7. Android的Menu
  8. JS判断客户端是否是iOS或者Android
  9. Download Android(安卓)1.5 NDK, Release
  10. android studio ndk 编译自定义MK文件