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

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

              

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

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

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

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

<?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的须要动画ide

 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以功能键为原点向右侧和下侧展开。致使弹出来的几个按钮都无点击事件。spa

 

通过从新计算,发现若是将插入的xml设置成屏幕宽高,那么起始的几个buttons都在屏幕中间,也就是图中target1的距离。因此最终肯定插入的xml位移为上图代码中七、8行。rest

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

 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     }

2、总结:

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

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

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

相关文章
相关标签/搜索