动画以及View绘制中的addview实战

Posted antble

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动画以及View绘制中的addview实战相关的知识,希望对你有一定的参考价值。

一、点击按钮弹出卫星导航Button

技术分享图片             技术分享图片 

1、背景:fragment中嵌套recyclerview,当点击功能键(三个点)的时候弹出如右图的导航菜单并伴随动画。

刚接到需求时,开始github上检索相似控件以提供灵感。最终采用这个。https://github.com/linglongxin24/CircleMenu (感谢)

2、灵感:采用根部局为framlayout将五个按钮堆在一起,并置于中间,当点击时,下面的四个按钮做动画,飞出来。点击item,recyclerview通过mRecyclerView.setTranslationY()方法进行上下移动,使buttons能全部弹出,不会被屏幕遮盖。监听到recyclerview位移动画结束后,进行buttons的弹出动画,同时将recyclerview置于不可滑动(通过重写recyclerview),增加蒙层在按钮下边,root_view上边。

3、实施:先上需要插入到root_view中xml item_buttons:(ps小技巧 : 进行测试时,当发现计算有错,或者区域绘制有错,应当给插入的布局一个底色

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

    <com.imaginationunlimited.enigma.weight.roundedimageview.RoundedImageView
        android:id="@+id/but_close"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#000000"
        android:padding="10dp"
        android:src="@drawable/play_cancel"
        app:riv_corner_radius="48dp"
        app:riv_mutate_background="true" />

    <com.imaginationunlimited.enigma.weight.roundedimageview.RoundedImageView
        android:id="@+id/but_again"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:layout_gravity="center"
        android:padding="12.7dp"
        android:src="@mipmap/saved_again"
        app:riv_corner_radius="48dp"
        app:riv_mutate_background="true" />

    <com.imaginationunlimited.enigma.weight.roundedimageview.RoundedImageView
        android:id="@+id/but_save"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:padding="12.7dp"
        android:layout_gravity="center"
        android:src="@mipmap/saved_save"
        app:riv_corner_radius="48dp"
        app:riv_mutate_background="true" />

    <com.imaginationunlimited.enigma.weight.roundedimageview.RoundedImageView
        android:id="@+id/but_share"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:layout_gravity="center"
        android:padding="12.7dp"
        android:src="@mipmap/saved_share"
        app:riv_corner_radius="48dp"
        app:riv_mutate_background="true" />

    <com.imaginationunlimited.enigma.weight.roundedimageview.RoundedImageView
        android:id="@+id/but_delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#ffffff"
        android:layout_gravity="center"
        android:padding="12.7dp"
        android:src="@mipmap/mygallery_delete"
        app:riv_corner_radius="48dp"
        app:riv_mutate_background="true" />
</FrameLayout>

 初始化recyclerview的需要动画

 1 private void initAnim() {
 2         moveRecycler = ValueAnimator.ofFloat(1, 0);
 3         moveRecycler.setDuration(500);
 4         moveRecycler.setInterpolator(new AccelerateDecelerateInterpolator());
 5         moveRecycler.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 6             @Override
 7             public void onAnimationUpdate(ValueAnimator valueAnimator) {
 8                 float value = (float) valueAnimator.getAnimatedValue();
 9                 mRecyclerView.setTranslationY(recoverTranslationY * (1 - value));
10                 shadowView.setAlpha(1 - value);
11             }
12         });
13         moveRecycler.addListener(new AnimatorListenerAdapter() {
14             @Override
15             public void onAnimationEnd(Animator animation) {
16                 butLists(mView);
17             }
18 
19             @Override
20             public void onAnimationStart(Animator animation) {
21                 ifCanClick = false;
22                 shadowView.setVisibility(View.VISIBLE);
23             }
24         });
25         //recycler复位
26         restoreRecycler = ValueAnimator.ofFloat(0, 1);
27         restoreRecycler.setDuration(500);
28         restoreRecycler.setInterpolator(new AccelerateDecelerateInterpolator());
29         restoreRecycler.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
30             @Override
31             public void onAnimationUpdate(ValueAnimator valueAnimator) {
32                 float value = (float) valueAnimator.getAnimatedValue();
33                 mRecyclerView.setTranslationY(recoverTranslationY * (1 - value));
34                 shadowView.setAlpha(1 - value);
35             }
36         });
37         restoreRecycler.addListener(new AnimatorListenerAdapter() {
38             @Override
39             public void onAnimationEnd(Animator animation) {
40                 mRecyclerView.setIfScroll(true);
41                 shadowView.setVisibility(View.GONE);
42             }
43 
44             @Override
45             public void onAnimationStart(Animator animation) {
46                 root_view.removeView(v);
47             }
48         });
49     }

 

