美文网首页
圆形菜单View

圆形菜单View

作者: coke613 | 来源:发表于2017-09-15 18:59 被阅读0次

    效果图:


    这是建行APP的一个界面,最近在学习自定义view,所以记录一下心得。

    分析:

    ① 因为该view里面可以盛放很多子view,所以要继承ViewGroup,而不是view。
    ② view中的子孩子数据是不确定的,也就是从服务器上请求下来的数据,所以我们在使用该view的时候,要进行设置数据的操作。
    ③ 当使用者设置数据,肯定是一组中必须要有一个text和一个ImageView这两个数据,那么这个是确定的,所以我们可以先写出子view的xml,通过遍历传递过来的数据,一个个将子view 添加到viewGroup中。
    ④ 将子view添加到viewGroup中,要对子view进行测量以及摆放操作。
    ⑤测量,也就是重写onMeasure方法,需要注意的几点:
    a.只是测量自身,不会测量子孩子。
    b.MeasureSpec 是由32位int组成的,前两位是数据模式Mode,剩余是数据大小Size。
    MeasureSpec = size + mode
    c.其中Mode分为三种:
    UNSPECIFIED数值为0<<30(后30位)
    EXACTLY数值为1<<30, 明确的尺寸,如XXXdp,MATCH_PARENT
    AT_MOST数值为2<<30,至多为多少,如WRAP_CONTENT,若控件本身没有默认尺寸,则系统会尽可能的把空间赋予控件,为MATCH_PARENT ~~~~~《注意这点》
    ⑥布局排列 onLayout
    确定子view的位置,左上右下,在计算的时候,会使用到角度值,注意该角度对应每一个view的变化。

    onMeasure方法中常用API:

    setMeasureDimension(width,height):设置控件的最终尺寸
    MeasureSpec spec= MeasureSpec.makeMeasureSpec(size,mode);用于指定MeasureSpec
    MeasureSpec.getSize(measureSpec);通过MeasureSpec获取size
    MeasureSpec.getMode(measureSpec);通过MeasureSpec获取mode
    getSuggestedMinimumWidth():获得背景图的宽度,如果没有背景,则返回值为0
    ····

    开撸:

    1·创建一个类,继承ViewGroup,实现构造:
    public class MenuView extends ViewGroup {
      private String TAG = "MenuView";
    
      public MenuView(Context context) {
          this(context,null);
      }
    
      public MenuView(Context context, AttributeSet attrs) {
          this(context, attrs,0);
      }
    
      public MenuView(Context context, AttributeSet attrs, int defStyleAttr) {
          super(context, attrs, defStyleAttr);
      }
    
    2.xml布局中使用
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent"
                  android:background="@color/colorWhite3"
                  android:gravity="center"
                  android:orientation="vertical">
    
        <zyh.demo.view.MenuView
            android:id="@+id/frg_circle_mv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="@drawable/circle_bg"/>
    </LinearLayout>
    
    3.调用者中使用
    private String[] texts = new String[]{"安全", "服务", "理财",
                "汇款", "账户", "信用"};
    private int[] imgs = new int[]{R.drawable.home_mbank_1_normal,
                R.drawable.home_mbank_2_normal, R.drawable.home_mbank_3_normal,
                R.drawable.home_mbank_4_normal, R.drawable.home_mbank_5_normal,
                R.drawable.home_mbank_6_normal};
    
    .....省略部分代码......
    
    View view = inflater.inflate(R.layout.frg_circle, null, false);
    MenuView  menuView = (MenuView) view.findViewById(R.id.frg_circle_mv);
    //给viewGroup设置数据,当然这个数据是模拟的假数据,不过要保证这两组数据要保持一致,不然会报角标越界
    menuView.setData(texts,imgs);
    
    4.对MenuView进行数据处理
     public void setData(String[] texts, int[] imgs) {
            for (int i = 0; i <texts.length ; i++) {
                 // 遍历使用者传递过来的数据,创建一个子view视图,并转换成view。
                View view = View.inflate(getContext(), R.layout.view_menu, null);
                TextView textView = (TextView) view.findViewById(R.id.menu_tv);
                ImageView imageView = (ImageView) view.findViewById(R.id.menu_iv);
                // 设置数据
                textView.setText(texts[i]);
                imageView.setImageResource(imgs[i]);
                //添加到viewGroup中
                addView(view);
            }
        }
    

    子view视图

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="10dp"
                  android:layout_height="10dp">
        <TextView
            android:id="@+id/menu_tv"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="sss"
            android:textColor="@color/colorWhite"/>
        <ImageView
            android:id="@+id/menu_iv"
            android:layout_gravity="center"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/home_mbank_1_normal"/>
    </LinearLayout>
    
    5.重写onLayout方法
        private int d = 350; //初次定义背景的直径,后期会有改动   
        /**弧度*/
        private int stratAngle = 0;
        @Override
        protected void onLayout(boolean b, int i, int i1, int i2, int i3) {
    
            for (int j = 0; j < getChildCount(); j++) {
                View childView = getChildAt(j);
                // 计算子view的宽度
                int childWidth = childView.getMeasuredWidth();
                // 自定义控件所在的圆到子视图之间的距离。
                float temp = d / 3.0f;
                // 动态的计算子view的左上右下
                int left = (int) (d /2 + Math.round(temp * Math.cos(Math.toRadians(stratAngle))) - childWidth/2);
                int right = left + childWidth;
                int top = (int) (d / 2 + Math.round(temp * Math.sin(Math.toRadians(stratAngle))) - childWidth / 2);
                int buttom = top + childWidth;
                // 确定view布局的位置
                childView.layout(left,top,right,buttom);
                //stratAngle弧度是一个累加过程,不是固定的值
                stratAngle += 360 / getChildCount();
            }
        }
    
    6.重写onMeasure方法,如果不重写该方法的话,会导致看不到子view,因为没有测量
          @Override
          protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          // 测量父布局
            //android:gravity="center"
          //  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
            int measuredWidth = 0;
            int measuredHeight = 0;
    
            //获取测量模式及测量的大小;
            int mode = MeasureSpec.getMode(widthMeasureSpec);
            int size = MeasureSpec.getSize(widthMeasureSpec);
    
            // 判断测量模式
            if(mode != MeasureSpec.EXACTLY) {
                // 这个是不确定的值,最大为 wrap_content
                // 有没有背景?
                //获取背景
                int suggestedMinimumWidth = getSuggestedMinimumWidth();
                if (suggestedMinimumWidth == 0) {   // 没有背景图
                    //默认宽度就是屏幕大小
                    measuredHeight = measuredWidth = getDefaultWidth();
                }else {
                    measuredHeight = measuredWidth = Math.min(suggestedMinimumWidth,getDefaultWidth());
                }
    
    
            }else{      // 固定的数据,match_parent  或者 xxxdp
                //如果是填充父窗体,也就是屏幕的宽高的较小值,《保证在屏幕可见范围内》
                // 如果是固定的数值的话,也就是size,如果用户传入大无穷的数据,也是不可见的,所以屏幕的大小是一个界限。最大不过屏幕的大小
                measuredHeight = measuredWidth = Math.min(size,getDefaultWidth());
            }
    
            d = measuredHeight;
            //设置控件的最终尺寸
            Log.e(TAG,"measuredHeight:"+measuredHeight);
            setMeasuredDimension(measuredWidth,measuredHeight);  //最终目的就是它~~~~~·
    
          //遍历所有的子孩子,进行测量
            for (int i = 0; i < getChildCount(); i++) {
                View childAt = getChildAt(i);
                    // 对子孩子指定MeasureSpec,一定要指定子孩子的MeasureSpec,否则子孩子无法测量,进而不知道子孩子的摆放位置。
                int makeMeasureSpec = MeasureSpec.makeMeasureSpec(d / 5, MeasureSpec.EXACTLY);
                childAt.measure(makeMeasureSpec,makeMeasureSpec);
            }
        }
    
    
        /**
         * 获取屏幕的宽高的较小值
         * */
        private int getDefaultWidth (){
            DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
            int width = displayMetrics.widthPixels;
            int height = displayMetrics.heightPixels;
            return Math.min(width,height);
        }
    

    这样一步步的,自定义view就实现了,明个周六,又可以休息啦~~~~·加油 !!!

    相关文章

      网友评论

          本文标题:圆形菜单View

          本文链接:https://www.haomeiwen.com/subject/nfjssxtx.html