自定义ViewGroup——圆形排列LinearLayout

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014736095/article/details/51480546

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">自定义ViewGroup——圆形排列LinearLayout</span>


官方的控件远远无法满足客户的需求了,很多时候需要自己去定义需要的View和ViewGroup。前段时间因为公司的项目非常赶,自己写好的东西都没有时间记录下来,现在稍微好点,就在博客这里留下点足迹。之前一直对onMeasure(),onLayout(),onDraw()这几个方法似懂非懂的,如今顺便复习一下。


如题,我们需要实现一个将ViewGroup内的ChildView进行环形平均排列,效果如下:



自定义ViewGroup步骤:

1、attr.xml自定义属性

2、自定义ViewGroup,并在构造方法中取得属性

3、复写测量控件大小方法onMeasure(),复写测量摆放ChildView方法onLayout()

4、在布局中使用自定义ViewGroup,并使用自己的自定义属性(

在根布局中声命名空间 xmlns:kubyattr="http://schemas.android.com/apk/res/com.happy.myfragmentapplication"

并使用happyattr:circleRadius="70dp"


下面上代码:

1、attr.xml自定义属性

  <declare-styleable name="CircleLinearLayout">
        <attr name="circleRadius"/>
    </declare-styleable>


2、自定义ViewGroup,并在构造方法中取得属性

3、复写测量控件大小方法onMeasure(),复写测量摆放ChildView方法onLayout()
package com.happy.myfragmentapplication.customview;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import com.happy.myfragmentapplication.R;
import com.happy.myfragmentapplication.Utils.MyMath;

import java.util.List;

/**
 * Created by Kuby on 2016/5/20.
 */
public class CircleLinearLayout extends ViewGroup{

    private final String TAG = "CircleLinearLayout";

    private int radius;

    public CircleLinearLayout(Context context) {
        super(context);
    }

    public CircleLinearLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CircleLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        TypedArray typedArray = getContext().getTheme().obtainStyledAttributes(attrs, R.styleable.CircleLinearLayout,defStyleAttr,0);
        int count = typedArray.getIndexCount();
        for (int i = 0; i<count; i++){
            int attNameId = typedArray.getIndex(i);
            switch (attNameId){
                case R.styleable.CircleLinearLayout_circleRadius:
                    radius = typedArray.getDimensionPixelSize(attNameId,10);
                    break;
            }
        }

        typedArray.recycle();
        Log.d(TAG, "radius = " + radius);
    }

    @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
//        return super.generateLayoutParams(attrs);
        return new MarginLayoutParams(getContext(),attrs);
    }

    /**
     * 计算所有ChildView的宽度和高度,然后根据ChildView的计算结果设置自己的宽度和高度
     * @param widthMeasureSpec
     * @param heightMeasureSpec
     */
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        /**
         * 获取此ViewGroup上级容器为其推荐计算模式
         */
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        /**
         * 获取此ViewGroup上级容器为其推荐的宽和高
         */
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        //计算出所有childView的宽和高,(通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,此行执行完成后,childView的宽和高都已经正确的计算过了)
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        /**
         * ViewGroup内子控件的宽度和高度
         */
        int widthContent = 0;
        int heightContent = 0;

        int itemHeight =getChildAt(0).getMeasuredHeight();//单个childView的高度

        heightContent = (itemHeight+radius)*2;
        widthContent = (itemHeight+radius)*2;
        Log.d(TAG + "onMeasure", "heightContent:"+heightContent);

        /**
         * 测量ViewGroup的宽高,如果为wrap_content就按照内容计算得到的宽高
         */
        setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? widthSize : widthContent, (heightMode == MeasureSpec.EXACTLY) ? heightSize : heightContent);

    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
//        drawInHorizontal();
        drawInCircle();

    }

    /**
     * 按照Circle的方式排列
     */
    private void drawInCircle() {
        int cCount = getChildCount();
        int lastW = 0;

        //圆心坐标
        float[] circleCentre = {getWidth()/2*1.0f, getHeight()/2*1.0f};

        //每个占多少个弧度
//        float oItem = 360/cCount*1.0f;
        float oItem = (float) (2*Math.PI/cCount*1.0f);

        //cCount个坐标
        float[][] xyPosition = new float[cCount][2];
        for (int i=0; i<cCount; i++)
        {
            xyPosition[i] = MyMath.getXYPoint(circleCentre,radius,oItem*(i));

            //x坐标
            int xLabel = (int) xyPosition[i][0];
            //y坐标
              int yLabel = (int) xyPosition[i][1];

            Log.d(TAG, "position : (" + xLabel + "," + yLabel + ")");
            View view = getChildAt(i);
            view.layout((int) (xLabel - view.getMeasuredWidth() / 2 * 1.0f), (int) (yLabel - view.getMeasuredHeight() / 2 * 1.0f), (int) (xLabel + view.getMeasuredWidth() / 2 * 1.0f), (int) (yLabel + view.getMeasuredHeight() / 2 * 1.0f));

        }

    }

    /**
     * 按照horizontal的方式排列
     */
    private void drawInHorizontal() {
        int cCount = getChildCount();
        int lastW = 0;
        for (int i=0; i < cCount; i++){
            View view = getChildAt(i);
            view.layout(lastW,0,view.getWidth(),view.getHeight());
            lastW += view.getWidth();
            Log.i(TAG, "lastW = " + lastW);
        }
    }
}

还有一个计算坐标的工具类

package com.happy.myfragmentapplication.Utils;

import android.util.Log;

/**
 * Created by Kuby on 2016/5/20.
 */
public class MyMath {

    private final static String TAG = "MyMath";

    /**
     * 以原点为圆点,以radius维半径画圆,通过弧度o,获得坐标
     * @param radius 半径
     * @param o 弧度
     * @return
     */
    public static float[] getXYPoint(float[] centrePoint, int radius, float o){
        Log.d(TAG,"o: "+o);
        Log.d(TAG,"radius: "+radius);
        Log.d(TAG,"centrePoint: ["+centrePoint[0]+","+centrePoint[1]+"]");
        float[] xyPoint = {(float) (radius*Math.sin(o) + centrePoint[0]), (float) ((-1)*radius*Math.cos(o) + centrePoint[1])};
//        Log.d(TAG,"test: ["+xyPoint[0]+","+xyPoint[1]+"]");
        return xyPoint;
    }
}

4、在布局中使用自定义ViewGroup,并使用自己的自定义属性

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:happyattr="http://schemas.android.com/apk/res/com.happy.myfragmentapplication"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.happy.myfragmentapplication.FragmentContainer1">

    <!-- TODO: Update blank fragment layout -->
    <TextView android:layout_width="match_parent" android:layout_height="match_parent"
        android:text="Fragment Container 2" />
    <RelativeLayout
        android:id="@+id/fragment_container"
        android:background="@android:color/holo_green_dark"
        android:layout_margin="20dp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
       
        <com.happy.myfragmentapplication.customview.CircleLinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/customvolum"
            happyattr:circleRadius="70dp"
            >

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tv1"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tv2"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tv3"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tv4"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tv5"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tv6"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tv7"/>

            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tv8"/>

        </com.happy.myfragmentapplication.customview.CircleLinearLayout>
    </RelativeLayout>

</FrameLayout>



猜你喜欢

转载自blog.csdn.net/u014736095/article/details/51480546