• 动画以及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、对计算偏移量不够熟练

  • 相关阅读:
    servlet中doGet()和doPost()的区别
    Hibernate 的getHibernateTemplate()方法使用
    c3p0 数据库连接池
    java 事务处理的概念
    hibernate 关联映射
    java 迭代器
    struts 值桟问题
    struts 属性驱动与模型驱动
    java 单例模式
    XML Node和Element
  • 原文地址:https://www.cnblogs.com/antble/p/10140224.html
Copyright © 2020-2023  润新知