动画以及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.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实战相关推荐
- android伸缩动画自定义,Android干货:自定义带动画的View
对于一个自定义View来说,onMeasure只是用来计算View尺寸,onDraw()才是真正执行View的绘制,所以一般我们都需要重写onDraw()函数来绘制我们期望的UI界面,下面我以一个具体 ...
- android view绘制中调用的函数,Android开发实践:自定义带动画的View
前面两篇文章介绍了自定义View的onMeasure和onLayout原理,本文准备介绍自定义View的第三个关键部分,即onDraw()函数的重载. 对于一个自定义View来说,onMeasure只 ...
- Android之动画精讲一:从setTranslationX谈属性动画和view动画的区别
转载:http://blog.csdn.net/yanzi1225627/article/details/47850471 最近又用到了动画,决定把几次项目里用到的动画走过的弯路总结一下,顺便梳理下a ...
- Android -- 三种动画(帧动画、View动画、属性动画)
Android的动画分为了三种, 分别是 帧动画.View动画.属性动画 一:帧动画 帧动画就是顺序播放一组预先定义好的图片,就类似于我们观看视频,就是一张一张的图片连续播放. 帧动画的使用很简单,总 ...
- android动画之帧动画(drawable animation)和补间动画(view animation)
帧动画 drawable animation使用比较简单,而且支持市场上所以API版本,下面简单贴下代码.帧动画,就像GIF图片,通过一系列Drawable依次显示来模拟动画的效果. 直接代码贴上 x ...
- Android模仿淘宝语音输入条形动画,录音动画自定义View
Android模仿淘宝语音输入条形动画自定义View,类似柱状音频,折线音频,音乐跳动,音频跳动,录音动画,语音输入效果 地址: https://github.com/xfans/VoiceWaveV ...
- Android的三种动画详解(帧动画、View动画、属性动画)
Android的动画分为了三种, 分别是 帧动画.View动画.属性动画. 1.帧动画 帧动画就是顺序播放一组预先定义好的图片,就类似于我们观看视频,就是一张一张的图片连续播放. 帧动画的使用很简单, ...
- android view交替动画,Android View原理(View树遍历,View重绘,View动画)
一.屏幕绘图基础 Android中的GUI系统是客户端和服务端配合的窗口系统,即后台运行了一个绘制服务,每个应用程序都是该服务端的一个客户端,当客户端需要绘制时,首先请求服务端创建一个窗口,然后在窗口 ...
- Matlab中动画绘制中hold on的小问题
有很多小伙伴都喜欢使用getframe和movie绘制动画,一般情况下都没有什么问题.但是当其结合使用hold on命令时一定要小心.比如如下代码: T_a = 0:300:3600; a = 802 ...
最新文章
- Tcpdump命令的使用与示例——linux下的网络分析
- SFB 项目经验-65-使用域管理员安装不了Exchange 2010 SP3 CU21
- PHP中的urlencode和urldecode
- [单反八]人像三平原则
- 【C】课堂结对联系-求整数数组的子数组之和的最大值(党云龙、黄为)
- [深度学习-实践]BP神经网络的Helloworld(手写体识别和Fashion_mnist)
- oracle11g 端口,navicate 连接 oracle11g精简版监听不到端口和用户密码错误问题
- java对象与内存控制
- UINavigation​Controller 的详解(基于 API )
- 解决iview中</Input>标签报错的方法
- JavaScript深入之执行上下文栈 1
- 微信小程序轮播图实现(超简单)
- 关于SQL2005安装完毕后,没有SQL Server Management Studio问题的解决方法
- 如何在Mac电脑上更改地区或国家?
- kronecker引理证明_连续型Kronecker引理
- 暑期实践第二十九天 2022-8-1
- bzoj4200: [Noi2015]小园丁与老司机(可行流+dp)
- 基于Helm和Operator的K8S应用管理
- CF1132D Stressful Training
- ccf化学方程式配平检验
热门文章
- 如何改变控件内的字体颜色?
- 实现Ajax异步交互
- r510服务器开机无显示,联智通达工业主板常见问题之工控电脑开机无显示
- 使用redis做缓存,遇到Could not return the resource to the pool异常怎么办呐!
- JS获取页面中Url的某个参数
- 阿里云自动java和mysql数据库_阿里云服务器之基于Linux系统部署上线JavaWeb项目和连接MySQL数据库(从购买云服务器到发布JavaWeb项目全套详细流程)...
- 活动目录管理中常用的脚本(一)
- C# 8.0 预览特性
- [原创]Scala学习:编写Scala脚本
- 几个常用方法有效优化ASP.NET的性能