一、点击按钮弹出卫星导航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.RoundedImageViewandroid: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.RoundedImageViewandroid: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.RoundedImageViewandroid: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.RoundedImageViewandroid: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.RoundedImageViewandroid: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、对计算偏移量不够熟练

转载于:https://www.cnblogs.com/antble/p/10140224.html

动画以及View绘制中的addview实战相关推荐

  1. android伸缩动画自定义,Android干货:自定义带动画的View

    对于一个自定义View来说,onMeasure只是用来计算View尺寸,onDraw()才是真正执行View的绘制,所以一般我们都需要重写onDraw()函数来绘制我们期望的UI界面,下面我以一个具体 ...

  2. android view绘制中调用的函数,Android开发实践:自定义带动画的View

    前面两篇文章介绍了自定义View的onMeasure和onLayout原理,本文准备介绍自定义View的第三个关键部分,即onDraw()函数的重载. 对于一个自定义View来说,onMeasure只 ...

  3. Android之动画精讲一:从setTranslationX谈属性动画和view动画的区别

    转载:http://blog.csdn.net/yanzi1225627/article/details/47850471 最近又用到了动画,决定把几次项目里用到的动画走过的弯路总结一下,顺便梳理下a ...

  4. Android -- 三种动画(帧动画、View动画、属性动画)

    Android的动画分为了三种, 分别是 帧动画.View动画.属性动画 一:帧动画 帧动画就是顺序播放一组预先定义好的图片,就类似于我们观看视频,就是一张一张的图片连续播放. 帧动画的使用很简单,总 ...

  5. android动画之帧动画(drawable animation)和补间动画(view animation)

    帧动画 drawable animation使用比较简单,而且支持市场上所以API版本,下面简单贴下代码.帧动画,就像GIF图片,通过一系列Drawable依次显示来模拟动画的效果. 直接代码贴上 x ...

  6. Android模仿淘宝语音输入条形动画,录音动画自定义View

    Android模仿淘宝语音输入条形动画自定义View,类似柱状音频,折线音频,音乐跳动,音频跳动,录音动画,语音输入效果 地址: https://github.com/xfans/VoiceWaveV ...

  7. Android的三种动画详解(帧动画、View动画、属性动画)

    Android的动画分为了三种, 分别是 帧动画.View动画.属性动画. 1.帧动画 帧动画就是顺序播放一组预先定义好的图片,就类似于我们观看视频,就是一张一张的图片连续播放. 帧动画的使用很简单, ...

  8. android view交替动画,Android View原理(View树遍历,View重绘,View动画)

    一.屏幕绘图基础 Android中的GUI系统是客户端和服务端配合的窗口系统,即后台运行了一个绘制服务,每个应用程序都是该服务端的一个客户端,当客户端需要绘制时,首先请求服务端创建一个窗口,然后在窗口 ...

  9. Matlab中动画绘制中hold on的小问题

    有很多小伙伴都喜欢使用getframe和movie绘制动画,一般情况下都没有什么问题.但是当其结合使用hold on命令时一定要小心.比如如下代码: T_a = 0:300:3600; a = 802 ...

最新文章

  1. Tcpdump命令的使用与示例——linux下的网络分析
  2. SFB 项目经验-65-使用域管理员安装不了Exchange 2010 SP3 CU21
  3. PHP中的urlencode和urldecode
  4. [单反八]人像三平原则
  5. 【C】课堂结对联系-求整数数组的子数组之和的最大值(党云龙、黄为)
  6. [深度学习-实践]BP神经网络的Helloworld(手写体识别和Fashion_mnist)
  7. oracle11g 端口,navicate 连接 oracle11g精简版监听不到端口和用户密码错误问题
  8. java对象与内存控制
  9. UINavigation​Controller 的详解(基于 API )
  10. 解决iview中</Input>标签报错的方法
  11. JavaScript深入之执行上下文栈 1
  12. 微信小程序轮播图实现(超简单)
  13. 关于SQL2005安装完毕后,没有SQL Server Management Studio问题的解决方法
  14. 如何在Mac电脑上更改地区或国家?
  15. kronecker引理证明_连续型Kronecker引理
  16. 暑期实践第二十九天 2022-8-1
  17. bzoj4200: [Noi2015]小园丁与老司机(可行流+dp)
  18. 基于Helm和Operator的K8S应用管理
  19. CF1132D Stressful Training
  20. ccf化学方程式配平检验

热门文章

  1. 如何改变控件内的字体颜色?
  2. 实现Ajax异步交互
  3. r510服务器开机无显示,联智通达工业主板常见问题之工控电脑开机无显示
  4. 使用redis做缓存,遇到Could not return the resource to the pool异常怎么办呐!
  5. JS获取页面中Url的某个参数
  6. 阿里云自动java和mysql数据库_阿里云服务器之基于Linux系统部署上线JavaWeb项目和连接MySQL数据库(从购买云服务器到发布JavaWeb项目全套详细流程)...
  7. 活动目录管理中常用的脚本(一)
  8. C# 8.0 预览特性
  9. [原创]Scala学习:编写Scala脚本
  10. 几个常用方法有效优化ASP.NET的性能