本文中代码参考开源项目 QuickNews中的频道选择模块,并非本人原创,只是将部分功能抽出单独分析。
主要是供个人学习,分析其实现过程,也是我自己(小白一枚)的学习笔记供大家一起学习。
移步下载源码,也可以直接下载QuickNews查看。
基本实现思路:其实还是蛮简单的,上下两个GridView,分别监听Item点击事件,当点击某个Item时,获取当前Item在窗口中的坐标作为平移动画起点坐标,并创建该Item的一个 DrawingCacheView( 缩略图)用于动画,同时在对方GridView末尾创建一个站位的Item(没有内容),并获得该Item在窗口中的坐标作为动画终点坐标。对DrawingCacheView启动动画,当动画结束删除自己GridView中的Item,显示对方GridView的Item。
        首先,GridView是固定高度的,即所有Item要全部显示出来,那么GridView在测量时就要将所有子View的高度包含进来,测量子View的方法我就不再细说,网上有,自定义GridView如下(当然我们也可以使用普通的GridView ,看你需要什么样效果):
public class ChannelGridView extends GridView{

    public ChannelGridView(Context context) {        super(context);    }

    public ChannelGridView(Context context, AttributeSet attrs) {        super(context, attrs);    }

    public ChannelGridView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }

    /**     * 屏蔽GridView滑动     * 让GridView全部显示出来 */    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,                MeasureSpec.AT_MOST);        super.onMeasure(widthMeasureSpec, expandSpec);    }}
基本布局结构如下(由于我们的GridView是全部显示出来的,考虑可能超过一屏,在外面套了一层ScrollView用于滑动):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent">

    <include        android:id="@+id/title"        layout="@layout/layout_title_bar" />

    <ScrollView        android:layout_width="match_parent"        android:layout_height="match_parent"        android:layout_below="@id/title">        <LinearLayout            android:layout_width="match_parent"            android:layout_height="match_parent"            android:orientation="vertical">            <LinearLayout                android:orientation="vertical"                android:layout_width="match_parent"                android:layout_height="wrap_content">

                <TextView                    android:layout_marginTop="14dp"                    android:layout_marginBottom="14dp"                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:text="已选"                    android:textSize="16sp"                    android:textStyle="bold" />

                <channel.android.ljb.com.channel.view.ChannelGridView                    android:id="@+id/top_gridview"                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:layout_marginLeft="14dip"                    android:layout_marginRight="14dip"                    android:gravity="center"                    android:horizontalSpacing="14dip"                    android:listSelector="@android:color/transparent"                    android:numColumns="4"                    android:scrollbars="vertical"                    android:stretchMode="columnWidth"                    android:verticalSpacing="14.0px"/>            </LinearLayout>

            <View                android:layout_width="match_parent"                android:layout_height="0.5dp"                android:layout_marginBottom="14dp"                android:layout_marginTop="14dp"                android:background="#ffcdcdcd" />

            <LinearLayout                android:orientation="vertical"                android:layout_width="match_parent"                android:layout_height="wrap_content">

                <TextView                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:text="未选"                    android:layout_marginBottom="14dp"                    android:textSize="16sp" />

                <channel.android.ljb.com.channel.view.ChannelGridView                    android:id="@+id/bottom_gridview"                    android:layout_width="match_parent"                    android:layout_height="wrap_content"                    android:layout_marginLeft="14dip"                    android:layout_marginRight="14dip"                    android:gravity="center"                    android:horizontalSpacing="14dip"                    android:listSelector="@android:color/transparent"                    android:numColumns="4"                    android:scrollbars="vertical"                    android:stretchMode="columnWidth"                    android:verticalSpacing="14.0px"/>            </LinearLayout>        </LinearLayout>    </ScrollView>

</RelativeLayout>

准备工作基本完成 ,进入主题(此处我先贴出Activity的全部代码,后面对其每部分代码进行分析):

public class ChannelActivity extends Activity implements AdapterView.OnItemClickListener {

    /**窗口容器(根容器)*/    private ViewGroup windowViewGroup;    /**要移动的View*/    private View moveView;

    /**顶部GridView*/    private ChannelGridView gv_top;    /**底部GridView*/    private ChannelGridView gv_bottom;

    /**顶部和底部GridView的的适配器*/    private ChannelAdapter mTopAdapter;    private ChannelAdapter mBottomAdapter;

    /**顶部和底部GridView的数据*/    private List<String> mTopDatas;    private List<String> mBottomDatas;

    /**标记:item是否正在移动*/    private boolean isMoving;

    public void getDatas() {        mTopDatas = new ArrayList<String>();        mTopDatas.add("ABT");        mTopDatas.add("奥迪");        mTopDatas.add("宝马");        mTopDatas.add("雷诺");        mTopDatas.add("别克");        mTopDatas.add("Jeep");        mTopDatas.add("巴博斯");        mTopDatas.add("比亚迪");        mTopDatas.add("标致");        mTopDatas.add("宾利");        mTopDatas.add("夏利");        mTopDatas.add("保时捷");        mTopDatas.add("吉利");        mTopDatas.add("克莱斯特");        mTopDatas.add("铃木");        mTopDatas.add("华普");

        mBottomDatas = new ArrayList<String>();        mBottomDatas.add("悍马");        mBottomDatas.add("劳斯莱斯");        mBottomDatas.add("卡迪拉克");        mBottomDatas.add("雪佛兰");        mBottomDatas.add("丰田");        mBottomDatas.add("马自达");        mBottomDatas.add("林肯");        mBottomDatas.add("沃尔沃");        mBottomDatas.add("雷克萨斯");        mBottomDatas.add("莲花");        mBottomDatas.add("现代");        mBottomDatas.add("猎豹");        mBottomDatas.add("江淮");        mBottomDatas.add("红旗");        mBottomDatas.add("五菱");    }

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        initView();        initData();    }

    private void initView() {        setContentView(R.layout.activity_channel);        gv_top = (ChannelGridView) findViewById(R.id.top_gridview);        gv_bottom = (ChannelGridView) findViewById(R.id.bottom_gridview);    }

    private void initData() {

        getDatas();

        mTopAdapter = new ChannelAdapter(mTopDatas, this);        mBottomAdapter = new ChannelAdapter(mBottomDatas, this);

        gv_top.setAdapter(mTopAdapter);        gv_bottom.setAdapter(mBottomAdapter);

        gv_top.setOnItemClickListener(this);        gv_bottom.setOnItemClickListener(this);    }

    @Override    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {        if (isMoving) {            return;        } else if (parent.getId() == R.id.top_gridview) {            onTopItemClick(view, position);        } else if (parent.getId() == R.id.bottom_gridview) {            onBottomItemClick(view, position);        }    }

    /**     * 顶部GridView Item被点击     */    private void onTopItemClick(View view, int position) {        //将点击的Item内容隐藏        mTopAdapter.setmRemovePosition(position);

        //获取点击item的缩略图,用于动画平移        moveView = UIUtils.getDrawingCacheView(view);        if (moveView != null) {            //获取起点坐标(ItemView的当前屏幕内坐标既是)            final int[] startLocation = new int[2];            view.getLocationInWindow(startLocation);

            //让底部的GridView暂时在末尾添加一个空白item站位            mBottomAdapter.setLastItemVisibility(false);            String itemData = mTopAdapter.getItem(position);            mBottomAdapter.addItem(itemData);

            //此处需要延时一段时间,等待Adapter末尾创建完站位的item            new Handler().postDelayed(new Runnable() {                @Override                public void run() {                    //获取终点坐标(底部GridView用于站位的最后一个item坐标既是)                    int[] endLocation = new int[2];                    gv_bottom.getChildAt(gv_bottom.getLastVisiblePosition()).getLocationInWindow(endLocation);

                    //开启平移动画                    moveItemAnim(startLocation, endLocation, gv_top);                }            }, 50L);        }    }

    /**     * 底部GridView Item被点击     */    private void onBottomItemClick(View view, int position) {        mBottomAdapter.setmRemovePosition(position);

        moveView = UIUtils.getDrawingCacheView(view);        if (moveView != null) {

            final int[] startLocation = new int[2];            view.getLocationInWindow(startLocation);

            mTopAdapter.setLastItemVisibility(false);            String itemData = mBottomAdapter.getItem(position);            mTopAdapter.addItem(itemData);

            new Handler().postDelayed(new Runnable() {                @Override                public void run() {                    int[] endLocation = new int[2];                    gv_top.getChildAt(gv_top.getLastVisiblePosition()).getLocationInWindow(endLocation);

                    moveItemAnim(startLocation, endLocation, gv_bottom);                }            }, 50L);        }    }

    /**     * Item平移动画     */    private void moveItemAnim(int[] startLocation, int[] endLocation , final GridView clickGridView) {

        //缩略图只是我们在内存中创建的,其还没有加载到我们的界面中,所以不可直接移动,先要进行处理        initMoveView();

        TranslateAnimation transAnima = new TranslateAnimation(startLocation[0], endLocation[0], startLocation[1],                endLocation[1]);        transAnima.setDuration(300);        transAnima.setFillAfter(true);        transAnima.setAnimationListener(new Animation.AnimationListener() {            @Override            public void onAnimationStart(Animation animation) {                isMoving = true;                moveView.setVisibility(View.VISIBLE);            }

            @Override            public void onAnimationEnd(Animation animation) {                isMoving = false;                moveView.setVisibility(View.INVISIBLE);                windowViewGroup.removeView(moveView);                moveView = null;

                if (clickGridView == gv_top) {                    //点击的是顶部GridView , 那么底部的最后一个Item可以显示出来了                    mBottomAdapter.setLastItemVisibility(true);                    mBottomAdapter.notifyDataSetChanged();

                    //最后移除顶部被点击的Item                    mTopAdapter.remove();

                } else if (clickGridView == gv_bottom) {                    mTopAdapter.setLastItemVisibility(true);                    mTopAdapter.notifyDataSetChanged();                    mBottomAdapter.remove();                }

            }

            @Override            public void onAnimationRepeat(Animation animation) {

            }        });

        moveView.startAnimation(transAnima);    }

    /**     * 对MoveView进行相关处理,并添加到我们的界面中     */    private void initMoveView() {        //获取窗口容器        windowViewGroup = (ViewGroup) getWindow().getDecorView();        //再将moveView添加到父容器中        ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT ,ViewGroup.LayoutParams.WRAP_CONTENT );        moveView.setLayoutParams(params);        moveView.setVisibility(View.GONE);        windowViewGroup.addView(moveView);    }}

getDatas()方法就不多说了,我们的模拟数据;initView()方法初始化两个GridView,initData()方法将数据与GridView适配,并为GridView设置Item点击事件。

重点就在Item点击事件中,上下两个GridView的点击事件基本相似,以下以顶部GridView Item点击事件进行讲解。
/**     * 顶部GridView Item被点击     */    private void onTopItemClick(View view, int position) {        //将点击的Item内容隐藏        mTopAdapter.setmRemovePosition(position);

        //获取点击item的缩略图,用于动画平移        moveView = UIUtils.getDrawingCacheView(view);        if (moveView != null) {            //获取起点坐标(ItemView的当前屏幕内坐标既是)            final int[] startLocation = new int[2];            view.getLocationInWindow(startLocation);

            //让底部的GridView暂时在末尾添加一个空白item站位            mBottomAdapter.setLastItemVisibility(false);            String itemData = mTopAdapter.getItem(position);            mBottomAdapter.addItem(itemData);

            //此处需要延时一段时间,等待Adapter末尾创建完站位的item            new Handler().postDelayed(new Runnable() {                @Override                public void run() {                    //获取终点坐标(底部GridView用于站位的最后一个item坐标既是)                    int[] endLocation = new int[2];                    gv_bottom.getChildAt(gv_bottom.getLastVisiblePosition()).getLocationInWindow(endLocation);

                    //开启平移动画                    moveItemAnim(startLocation, endLocation, gv_top);                }            }, 50L);        }    }
Adapter中代码:
public class ChannelAdapter extends BaseAdapter {

    private List<String> mDatas ;    private Context mContext;

    /**标识最后一个item是否显示数据,默认显示*/    private boolean lastVisable = true;

    /**要移除的Item位置*/    private int mRemovePosition = -1;

    public ChannelAdapter(List<String> datas , Context context){        this.mDatas = datas ;        this.mContext = context;    }

    @Override    public int getCount() {        return mDatas.size();    }

    @Override    public String getItem(int position) {        return mDatas.get(position);    }

    @Override    public long getItemId(int position) {        return position;    }

    @Override    public View getView(int position, View convertView, ViewGroup parent) {        View view = View.inflate(mContext , R.layout.item_channel , null);        ViewHolder holder = null;

        if(convertView==null){            holder = new ViewHolder();            holder.tv_content = (TextView) view.findViewById(R.id.tv_content);            holder.iv_new = (ImageView) view.findViewById(R.id.iv_new);            view.setTag(holder);        }else{            view = convertView;            holder = (ViewHolder) view.getTag();        }

        //判断最后一个Item是否需要设置数据        if(!lastVisable && position == getCount()-1){            holder.tv_content.setText("");        }else{            holder.tv_content.setText(getItem(position));        }

        //隐藏要移除的Item内容        if(mRemovePosition == position){            holder.tv_content.setText("");        }

        return view;    }

    /**设置最后一个item是否显示数据*/    public void setLastItemVisibility(boolean visible) {        this.lastVisable = visible;    }

    /**新增一个item*/    public void addItem(String itemData){        mDatas.add(itemData);        notifyDataSetChanged();    }

    /**要移除Item的位置*/    public void setmRemovePosition(int position){        mRemovePosition = position;        notifyDataSetChanged();    }

    /**移除mRemovePosition对应的Item*/    public void remove() {        mDatas.remove(mRemovePosition);        mRemovePosition = -1;        notifyDataSetChanged();    }

    static  class ViewHolder{        TextView tv_content;        ImageView iv_new;    }}
Item点击时,为了后面配合平移动画,此处将当前点击的Item内容进行隐藏

mBottomAdapter.setmRemovePosition(position);
Adapter中:
/**要移除Item的位置*/
public void setmRemovePosition(int position){mRemovePosition = position;
    notifyDataSetChanged();
}
拿到要隐藏的postion后,刷新数据,在gitView方法中判断, 并将当前要移除的数据设置为空。
以上这步操作只是为了配合动画体验,紧接着继续往下看Item点击事件, 获取当前Item的 DrawingCacheView,获取方式如下:
moveView = UIUtils.getDrawingCacheView(view);
UIUtils中:
public static ImageView getDrawingCacheView(View view) {view.destroyDrawingCache();
    view.setDrawingCacheEnabled(true);
    Bitmap cache = Bitmap.createBitmap(view.getDrawingCache());
    view.setDrawingCacheEnabled(false);
    ImageView iv = new ImageView(getContent());
    iv.setImageBitmap(cache);
    return iv;
}
此时,拿到了当前Item的缩略图,但当前缩略图只是由我们在内存中创建的,还未使用到界面中,稍后我们会处理。
以下这段代码主要是获取动画的起始点和重点,起始点就是当前点击的Item在窗口中的位置,终点则是我们通过另一个GridView中末尾新添加的Item位置,此处需要注意由于新添加的Item需要一点时间,所以在获取它的坐标前,延时50毫秒。
if (moveView != null) {final int[] startLocation = new int[2];
    view.getLocationInWindow(startLocation);

    mTopAdapter.setLastItemVisibility(false);
    String itemData = mBottomAdapter.getItem(position);
    mTopAdapter.addItem(itemData);

    new Handler().postDelayed(new Runnable() {@Override
        public void run() {int[] endLocation = new int[2];
            gv_top.getChildAt(gv_top.getLastVisiblePosition()).getLocationInWindow(endLocation);

            moveItemAnim(startLocation, endLocation, gv_bottom);
        }}, 50L);
}
当拿到动画的两点坐标后就可以准备动画了:
/**
 * Item平移动画
 */
private void moveItemAnim(int[] startLocation, int[] endLocation , final GridView clickGridView)
moveItemAnim()方法参数分别代表(起始点,终点,当前被点击的Item属于的GridView)
我们前面说过此时的moveView只是内存中的一个对象,所以在为它启动动画前,先需要将其加入到我们的界面中,在initMoveView()方法中我们对其进行处理:
/**
 * 对MoveView进行相关处理,并添加到我们的界面中
 */
private void initMoveView() {//获取窗口容器
    windowViewGroup = (ViewGroup) getWindow().getDecorView();
    //再将moveView添加到父容器中
    ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT ,ViewGroup.LayoutParams.WRAP_CONTENT );
    moveView.setLayoutParams(params);
    moveView.setVisibility(View.GONE);
    windowViewGroup.addView(moveView);
}
获取窗口的activity的根容器,将其加入,此时这个用于动画的缩略图应该是在窗体的左上角,为了不影响UI,当动画开启时我们再让它显示出来。
最后,开启动画,并对动画进行监听,实现我们想要的效果即可。
TranslateAnimation transAnima = new TranslateAnimation(startLocation[0], endLocation[0], startLocation[1],
        endLocation[1]);
transAnima.setDuration(300);
transAnima.setFillAfter(true);
transAnima.setAnimationListener(new Animation.AnimationListener() {@Override
    public void onAnimationStart(Animation animation) {//动画开启显示移动的view,并改变标识
        isMoving = true;
        moveView.setVisibility(View.VISIBLE);
    }@Override
    public void onAnimationEnd(Animation animation) {//动画结束后,改变标识,当前moveView已经没有任何意义,从容器中移除
        isMoving = false;
        moveView.setVisibility(View.INVISIBLE);
        windowViewGroup.removeView(moveView);
        moveView = null;

        if (clickGridView == gv_top) {//点击的是顶部GridView , 那么底部的最后一个Item可以显示出来了
            mBottomAdapter.setLastItemVisibility(true);
            mBottomAdapter.notifyDataSetChanged();
            //最后移除顶部被点击的Item
            mTopAdapter.remove();

        } else if (clickGridView == gv_bottom) {mTopAdapter.setLastItemVisibility(true);
            mTopAdapter.notifyDataSetChanged();
            mBottomAdapter.remove();
        }}@Override
    public void onAnimationRepeat(Animation animation) {}
});

moveView.startAnimation(transAnima);
此处,isMoving标记是为了控制只有一个平移动画在运行,以防混乱,并且在动画结束时显示先前用于在对方GridView上站位的Item数据和移除自身的Item(前面操作我们只是隐藏了自身的数据,并没有移除整个Item)。
到此,基本的带平移动画的栏目选择就完成了,QuickNews中的顶部GridView还带有长按排序功能,稍后我会对其实现过程再进行介绍。

Android带平移动画的栏目选择功能相关推荐

  1. Android【平移动画】

    平移动画

  2. android 指示器平移动画,Android实现带指示器的自动轮播式ViewPager

    前言 最近在做项目的时候,有个需求就是实现自动轮播式的ViewPager,最直观的例子就是知乎日报顶部的ViewPager,它内部有着好几个子view,每个一段时间便自动滑动到下一个item view ...

  3. android 循环平移动画

    2019独角兽企业重金招聘Python工程师标准>>> 实现用一张背景图做循环从左往右平移动画. 1. 实现两个animation xml文件,一个起始位置在-100%p ,一个在0 ...

  4. android 方块平移动画,Canvas 方块平移动画

    JavaScript 语言: JaveScriptBabelCoffeeScript 确定 var c = document.createElement("canvas"); do ...

  5. android 方块平移动画,android – 在Surface View中动画和旋转图像

    手动旋转图像可能有点痛苦,但这就是我如何做到的. private void animateRotation(int degrees,float durationOfAnimation){ long s ...

  6. android图片做平移动画,Android中用Matrix实现ImageView里的图片平移和缩放动画

    注: 这里说的图片的平移和缩放不是对ImageView整个view进行的,而是对ImageView里面的图片进行的(view本身没有什么变化),所以Android自带的动画效果不能满足需求. 功能点: ...

  7. android淡入淡出动画循环,Android应用开发之淡入淡出、缩放、旋转、平移、组合动画效果代码实现...

    本文将带你了解Android应用开发Android动画开发之淡入淡出.缩放.旋转.平移.组合动画效果代码实现,希望本文对大家学Android有所帮助. 1.activity_main.xml文件 an ...

  8. android 缩放透明动画,Android旋转、平移、缩放和透明度渐变的补间动画

    android实现旋转.平移.缩放和透明度渐变的补间动画,具体实现如下: 1.在新建项目的res目录中,创建一个名为anim的目录,并在该目录中创建实现旋转.平移.缩放和透明度渐变的动画资源文件. 透 ...

  9. Android 仿微信红包动画 平移动画

    Android 仿微信红包动画 平移动画 先看效果图: 简单思路: 先找好素材,一张红包封面和 "开"这个图片,先用ps将红包图片P成两部分,两个椭圆的样子."开&quo ...

最新文章

  1. mysql 包括冒号_hibernate中SQL包含冒号
  2. formal method 2月23日第八课的内容!schema calculus!
  3. 在WebGL场景中进行棋盘操作的实验
  4. java里面如何加入高级的东西_如何成为一名Java高级架构师
  5. 轻松实现SQL Server与Access、Excel数据表间的导入导出
  6. linux系统电脑的权限设置,Linux下的文件权限设置修改详解linux操作系统 -电脑资料...
  7. 递增的整数序列链表的插入_你所不知道的序列容器
  8. Chrome的两个工具
  9. java -jar 命令隐藏黑窗口
  10. OPA:open policy agent简介
  11. 学生的知识管理工具:有道云笔记、幕布和 Effie
  12. Vue中的vm和VueComponent的实例对象
  13. python爬虫爬取音乐_利用python爬虫实现爬取网易云音乐热歌榜
  14. 快捷键-vscode-excel
  15. Vue项目关闭格式检查命令
  16. Android WebView最佳优化(WebView池)
  17. python魔方方法__call__
  18. CSS学习笔记-—学会用PS切图和取色—day03(基本用法)
  19. 从CSDN账户密码被盗说起
  20. 【二】jupyter lab插件相关问题

热门文章

  1. SppNet论文笔记
  2. 我的5s手机为啥显示无服务器,苹果5信号增强器安转方法?增强信号的相关方法...
  3. 日学壹技:json.load() vs json.loads()
  4. 简单几行python + opencv代码达到美颜、逆美颜效果
  5. 如何做一个淘宝商品的预览效果图
  6. android 空调遥控器——简单发送内容
  7. Redis是单线程的,为什么还会这么快?
  8. python基础之 序列化,os,sys,random,hashlib
  9. 001:半路天师张北玄,一键传承天师度!
  10. redis配置项(protected-mode daemonize )