仿八大行星繞太陽3D旋轉效果

android實現八大行星繞太陽3D旋轉效果
仿上面效果,採用kotlin實現,邏輯要簡單些,註釋在源碼中,一看就懂

在這裏插入圖片描述

<com.example.androidxdemo.star.StarGroupView
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" >

        <!-- 增加太陽View -->
        <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_launcher_background"
            android:tag="center" />

        <TextView
            android:id="@+id/tv1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent"
            android:text="1" />

        <TextView
            android:id="@+id/tv2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/darker_gray"
            android:gravity="center"
            android:text="2" />

        <TextView
            android:id="@+id/tv3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_green_dark"
            android:gravity="center"
            android:text="3" />

        <TextView
            android:id="@+id/tv4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_blue_dark"
            android:gravity="center"
            android:text="4" />

        <TextView
            android:id="@+id/tv5"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_green_light"
            android:gravity="center"
            android:text="5" />

        <TextView
            android:id="@+id/tv6"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@android:color/holo_orange_light"
            android:gravity="center"
            android:text="6" />

        <TextView
            android:id="@+id/tv7"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ff3311"
            android:gravity="center"
            android:text="7" />

        <TextView
            android:id="@+id/tv8"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#11aa44"
            android:gravity="center"
            android:text="8" />

        <TextView
            android:id="@+id/tv9"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="#ff99cc"
            android:gravity="center"
            android:text="9" />
    </com.example.androidxdemo.star.StarGroupView>
class StarGroupView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {

    val TAG_CENTER = "center"

    //控件寬高
    var mWidth = 0
    var mHeight = 0
    //中心點
    var centerWidth = 0
    var centerHeight = 0
    private var mRadius = 0
    //子View的大小,默認是1:1正方形
    private var childWidth = 0
    private var childHeight = 0
    //初始角度,掃過的角度
    var sweepAngle = 90

    //值越大,轉動越快
    val changedAngle = 2
    //延時delayTime ms,進行一次值的改變
    val delayTime = 100L

    //手指按下時x和角度
    private var downX = 0f
    private var downAngle = 0
    //延時改變view位置和繪製順序
    private val autoScrollRunnable = object : Runnable {
        override fun run() {
            sweepAngle = (sweepAngle + changedAngle) % 360
            layoutChildren()
            postDelayed(this, delayTime)
        }
    }

    init {
        postDelayed(autoScrollRunnable, delayTime)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
//        "w: $w h: $h oldw: $oldw oldh: $oldh".log()
        //如果寬度大於兩倍高度,則高度決定寬度,寬度 = 2 * 高度
        //否則,寬度小於等於兩倍高度,則寬度決定高度,高度 = 1/2 * 寬度
        if (w > h * 2) {
            mHeight = h
            mWidth = mHeight * 2
        } else {
            mWidth = w
            mHeight = mWidth / 2
        }
        //確定中心點的位置,childWidth = heightWidth = mWidth / 6
        centerWidth = mWidth / 2
        centerHeight = mHeight / 2
        childWidth = mWidth / 6
        childHeight = childWidth
        //半徑,
        mRadius = mHeight - childWidth
        "w: $w h: $h oldw: $oldw oldh: $oldh mWidth: $mWidth mHeight: $mHeight mRadius: $mRadius childWidth: $childWidth".log()
    }

    override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
        super.onLayout(changed, left, top, right, bottom)
//        "changed $changed left: $left top: $top right: $right bottom: $bottom".log()
        layoutChildren()
    }

    /**
     * 計算view的位置
     * z值範圍(0-1)
     * 改變子View的z值以改變子View的繪製優先級,z越大優先級越低(最後繪製)
     */
    private fun layoutChildren() {
        val centerView = findViewWithTag<View>(TAG_CENTER)
        val degree: Float
        if (centerView == null) {
            degree = 360f / childCount
        } else {
            //中心點view的寬高爲 2 * childWith
            degree = 360f / (childCount - 1)
            centerView.layout(
                centerWidth - childWidth, centerHeight - childHeight,
                centerWidth + childWidth, centerHeight + childHeight
            )
        }
        for (index in 0 until childCount) {
            val child = getChildAt(index)
            if (child.tag == TAG_CENTER) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    child.z = 0.5f
                }
                child.rotation = sweepAngle.toFloat()
//                "中心旋轉 rotation: $sweepAngle".log()
                continue
            }
            //設置child的大小尺寸和佈局, index爲0的是centerView
            val radius = (degree * index + sweepAngle) * Math.PI / 180
            val childCenterX = (mRadius * cos(radius)).toInt()
            val childCenterY = (mRadius * sin(radius) / 2).toInt()
//            "index:$index degree:${degree * (index - 1)} radius:$radius childCenterX: $childCenterX childCenterY: $childCenterY".log()
            val left = childCenterX - childWidth / 2 + centerWidth
            val top = childCenterY - childHeight / 2 + centerHeight
            val right = childCenterX + childWidth / 2 + centerWidth
            val bottom = childCenterY + childHeight / 2 + centerHeight
            child.layout(left, top, right, bottom)
            val scale = ((sin(radius) + 2) / 3f).toFloat()
            child.scaleX = scale
            child.scaleY = scale
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                child.z = scale
            }
//            "index:$index child left: $left top: $top right: $right bottom: $bottom child.scaleX: ${child.scaleX}".logW()
        }
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                downX = event.x
                downAngle = sweepAngle
                removeCallbacks(autoScrollRunnable)
                "actionDown downX $downX downAngle: $downAngle ".logW()
                return true
            }
            MotionEvent.ACTION_MOVE -> {
                val dx = event.x - downX
                if (dx != 0f) {
                    sweepAngle = (dx * 0.2 + downAngle).toInt()
                    "actionMove dx $dx sweepAngle: $sweepAngle ".log()
                    layoutChildren()
                }
            }
            MotionEvent.ACTION_UP -> {
                "actionUp ".logW()
                postDelayed(autoScrollRunnable, 16)
            }
        }
        return super.onTouchEvent(event)
    }

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章