初始化数据,设置adapter

 1  private void refreshData() {
 2         //realm中读取拼图
 3         initRealmData();
 4         if (resultsList != null) {
 5             galleryAdapter = new GalleryAdapter(getActivity(), resultsList);
 6             mRecyclerView.setAdapter(galleryAdapter);
 7             LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity(), LinearLayoutManager.
 8                     VERTICAL, false);
 9             mRecyclerView.setLayoutManager(linearLayoutManager);
10             galleryAdapter.setOnItemClickListener(new GalleryAdapter.OnItemClickListener() {
11                 @Override
12                 public void onItemClick(View view, int position) {
13                     view.getLocationOnScreen(a);
14                     mView = view;
15                     // TODO: 2018/12/18 第一个是否需要特殊处理?
16                     moveToMid(a);
17                     mRecyclerView.setIfScroll(false);
18                     galleryAdapter.notifyDataSetChanged();
19                 }
20             });
21         } else {
22             empty_content.setVisibility(View.VISIBLE);
23         }
24     }

 

计算recyclerview偏移量,增加蒙层

 1 private void moveToMid(final int[] a) {
 2         final int screenHeight = DeviceUtils.getScreenHeight();
 3         final int scrollHeight = screenHeight / 2 - a[1];
 4         Log.e(TAG, "moveToMid: scrollHeight:" + scrollHeight + " itemY:" + a[1] + " ");
 5 
 6         recoverTranslationY = scrollHeight;
 7         buttonTranslationY = scrollHeight;
 8 
 9         shadowView = new View(getActivity());
10         shadowView.setBackgroundColor(Color.parseColor("#66000000"));
11         root_view.addView(shadowView);
12         shadowView.setAlpha(0);
13         shadowView.setOnClickListener(new View.OnClickListener() {
14             @Override
15             public void onClick(View view) {
16 
17             }
18         });
41         moveRecycler.start();
42     }

 

