Android Custom View系列《圓形菜單一》


前言

自定義view能夠做出很多不同尋常的效果,圓形菜單交互效果不錯,目前網上有兩個版本,雖然比較龐大,但非常值得研究與學習。

radial-menu-widget: https://code.google.com/p/radial-menu-widget/

Radial-Menu-Widget-Android:https://github.com/strider2023/Radial-Menu-Widget-Android

這兩個版本呢實際上第一個是最原始的作者Jason Valestin,后來被Arindam Nath修改后出現了后面的版本。在分析過程中可以逐個擊破,關鍵在於理解要點,本文講自定義一個圓形的view作為自定義圓形菜單的一個入門基礎,暫且命名為CircleView,點擊后變化顏色。

源碼鏈接: https://github.com/avenwu/RadialDemo/tree/draw_circle

實現

如果要自定義一個圓形的菜單,那么現有的LinearLayout或者RelativeLayout等都已經無法滿足,我們需要從View直接繼承,由於需要處理觸摸事件,所以需要重載onTouchEvent(),來根據當前觸摸的坐標額動作狀態調整view

,除此之外需要判斷點擊的位置出於菜單的可是區域內,在Android中所有的view本質上是矩形區域的,所以需要通過數學計算來判斷當前是否點中了菜單,現在我們對這兩個關鍵做類似的實現。

1.初始化畫筆菜單位置及大小

在合適的地方初始化資源是很重要的,對畫筆和菜單半徑等我們可以在構造方法內賦值,


201401101751.jpg

繪制一個圓還需要中心坐標,這個值我們在view的大小確定時初始化,不要嘗試直接獲取view的高寬,這兩個值必須在view繪制后才拿的到,這里我們在onMeasure和onSizeChanged都可以得到view的大小。


201401101752.jpg

2.通過畫布繪制菜單

繪制一個圓形比較簡單,直接調用canvas的drawCircle方法


201401101754.jpg

3.處理觸摸事件

重載onTouchEvent方法我們監聽到view上面的觸摸動作,一般來說ACTION_DOWN, ACTION_UP, ACTION_MOVE都是比較重要的

我們在點擊view的時候更改畫筆的顏色為粉紅色,當手離開時改回默認的紅色,所以可以在ACTION_DOWN時設置color為Color.MAGENTA, 在ACTION_UP時設置為Color.RED

另外在我們按住屏幕后移動位置,這是有可能會移除菜單區域,所以在ACTION_MOVE時我們也需要設置


201401101800.jpg

4.計算觸摸位置是否在菜單內

這一步將直接影響我們的觸摸效果,這里我們的區域是圓形,所以比較好處理,只需計算觸摸點到圓心的距離就可以知道相對位置。


201401101803.jpg

后面當單一圓形菜單分割為多個菜單項時,位置計算會復雜一些。

完整代碼:

package com.avenwu.radialdemo;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

/**
 * @author chaobin
 * @date 1/10/14.
 */
public class CircleView extends View {
    private float mCenterX;
    private float mCenterY;
    private float mRadius;
    private Paint mPaint;

    public CircleView(Context context) {
        this(context, null);
    }

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

    public CircleView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mRadius = getContext().getResources().getDisplayMetrics().density * 100;
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setColor(Color.RED);
        mPaint.setStrokeWidth(getContext().getResources().getDisplayMetrics().density * 5);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        canvas.drawCircle(mCenterX, mCenterY, mRadius, mPaint);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec) / 2;
        int height = MeasureSpec.getSize(heightMeasureSpec) / 2;
        Log.d("CircleView", "onMeasure, height=" + height + ", width=" + width);
        updateCenter(width, height);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        Log.d("CircleView", "onSizeChanged: w=" + w + ", h=" + h);
        updateCenter(w, h);
    }

    void updateCenter(int x, int y) {
        mCenterX = x / 2;
        mCenterY = y / 2;
    }

    boolean isInside(float x, float y) {
        return Math.pow(x - mCenterX, 2) + Math.pow(y - mCenterY, 2)
                <= Math.pow(mCenterX - getPaddingLeft(), 2);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (isInside(event.getX(), event.getY())) {
                    mPaint.setColor(Color.MAGENTA);
                }
                break;
            case MotionEvent.ACTION_UP:
                mPaint.setColor(Color.RED);
                break;
            case MotionEvent.ACTION_MOVE:
                if (!isInside(event.getX(), event.getY())) {
                    mPaint.setColor(Color.RED);
                }
                Log.d("CircleView", "position, x=" + event.getX() + ", y=" + event.getY());
                break;
        }
        invalidate();
        return true;
    }

}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM