Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android 自定義View彎曲滑竿指示器

android 自定義View彎曲滑竿指示器

編輯:關於Android編程

效果說明:滑竿指示器,是一段彎曲的圓弧,要求在桿上,有滑動小球事件,小球會根據下標文字的起始角度與終止角度,是否選擇滑倒下一個位置。當點擊下標文字時,小球也要做出相應的指示。

1)MainActivity

package com.example.chenkui.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        intIndicatorTabView();

//        setContentView(R.layout.watch_view);

    }
    private void intIndicatorTabView() {
        IndicatorTabView view = (IndicatorTabView) findViewById(R.id.myView);
        List list = new ArrayList<>();
        list.add("待評價");
        list.add("待付款");
        list.add("待發貨");
        list.add("待收貨");
        view.setTabInfo(list);
        view.setSelection(3);//初始化選擇指示器小球位置;
        view.setTabChangeListener(new IndicatorTabView.OnTabChangeListener() {
            @Override
            public void onTabSelected(View v, int position) {

            }
        });
    }
}

2)IndicatorTabView
在繪制時,按照圖層的結構,先繪制底層顏色,再繪制上一層圖形,對於特殊繪制的,如旋轉畫布,移動繪制圓心坐標,一般先 canvas.save();保存已經繪制圖層,canvas.restore();//他的作用為,將之前的繪制保存的圖片save(),進行合並.

package com.example.chenkui.myapplication;

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

public class IndicatorTabView extends View {
    private String TAG = IndicatorTabView.class.getSimpleName();
    private static final float DEFAULT_SWEEP_ANGLE = 48.0f;
    private float mSweepAngle = DEFAULT_SWEEP_ANGLE;
    private float mStartAngle = (180.0f - mSweepAngle) / 2;
    private List mTabItems = new ArrayList<>();
    private int mSelectTabIndex = -1;
    private Paint mTabBackColorPaint;
    private Paint mTabPaint;
    private Paint mTabTtileTextPaint;//滑竿或點擊標題

    private Paint mTabWheelPaint;
    private Paint mTabTextPaint;
    private Paint mTabPointerPaint;

    private float mWheelCenterX;
    private float mWheelCenterY;
    private float mWheelRadius;
    private RectF mWheelArcRect;

    private float mPointerAngle;
    private float mPointerRadius;
    private boolean mIsMovingPointer = false;
    private OnTabChangeListener mTabChangeListener = null;

    private List mTabRectItem = new ArrayList();
    private Paint textPaint = new Paint();
    private float mMinRectRadius;//文字區域,
    private float mMaxRectRadius;//文字區域,


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

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

    public IndicatorTabView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    private void initView() {
        mTabBackColorPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制最大的裡邊圖層背景
        mTabBackColorPaint.setStyle(Paint.Style.FILL);
        mTabBackColorPaint.setColor(Color.argb(255, 35, 47, 62));

        mTabPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制裡面最小圖層背景。
        mTabPaint.setStyle(Paint.Style.FILL);
        mTabPaint.setColor(Color.argb(255, 253,253, 254));

        mTabTtileTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制裡面最小圖層背景。
        mTabTtileTextPaint.setStyle(Paint.Style.FILL);
        mTabTtileTextPaint.setColor(Color.WHITE);

        mTabWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mTabWheelPaint.setStyle(Paint.Style.STROKE);
        mTabWheelPaint.setStrokeWidth(getResources().getDimension(R.dimen.tab_wheel_width));
        mTabWheelPaint.setColor(Color.argb(200, 253, 250, 245));
        mTabWheelPaint.setStrokeCap(Paint.Cap.ROUND);//這個是設置繪制弧是,兩端圓滑;

        mTabTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制文字
        mTabTextPaint.setColor(Color.argb(255, 253, 253, 254));
        mTabTextPaint.setTextSize(getResources().getDimension(R.dimen.tab_text));
        mTabTextPaint.setStrokeWidth(5);

        mTabPointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//繪制小點
        mTabPointerPaint.setStyle(Paint.Style.FILL);
        mTabPointerPaint.setColor(Color.argb(255, 255, 255, 255));
        mPointerRadius = getResources().getDimension(R.dimen.tab_pointer_radius);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //繪制深藍背景。
        canvas.drawCircle(mWheelCenterX, mWheelCenterY - getResources().getDimension(R.dimen.tab_wheel_padding_inner), mWheelRadius*1.2f, mTabBackColorPaint);

        //繪制裡面第一條弧
        canvas.drawCircle(mWheelCenterX, mWheelCenterY - getResources().getDimension(R.dimen.tab_wheel_padding_inner), mWheelRadius, mTabPaint);

        canvas.drawArc(mWheelArcRect, mStartAngle, mSweepAngle, false, mTabWheelPaint);//繪制滑桿

        for (int i = 0; i < mTabItems.size(); i++) {
            IndicatorTabItem tempItem = mTabItems.get(i);
            float angle = (tempItem.getStartAngle() + tempItem.getEndAngle()) / 2 - 90.0f;
            canvas.save();//將之前繪制圖片保存起來,
            canvas.rotate(angle, mWheelCenterX, mWheelCenterY);
            canvas.drawText(tempItem.getName(), mWheelCenterX - tempItem.getMesureWidth() / 2,
                    getResources().getDimension(R.dimen.tab_wheel_width) + mWheelCenterY + mWheelRadius + getResources().getDimension(R.dimen.tab_wheel_padding_inner),
                    mTabTextPaint);
            /***********************************************************************************************************/
            Log.d(TAG, "-----------onDraw()-----------" + "X===[" + (mWheelCenterX - tempItem.getMesureWidth() / 2) + "]-------Y==={" + (getResources().getDimension(R.dimen.tab_wheel_width) + mWheelCenterY + mWheelRadius + getResources().getDimension(R.dimen.tab_wheel_padding_inner)) + "}");

            float rectWidth = mTabTextPaint.measureText(tempItem.getName());
            Paint.FontMetrics fm = mTabTextPaint.getFontMetrics();

            float offsetAscent = fm.ascent;
            float offsetBottom = fm.bottom;
            float startX = mWheelCenterX - tempItem.getMesureWidth() / 2;
            float startY = getResources().getDimension(R.dimen.tab_wheel_width) + mWheelCenterY + mWheelRadius + getResources().getDimension(R.dimen.tab_wheel_padding_inner);
            Log.d(TAG, "TEXT-offsetAscent=" + offsetAscent);
//            RectF testRect = new RectF(
//                    startX,
//                    (float) (startY + offsetAscent),
//                    (float) (startX + rectWidth),
//                    (float) (startY + offsetBottom)
//            );
//            textPaint.setColor(Color.argb(100, 233, 233, 0));
//            canvas.drawRect(testRect, textPaint);
/*************************************************************************************************************************/
            canvas.restore();//他的作用為,將之前的繪制保存的圖片save(),進行合並.

            mMinRectRadius = distance(mWheelCenterX, startY + offsetAscent);//計算
            mMaxRectRadius = distance(mWheelCenterX, startY + offsetBottom);


        }

        float[] pointerPosition = calculatePointerPosition(mPointerAngle);
        canvas.drawCircle(mWheelCenterX + pointerPosition[0], mWheelCenterY + pointerPosition[1], mPointerRadius, mTabPointerPaint);//繪制小球
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        mWheelRadius = (float) (widthSize / (2.0f * Math.sin(Math.toRadians(32))));

        int offset = (int) (heightSize - getResources().getDimension(R.dimen.tab_wheel_padding_bottom));

        mWheelCenterY = offset - (int) Math.sqrt(Math.pow((double) mWheelRadius, 2) - Math.pow((double) (widthSize / 2), 2));

        mWheelCenterX = widthSize / 2.0f;

        mWheelArcRect = new RectF(mWheelCenterX - mWheelRadius, mWheelCenterY - mWheelRadius,
                mWheelCenterX + mWheelRadius, mWheelCenterY + mWheelRadius);

    }

    private float[] calculatePointerPosition(float angle) {
        float x = (float) (mWheelRadius * Math.cos(Math.toRadians(angle)));
        float y = (float) (mWheelRadius * Math.sin(Math.toRadians(angle)));

        return new float[]{x, y};
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float x = event.getX() - mWheelCenterX;
        float y = event.getY() - mWheelCenterY;

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                float[] pointerPosition = calculatePointerPosition(mPointerAngle);
                if (x >= (pointerPosition[0] - mPointerRadius * 2)
                        && x <= (pointerPosition[0] + mPointerRadius * 2)
                        && y >= (pointerPosition[1] - mPointerRadius * 2)
                        && y <= (pointerPosition[1] + mPointerRadius * 2)) {
                    mIsMovingPointer = true;
                    return true;
                }
                float pointerLength = distanceRelative(x, y);//計算觸摸點距離圓心的坐標:
                //計算文本觸摸區域的頂部距離圓心的坐標的距離:
                //計算文本觸摸區域的底部距離圓心的坐標的距離;
                //計算觸摸點的角度,

                float tempPointerRectFAngle = (float) Math.toDegrees(Math.atan2(y, x));//文本觸摸區域的的角度
                if (pointerLength >= mMinRectRadius - mPointerRadius && pointerLength <= mMaxRectRadius + mPointerRadius) {
                    int willSelectedIndex = -1;
                    for (int i = 0; i < mTabRectItem.size(); i++) {
                        IndicatorTabRectItem item = mTabRectItem.get(i);
                        if (tempPointerRectFAngle >= item.getStartAngle() && tempPointerRectFAngle <= item.getEndAngle()) {
                            willSelectedIndex = i;
                            break;
                        }
                    }
                    if (mSelectTabIndex != willSelectedIndex) {
                        if (mTabChangeListener != null) {
                            mTabChangeListener.onTabSelected(this, willSelectedIndex);
                        }
                    }
                    setSelection(willSelectedIndex);
                    invalidate();
                    return true;
                }


                break;
            case MotionEvent.ACTION_MOVE:
                if (mIsMovingPointer) {
                    float tempPointerAngle = (float) Math.toDegrees(Math.atan2(y, x));
                    if (tempPointerAngle >= mTabItems.get(0).getStartAngle()
                            && tempPointerAngle <= mTabItems.get(mTabItems.size() - 1).getEndAngle()) {
                        mPointerAngle = tempPointerAngle;
                        invalidate();
                    }

                    return true;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mIsMovingPointer) {
                    mIsMovingPointer = false;
                    smoothMove();
                    return true;
                }
                break;
        }

        return false;
    }

    /**
     * 根據觸摸點距離圓心的距離,與每一塊的活動角度,確定惟一的文本觸摸塊。
     */
//    private void calculateTouchRect(float x, float y) {
////        =x-mWheelCenterX;
//        float dpow2 = (float) (Math.pow((x - mWheelCenterX), 2) + Math.pow((y - mWheelCenterY), 2));
//        float d = (float) Math.sqrt(dpow2);
//        Log.d(TAG, "TouchRect---d=" + d);
//        Log.d(TAG, "mMinRectRadius====" + mMinRectRadius);
//        Log.d(TAG, "mMAXRectRadius====" + mMaxRectRadius);
//
//
//    }

    /**
     * 相對與(x-mWheelCenterX,y-mWheelCenterY)坐標,
     * 計算觸摸點。求兩點間距離,此時的圓心為
     */
    public float distanceRelative(float x, float y) {
        float dx = Math.abs(x);
        float dy = Math.abs(y);
        Log.d(TAG, "distance---d=" + (float) Math.hypot(dx, dy));
        Log.d(TAG, "mMinRectRadius====" + mMinRectRadius);
        Log.d(TAG, "mMAXRectRadius====" + mMaxRectRadius);
        return (float) Math.hypot(dx, dy);
    }

    /**
     * 計算觸摸點。求兩點間距離
     */
    public float distance(float x, float y) {
        float dx = Math.abs(x - mWheelCenterX);
        float dy = Math.abs(y - mWheelCenterY);
        Log.d(TAG, "distance---d=" + (float) Math.hypot(dx, dy));
        Log.d(TAG, "mMinRectRadius====" + mMinRectRadius);
        Log.d(TAG, "mMAXRectRadius====" + mMaxRectRadius);
        return (float) Math.hypot(dx, dy);
    }


    private void smoothMove() {
        int willSelectedIndex = -1;
        for (int i = 0; i < mTabItems.size(); i++) {
            IndicatorTabItem item = mTabItems.get(i);
            if (mPointerAngle >= item.getStartAngle() && mPointerAngle <= item.getEndAngle()) {
                willSelectedIndex = i;
                break;
            }
        }

        if (mSelectTabIndex != willSelectedIndex) {
            if (mTabChangeListener != null) {
                mTabChangeListener.onTabSelected(this, willSelectedIndex);
            }
        }

        setSelection(willSelectedIndex);
    }

    public void setTabInfo(List tabInfo) {
        if (tabInfo == null && (tabInfo.size() == 0 && tabInfo.size() > 4)) {
            return;
        }

        float totalPercent = 0.0f;
        for (int i = 0; i < tabInfo.size(); i++) {
            IndicatorTabItem item = new IndicatorTabItem();
            item.setName(tabInfo.get(i));
            item.setMesureWidth(mTabTextPaint.measureText(item.getName()));

            Log.d(TAG, "--------setTabInfo()-------" + item.getName());
            totalPercent += item.getMesureWidth();
            mTabItems.add(item);
        }

        float startAngle = mStartAngle;
        for (int i = 0; i < mTabItems.size(); i++) {
            IndicatorTabItem tempItem = mTabItems.get(i);
            float itemSweepAngle = mSweepAngle * tempItem.getMesureWidth() / totalPercent;
            tempItem.setStartAngle(startAngle);
            tempItem.setEndAngle(startAngle + itemSweepAngle);
            startAngle += itemSweepAngle;
            Log.d(TAG, "startAngle" + i + "======" + startAngle);
            Log.d(TAG, "EndAngle" + i + "======" + (startAngle + itemSweepAngle));
        }
        setSelection(0);

        initIndicatorTabRectItem(tabInfo);
    }


    private void initIndicatorTabRectItem(List tabInfo) {
        if (tabInfo == null && (tabInfo.size() == 0 && tabInfo.size() > 4)) {
            return;
        }
        float totalPercent = 0.0f;
        for (int i = 0; i < tabInfo.size(); i++) {
            IndicatorTabRectItem rectItem = new IndicatorTabRectItem();
            rectItem.setRectName(tabInfo.get(i));
            rectItem.setRectWidth(mTabTextPaint.measureText(rectItem.getRectName()));
            totalPercent += rectItem.getRectWidth();
            Log.d(TAG, "----initIndicatorTabRectItem--------setRectWidth" + i + "======" + mTabTextPaint.measureText(rectItem.getRectName()));
            mTabRectItem.add(rectItem);
        }
        float startAngle = mStartAngle;

        for (int i = 0; i < mTabRectItem.size(); i++) {
            IndicatorTabRectItem tempItem = mTabRectItem.get(i);
            float itemSweepAngle = mSweepAngle * tempItem.getRectWidth() / totalPercent;

            tempItem.setStartAngle(startAngle);
            tempItem.setEndAngle(startAngle + itemSweepAngle);
            startAngle += itemSweepAngle;
            Log.d(TAG, "----initIndicatorTabRectItem-------startAngle" + i + "======" + startAngle);
            Log.d(TAG, "----initIndicatorTabRectItem-------EndAngle" + i + "======" + (startAngle + itemSweepAngle));
        }
    }

    /**
     * 設置選擇小點的每一段中心點角度。
     *
     * @param index
     */
    public void setSelection(int index) {
        if (index < 0 || index > mTabItems.size() - 1) {
            return;
        }

        mSelectTabIndex = index;
        mPointerAngle = (mTabItems.get(mSelectTabIndex).getStartAngle() + mTabItems.get(mSelectTabIndex).getEndAngle()) / 2;
        invalidate();
    }

    public void setTabChangeListener(OnTabChangeListener tabChangeListener) {
        this.mTabChangeListener = tabChangeListener;
    }

    public interface OnTabChangeListener {
        void onTabSelected(View v, int position);
    }
}