addview,设置监听(view为功能键 三个点)

 1     public void butLists(final View view) {
 2         view.setVisibility(View.GONE);
 3         //添加布局
 4         v = LayoutInflater.from(getActivity()).inflate(R.layout.item_buttons, null, false);
 5 
 6         Log.e(TAG, "butLists: a[0]=" + a[0] + ";a[1]=" + a[1] + ";getScreenWidth=" + DeviceUtils.getScreenWidth());
 7         v.setTranslationX(a[0] - DeviceUtils.getScreenWidth() / 2 + mView.getWidth() / 2);    //-------------------写法有待考证
 8         v.setTranslationY(mView.getHeight() / 2);                               //--------------------写法有待考证
 9         ImageView but_close = v.findViewById(R.id.but_close);
10         but_close.setColorFilter(Color.WHITE);
11         //卫星菜单相关
12         final List<ImageView> imageViews = new ArrayList<>();
13         ImageView but_again = v.findViewById(R.id.but_again);
14         ImageView but_share = v.findViewById(R.id.but_share);
15         ImageView but_save = v.findViewById(R.id.but_save);
16         ImageView but_delete = v.findViewById(R.id.but_delete);
17         imageViews.add(but_again);
18         imageViews.add(but_share);
19         imageViews.add(but_save);
20         imageViews.add(but_delete);
21         //将弹出的四个按钮设置为功能键1.3倍 (产品需求)
22 //        for (int i = 0; i < imageViews.size(); i++) {
23 //            FrameLayout.LayoutParams l = new FrameLayout.LayoutParams(imageViews.get(i).getLayoutParams());
24 //            l.width = (int) (view.getWidth() * 1.3);
25 //            l.height = (int) (view.getHeight() * 1.3);
26 //            imageViews.get(i).setLayoutParams(l);
27 //        }
28         but_close.setOnClickListener(new View.OnClickListener() {
29             @Override
30             public void onClick(View view1) {
31                 closeSectorMenu(imageViews, v);
32             }
33         });
34         but_again.setOnClickListener(new View.OnClickListener() {
35             @Override
36             public void onClick(View view) {
37                 Toast.makeText(getActivity(), "but_again", Toast.LENGTH_SHORT).show();
38             }
39         });
40         but_share.setOnClickListener(new View.OnClickListener() {
41             @Override
42             public void onClick(View view) {
43                 Toast.makeText(getActivity(), "but_share", Toast.LENGTH_SHORT).show();
44 
45             }
46         });
47         but_save.setOnClickListener(new View.OnClickListener() {
48             @Override
49             public void onClick(View view) {
50                 Toast.makeText(getActivity(), "but_save", Toast.LENGTH_SHORT).show();
51 
52             }
53         });
54         but_delete.setOnClickListener(new View.OnClickListener() {
55             @Override
56             public void onClick(View view) {
57                 Toast.makeText(getActivity(), "but_delete", Toast.LENGTH_SHORT).show();
58 
59             }
60         });
61         showCircleMenu(imageViews, view);
62         root_view.addView(v);
63         RelativeLayout.LayoutParams ll = new RelativeLayout.LayoutParams(DeviceUtils.getScreenWidth(), DeviceUtils.getScreenHeight());   //将插入的布局设置成全屏
64         v.setLayoutParams(ll);
65     }

 

此前直接将需要addview的布局加到指定位置,结果出现了只有功能键有监听事件,其他弹出的按钮没有。后来通过将插入的xml背景颜色置于黑色,发现整个xml以功能键为原点向右侧和下侧展开。导致弹出来的几个按钮都无点击事件。

 

经过重新计算,发现如果将插入的xml设置成屏幕宽高,那么起始的几个buttons都在屏幕中间,也就是图中target1的距离。所以最终确定插入的xml位移为上图代码中7、8行。

技术分享图片

接下来的代码就是打开和关闭卫星栏及一些状态的监听了

 1 private void showCircleMenu(final List<ImageView> imageViews, View view) {
 2         isShowing = true;
 3         /***第一步,遍历所要展示的菜单ImageView*/
 4         for (int i = 0; i < imageViews.size(); i++) {
 5             final View v = imageViews.get(i);
 6 //            .setLayoutParams(new FrameLayout.LayoutParams(view.getWidth()*1.3,view.getWidth()*1.3));
 7             PointF point = new PointF();
 8             /***第二步,根据菜        .fit()
 9              //         单个数计算每个菜单之间的间隔角度*/
10             int avgAngle = (135 / (imageViews.size() - 1));
11             /**第三步,根据间隔角度计算出每个菜单相对于水平线起始位置的真实角度**/
12             int angle = avgAngle * i - 45;
13             Log.e(TAG, "showCircleMenu: angle:" + angle);
14 
15             //圆点坐标:(x0,y0)
16             //半径:r
17             //角度:a0
18             //则圆上任一点为:(x1,y1)
19             //x1   =   x0   +   r   *   cos(ao   *   3.14   /180   )
20             //y1   =   y0   +   r   *   sin(ao   *   3.14   /180   )
21 
22 
23             //第四步,根据每个菜单真实角度计算其坐标值
24             point.x = (float) -Math.cos(angle * (Math.PI / 180)) * radius1;
25             point.y = (float) Math.sin(angle * (Math.PI / 180)) * radius1;
26             Log.e(TAG, point.toString());
27             ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(imageViews.get(i), "translationX", 0, point.x / 2);
28             ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(imageViews.get(i), "translationY", 0, point.y / 2);
29             //动画集合,用来编排动画
30             AnimatorSet animatorSet = new AnimatorSet();
31             animatorSet.setDuration(300);
32             animatorSet.play(objectAnimatorX).with(objectAnimatorY);
33             animatorSet.addListener(new AnimatorListenerAdapter() {
34                 @Override
35                 public void onAnimationEnd(Animator animation) {
36                     ifCanClick = true;
37                 }
38 
39                 @Override
40                 public void onAnimationStart(Animator animation) {
41                 }
42             });
43             animatorSet.start();
44         }
45     }

 

关闭

 1  private void closeSectorMenu(List<ImageView> imageViews, final View v) {
 2         for (int i = 0; i < imageViews.size(); i++) {
 3             PointF point = new PointF();
 4             int avgAngle = (135 / (imageViews.size() - 1));
 5             int angle = avgAngle * i - 45;
 6             Log.d(TAG, "angle=" + angle);
 7             point.x = (float) -Math.cos(angle * (Math.PI / 180)) * radius1;
 8             point.y = (float) Math.sin(angle * (Math.PI / 180)) * radius1;
 9             Log.d(TAG, point.toString());
10 
11             ObjectAnimator objectAnimatorX = ObjectAnimator.ofFloat(imageViews.get(i), "translationX", point.x / 2, 0);
12             ObjectAnimator objectAnimatorY = ObjectAnimator.ofFloat(imageViews.get(i), "translationY", point.y / 2, 0);
13             AnimatorSet animatorSet = new AnimatorSet();
14             animatorSet.setDuration(300);
15             animatorSet.play(objectAnimatorX).with(objectAnimatorY);
16             animatorSet.addListener(new AnimatorListenerAdapter() {
17                 @Override
18                 public void onAnimationEnd(Animator animation) {
19                     //点击叉,收起后recyclerview归位
20                     mView.setVisibility(View.VISIBLE);
21                     restoreRecycler.start();
22                 }
23             });
24             animatorSet.start();
25         }
26         isShowing = false;
27     }

 

剩下就是adapter中的回调了,比较简单

 1  //点击功能键
 2         holder.but_func.setOnClickListener(new View.OnClickListener() {
 3             @Override
 4             public void onClick(View view) {
 5                 if (GalleryFragment.ifCanClick){
 6                     int position = holder.getAdapterPosition();
 7                     onItemClickListener.onItemClick(holder.but_func, position);
 8                     //标记点击的item的位置(没用上)
 9                     posList.clear();
10                     posList.add(position);
11                     Log.e(TAG, "点击功能键: " + position);
12                 }
13             }
14         });

 

接口:包括set方法

1   /**
2      * 自定义的点击事件接口
3      *
4      * @author lish
5      */
6     public interface OnItemClickListener {
7         void onItemClick(View view, int position);
8 //      void onItemLongClick(View view, int position); //长按
9     }

二、总结:

1、对view的绘制流程还是不熟练,LayoutParam不够深入了解,只会简单实用,不知原理

2、对动画的使用不够熟练

3、对计算偏移量不够熟练

 

以上是关于动画以及View绘制中的addview实战的主要内容,如果未能解决你的问题,请参考以下文章

安卓自定义view仿小米商城购物车动画

安卓自定义view仿小米商城购物车动画

Android自定义View实战之仿QQ运动步数圆弧及动画,Dylan计步中的控件StepArcView

Android自定义View实战教程5??---Canvas详解及代码绘制安卓机器人

Android13_fragment_view动画_属性动画

addView(View)不向objectView添加对象