3)IndicatorTabRectItem

package com.example.chenkui.myapplication;

/**
 * Created by chenkui on 2016/10/11.
 */
public class IndicatorTabRectItem {
    private float startX;
    private float endX;
    private float startY;
    private float endY;
    private float rectWidth;
    private float rectHeigth;
    private  String rectName;

    private float startAngle;
    private float endAngle;


    public float getStartX() {
        return startX;
    }

    public void setStartX(float startX) {
        this.startX = startX;
    }

    public float getEndX() {
        return endX;
    }

    public void setEndX(float endX) {
        this.endX = endX;
    }

    public float getStartY() {
        return startY;
    }

    public void setStartY(float startY) {
        this.startY = startY;
    }

    public float getEndY() {
        return endY;
    }

    public void setEndY(float endY) {
        this.endY = endY;
    }

    public float getRectWidth() {
        return rectWidth;
    }

    public void setRectWidth(float rectWidth) {
        this.rectWidth = rectWidth;
    }

    public float getRectHeigth() {
        return rectHeigth;
    }

    public void setRectHeigth(float rectHeigth) {
        this.rectHeigth = rectHeigth;
    }

    public String getRectName() {
        return rectName;
    }

    public void setRectName(String rectName) {
        this.rectName = rectName;
    }

    public float getStartAngle() {
        return startAngle;
    }

    public void setStartAngle(float startAngle) {
        this.startAngle = startAngle;
    }

    public float getEndAngle() {
        return endAngle;
    }

    public void setEndAngle(float endAngle) {
        this.endAngle = endAngle;
    }
}

4)IndicatorTabItem


package com.example.chenkui.myapplication;


public class IndicatorTabItem  {

    private String name;
    private float mesureWidth;
    private float startAngle;
    private float endAngle;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public float getMesureWidth() {
        return mesureWidth;
    }

    public void setMesureWidth(float mesureWidth) {
        this.mesureWidth = mesureWidth;
    }

    public float getStartAngle() {
        return startAngle;
    }

    public void setStartAngle(float startAngle) {
        this.startAngle = startAngle;
    }

    public float getEndAngle() {
        return endAngle;
    }

    public void setEndAngle(float endAngle) {
        this.endAngle = endAngle;
    }
}

5)activity_main.xml





    
    


dimen.xml


    
    16dp
    10dp
    130dp
    30dp
    20dp
    10dp

效果圖如下:
這裡寫圖片描述

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved