新手學(xué)自定義View系列(一)Demo

簡述:

  1. 為什么要另寫這篇Demo博客?

上篇博客最后給出了一個折線圖的例子兽狭,記得當(dāng)時是說下篇博客給出其源碼腊尚,但是后來我又想了下,咱們既然是新手系列的自定義View庵佣,內(nèi)容就需要做到詳細(xì)歉胶,清楚和明白。如果隨意丟出一坨源碼對于新手來說太坑爹了巴粪,我們每一位老司機(jī)都是從新手走過來通今,相信大家對于動不動就扔出一坨代碼博主,心中很是無奈肛根。今天咱們就簡單分析下這個demo辫塌。

  1. 回答上篇博客的問題?

上篇博客說到如何在一個自定義View中對某個特定區(qū)域?qū)崿F(xiàn)點(diǎn)擊觸摸反饋派哲。我們都知道對于View獨(dú)立單一組件唯一可以設(shè)置點(diǎn)擊的就是對整個View設(shè)置點(diǎn)擊事件臼氨。對于一個View控件的某個區(qū)域也能實(shí)現(xiàn)點(diǎn)擊觸摸反饋嗎?答案是可以的芭届,本期的例子柱狀圖也是只有點(diǎn)擊矩形才會有事件觸發(fā)储矩,而不是整個View控件。這實(shí)際上就是用到了一個特殊的API的Region區(qū)域褂乍。它可以框出某個VIew的某個特定的區(qū)域持隧,然后在View的OnTouchEvent事件中,監(jiān)聽手指按下和抬起點(diǎn)是否落入對應(yīng)的Region區(qū)域逃片,如果落入就會給出回調(diào)屡拨,就這么簡單粗暴。

  1. 自定義View直方圖思路分析?
m3.png
  1. 首先從整個控件角度出發(fā)洁仗,任何控件實(shí)現(xiàn)都得包括了數(shù)據(jù)接口定義和UI渲染繪制兩個方面。

  2. 對于數(shù)據(jù)接口定義性锭,從本控件可以看到就兩個數(shù)據(jù)因素赠潦,一是類型對應(yīng)的名稱(String),二是對應(yīng)類型占最大值得比例(float),用比例更加直觀反映出繪制的高度。兩個數(shù)據(jù)因素草冈,很多人認(rèn)為寫個類包一下她奥,個人感覺對于View層直接渲染數(shù)據(jù)越是簡單數(shù)據(jù)類型,它的通用性越強(qiáng)怎棱。所以我想到的時候通過外部傳入一個List < Pair < String, float> >即可哩俭。

  3. 對于UI渲染繪制,考慮有兩個方面靜態(tài)繪制和點(diǎn)擊交互拳恋。

  4. 對于靜態(tài)繪制可以分為純直方圖的繪制和折線圖繪制凡资。

  5. 直方圖的繪制包括坐標(biāo)系繪制、文字集合繪制谬运、矩形集合繪制隙赁。

  6. 對于坐標(biāo)系繪制為了計算繪制坐標(biāo)方便,將原來默認(rèn)為View左上角的坐標(biāo)原點(diǎn)梆暖,通過translate位移canvas畫布正好移動到直方圖坐標(biāo)的原點(diǎn)位置伞访。 文字的繪制就是上期博客講的,不過是文字集合通過一個循環(huán)來繪制轰驳,矩形集合的繪制使用的是循環(huán) + path.addRect方式厚掷,最后繪制整個Path就OK。

  7. 折線圖的繪制包括點(diǎn)的集合繪制级解、線段的集合繪制冒黑。

  8. 點(diǎn)集合繪制采用的是drawPoints繪制多個點(diǎn),線段集合繪制采用drawLines繪制多條線蠕趁。

  9. 對于點(diǎn)擊交互就比較麻煩點(diǎn)薛闪,思路是定義一個和View控件畫布一樣大小的Region,姑且定義為globalRegion.然后針對每一個矩形都定義一個Path和Region.把每個矩形通過path.addRect加入對應(yīng)的Path中,通過region.setPath(path, globalRegion)將path區(qū)域和clip區(qū)域取交集俺陋,捕獲出對應(yīng)path的region區(qū)域坐標(biāo)范圍豁延。最后把每個path添加到全局的mPath中,把每個region區(qū)域加入mRegionList集合中腊状。

  10. 通過重寫onTouchEvent,獲得點(diǎn)擊的點(diǎn)的坐標(biāo)诱咏,通過遍歷mRegionList集合判斷觸摸點(diǎn)是否落入對應(yīng)的區(qū)域的Region的position。然后再次重繪缴挖,在onDraw方法中針對相應(yīng)的position繪制不同顏色背景矩形即可袋狞。

  11. 最后一個坑就是坐標(biāo)轉(zhuǎn)換,因為在繪制的時候我們?yōu)榱擞嬎阕鴺?biāo)簡單將坐標(biāo)系原點(diǎn)移到直方圖坐標(biāo)原點(diǎn)位置,但是在onTouchEvent觸摸坐標(biāo)系卻還是以View左上角為原點(diǎn)的坐標(biāo)系苟鸯,所以需要有個觸摸點(diǎn)坐標(biāo)系向繪制坐標(biāo)系的轉(zhuǎn)換同蜻。利用了Matrix矩陣中逆矩陣的知識。

  1. 本篇博客包含哪些核心知識點(diǎn)內(nèi)容早处?

    1. Canvas中繪制矩形(上篇博客已經(jīng)講過)

    2. Canvas的繪制多條線段(上篇博客已經(jīng)講過)

    3. Canvas的繪制多個點(diǎn)(上篇博客已經(jīng)講過)

    4. Canvas繪制文字(上篇博客已經(jīng)講過)

    5. Canvas的幾何變換使用translate移動繪圖坐標(biāo)系(后期博客會詳細(xì)講解湾蔓,可以先記住一下,下次講的時候不會太陌生)

    6. Canvas的繪制Path(下期博客會詳細(xì)講解砌梆,可以先記住一下默责,下次講的時候不會太陌生)

    7. Canvas中的Path與Region的組合使用裁切矩形區(qū)域。(難點(diǎn),下期會深入講解)

    8. Region區(qū)域的使用判斷觸摸點(diǎn)是否落入柱狀圖形的范圍區(qū)域并給出觸摸反饋的回調(diào)咸包。(難點(diǎn),下期會深入講解)

    9. 變換坐標(biāo)系后桃序,通過Matrix類的逆矩陣實(shí)現(xiàn),觸摸坐標(biāo)系的坐標(biāo)向繪圖坐標(biāo)的坐標(biāo)轉(zhuǎn)換烂瘫。(難點(diǎn),下期會深入講解)

1媒熊、數(shù)據(jù)接口定義List < Pair < String, float> >類型

  • 使用基本數(shù)據(jù)類型,有利于提高View的通用性
List<Pair<String,Float>> pairList = new ArrayList<>();      pairList.add(new Pair<>("Java",0.71f));
pairList.add(new Pair<>("Swift",0.61f));
pairList.add(new Pair<>("C",0.26f));
pairList.add(new Pair<>("C++",0.37f));
pairList.add(new Pair<>("Python",0.84f));
pairList.add(new Pair<>("Go",0.6f));
setContentView(new RectChart(this, pairList));//直接給contentView設(shè)置自定義View

2忱反、坐標(biāo)軸的繪制

  • 為了繪制方便和坐標(biāo)計算方便泛释,采用translate位移畫布將原來View左上角原點(diǎn)移動到直方圖原點(diǎn)(控件中兩個坐標(biāo)軸交點(diǎn)位置)
//初始化坐標(biāo)系畫布
    private void initCoordinateCanvas(Canvas canvas) {
    
        canvas.drawColor(Color.parseColor("#eeeeee"));//繪制畫布純色背景
        canvas.translate(100, mHeight - 100);//平移坐標(biāo)系,原來View左上角原點(diǎn)移動到直方圖原點(diǎn)
        // 獲取測量矩陣(逆矩陣) 由于繪制畫布的View坐標(biāo)系被平移了温算,
        // 但是所處的觸摸坐標(biāo)系并沒有改變怜校,需要利用Matrix矩陣實(shí)現(xiàn)觸摸坐標(biāo)系向繪制坐標(biāo)系轉(zhuǎn)化
        if (mMatrix.isIdentity()) {
            canvas.getMatrix().invert(mMatrix);
        }
        
        float[] opts = {0, 0, mWidth - 200, 0, 0, 0, 0, -(mHeight - 200)};//兩條線8個點(diǎn)坐標(biāo)數(shù)據(jù)
        canvas.drawLines(opts, mCoordinatePaint);//繪制兩條線

        mCoordinatePaint.setStrokeCap(Paint.Cap.ROUND);//繪制點(diǎn)為圓點(diǎn)
        mCoordinatePaint.setStrokeWidth(20f);//設(shè)置點(diǎn)的大小
        canvas.drawPoint(0, 0, mCoordinatePaint);//繪制兩條線段
    }

3、文字集合的繪制

  • 文字繪制采用了paint.setTextBounds方式拿到文字的尺寸
        //初始化文字畫筆
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.parseColor("#C2185B"));
        mTextPaint.setTextSize(40);
        if (mDataList == null || mDataList.isEmpty()) {
            return;
        }
        //先取出數(shù)據(jù)中所有的文字測量每個文字尺寸注竿,并把每個文字尺寸信息記錄在對應(yīng)的rect中茄茁,通過paint.getTextBounds(text,start,end,rect),
        // 最后把每個文字的rect保存在集合中
        for (Pair<String, Float> pair : mDataList) {
            Rect textBound = new Rect();
            mTextPaint.getTextBounds(pair.first, 0, pair.first.length(), textBound);
            mTextBounds.add(textBound);
        }
        //繪制對應(yīng)矩形居中的文字,通過每個矩形region中的left巩割,left + (矩形寬度 - 文字寬度) / 2 作為文字繪制的起點(diǎn)x, y 取文字高度裙顽。因為文字繪制起點(diǎn)很怪異是第一個字的左下角還要向左偏移一點(diǎn)距離。
            private void drawTextList(Canvas canvas) {
        for (int i = 0; i < mRegionList.size(); i++) {
            canvas.drawText(mDataList.get(i).first, mRegionList.get(i).getBounds().left + (mRectWidth / 2 - mTextBounds.get(i).width() / 2), mTextBounds.get(i).height() + 20F, mTextPaint);
        }
    }

3宣谈、矩形集合的繪制

  • 矩形集合繪制比較麻煩愈犹,需要裁剪每個rect的region,以及添加對應(yīng)的path
mGlobalRegion = new Region(-w, -h, w, h);//創(chuàng)建全局的region

        mPointList.clear();//重置折線圖中點(diǎn)集合
        mRegionList.clear();//重置region集合
        for (int i = 0; i < mDataList.size(); i++) {
            //根據(jù)每個矩形需要添加一個間隔距離,第一個矩形left就是間隔闻丑,第二個就是第一個矩形的left + 間隔
            //反推第N個就是第 N-1 個矩形的left + 間隔漩怎。那么這個left =  mGap * (i + 1) + mRectWidth * i
            float left = mGap * (i + 1) + mRectWidth * i;
            //top就是按比例算,負(fù)數(shù)因為繪制處于Y的負(fù)半軸嗦嗡,mHeight-200是為了留出坐標(biāo)軸頂點(diǎn)距離控件底部距離為200.
            float top = -mDataList.get(i).second * (mHeight - 200);
            float right = left + mRectWidth;
            //-mCoordinatePaint.getStrokeWidth()一個細(xì)微處理為了防止矩形繪制勋锤,會蓋住底部坐標(biāo)軸
            float bottom = -mCoordinatePaint.getStrokeWidth();

            //創(chuàng)建折線圖中每個點(diǎn),每個點(diǎn)位置也正處于矩形寬度中點(diǎn)位置
            Point point = new Point(left + mRectWidth / 2, top);
            mPointList.add(point);

            //為每個矩形創(chuàng)建一個path
            Path path = new Path();
            //向每個path中添加一個矩形
            path.addRect(left, top, right, bottom, Path.Direction.CW);
            //創(chuàng)建一個region
            Region region = new Region();
            //在全局的mGlobalRegion中裁剪出對應(yīng)path的矩形范圍侥祭,并把相應(yīng)的范圍信息保存在region
            region.setPath(path, mGlobalRegion);
            //把每個path添加全局的mPath中
            mPath.addPath(path);
            //保存每個region信息到集合中
            mRegionList.add(region);
        }
    //繪制直方圖
    private void drawHistogram(Canvas canvas) {
        canvas.drawPath(mPath, mRectPaint);//繪制最后mPath
        if (mClickPosition != -1) {//判斷點(diǎn)擊mClickPosition叁执,根據(jù)位置重新繪制點(diǎn)擊色的矩形
            mRectPaint.setColor(mPressColor);
            canvas.drawRect(mRegionList.get(mClickPosition).getBounds(), mRectPaint);
            mClickPosition = -1;//重置ClickPosition
        }
    }

4茄厘、折線圖的繪制

  • 折線圖繪制比較簡單,就是對一組點(diǎn)和一組線段的繪制谈宛,關(guān)鍵就是坐標(biāo)的計算次哈,我們在計算矩形的位置順帶就把點(diǎn)的信息計算好,最后保存在pointList集合吆录,直接拿來用即可亿乳。

//計算點(diǎn)的坐標(biāo),創(chuàng)建折線圖中每個點(diǎn)径筏,每個點(diǎn)位置也正處于矩形寬度中點(diǎn)位置
            Point point = new Point(left + mRectWidth / 2, top);
            mPointList.add(point);

//繪制折線圖
    private void drawPolyline(Canvas canvas) {
        for (int i = 0; i < mPointList.size(); i++) {
            //繪制點(diǎn)交點(diǎn)
            mCoordinatePaint.setStrokeWidth(20f);
            canvas.drawPoint(mPointList.get(i).x, mPointList.get(i).y, mCoordinatePaint);
            //繪制連線
            if (i < mPointList.size() - 1) {
                mCoordinatePaint.setStrokeWidth(5f);
                canvas.drawLine(mPointList.get(i).x, mPointList.get(i).y, mPointList.get(i + 1).x, mPointList.get(i + 1).y, mCoordinatePaint);
            }
        }
    }

5、點(diǎn)擊交互的實(shí)現(xiàn)

  • 它實(shí)現(xiàn)核心在于region中提供一個region.contain(x, y)方法可以判斷傳入點(diǎn)的坐標(biāo)是否落入當(dāng)前的region內(nèi)丽涩,返回true or false
@Override
    public boolean onTouchEvent(MotionEvent event) {
        float[] pts = new float[2];
        pts[0] = event.getX();
        pts[1] = event.getY();
        mMatrix.mapPoints(pts);//利用Matrix矩陣實(shí)現(xiàn)觸摸坐標(biāo)系向繪制坐標(biāo)系轉(zhuǎn)化

        int touchX = (int) pts[0];
        int touchY = (int) pts[1];

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_UP:
                mClickPosition = findTouchPoint(touchX, touchY);
                if (mClickPosition != -1) {
                    invalidate();
                    Toast.makeText(getContext(), String.format(Locale.US, "當(dāng)前選中: %s 數(shù)據(jù)為: %f", mDataList.get(mClickPosition).first, mDataList.get(mClickPosition).second), Toast.LENGTH_SHORT).show();
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    //根據(jù)觸摸點(diǎn)的坐標(biāo)找到對應(yīng)的position
    private int findTouchPoint(int touchX, int touchY) {
        int position = -1;
        for (int i = 0; i < mRegionList.size(); i++) {
            Region region = mRegionList.get(i);
            if (region.contains(touchX, touchY)) {//region中提供一個非常不錯的API旭咽,region.contains(x,y),可以判斷傳入點(diǎn)的坐標(biāo)是否落入當(dāng)前的region內(nèi)挑围,返回true or false
                position = i;//觸摸點(diǎn)落入對應(yīng)region也就找到對應(yīng)region所在position
                return position;
            }
        }
        return position;
    }

6、坐標(biāo)轉(zhuǎn)換的坑

  • 由于一開始為了繪制方便變換了繪圖坐標(biāo)系恢氯,可是觸摸坐標(biāo)又不能變換,手指觸摸的坐標(biāo)系和畫布坐標(biāo)系不統(tǒng)一鼓寺,就可能引起手指觸摸位置和繪制位置不統(tǒng)一勋拟,只能將觸摸的坐標(biāo)轉(zhuǎn)化成繪圖坐標(biāo)系。這里需要用到Matrix矩陣知識,Matrix最大功能之一就是坐標(biāo)映射妈候,數(shù)值轉(zhuǎn)換敢靡,后期會專門了解Matrix。
canvas.translate(100, mHeight - 100);//平移坐標(biāo)系
        // 獲取測量矩陣(逆矩陣) 由于繪制畫布的View坐標(biāo)系被平移了苦银,
        // 但是所處的觸摸坐標(biāo)系并沒有改變啸胧,需要利用Matrix矩陣實(shí)現(xiàn)觸摸坐標(biāo)系向繪制坐標(biāo)系轉(zhuǎn)化
        if (mMatrix.isIdentity()) {
            canvas.getMatrix().invert(mMatrix);
        }
        
mMatrix.mapPoints(pts);//利用Matrix矩陣實(shí)現(xiàn)觸摸坐標(biāo)系向繪制坐標(biāo)系轉(zhuǎn)化

7、最后附上全部源碼

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent;
import android.widget.Toast;

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

public class RectChart extends CanvasView {

    private int mNormalColor;
    private int mPressColor;

    private Paint mRectPaint;//繪制矩形的畫筆
    private Paint mTextPaint;//繪制文字的畫筆
    private Paint mCoordinatePaint;//繪制坐標(biāo)軸的畫筆

    private Matrix mMatrix;//用于坐標(biāo)轉(zhuǎn)換的矩陣Matrix的對象
    private Path mPath = new Path();//定義全局所有矩形path集合幔虏,最后所有矩形的Path會加入到mPath中
    private Region mGlobalRegion;//定義全局的Region

    private List<Rect> mTextBounds = new ArrayList<>();//為了繪制文字的集合纺念,每個文字都用rect框住,便于拿到文字的寬度和高度想括。
    private List<Point> mPointList = new ArrayList<>();//為了繪制折線圖中點(diǎn)集合陷谱,每個point對象包含了x,y坐標(biāo)
    private List<Region> mRegionList = new ArrayList<>();//為每個矩形集合定義的region集合

    private int mWidth;//控件的寬度
    private int mHeight;//控件的高度
    private float mGap = 40f;//每個矩形之間間隔大小
    private float mRectWidth = 80f;//每個矩形寬度大小
    private boolean isShowHistogram = true;//是否繪制矩形圖
    private boolean isShowPolyline = true;//是否繪制折線圖
    private int mClickPosition = -1;//用于記錄刷選出點(diǎn)擊落入對應(yīng)的region的position

    private List<Pair<String, Float>> mDataList;//數(shù)據(jù)pair集合,pair對象第一個是用于底部文字繪制的內(nèi)容瑟蜈,第二個是比例對應(yīng)所畫實(shí)際矩形高度烟逊。

    public RectChart(Context context, List<Pair<String, Float>> mDataList) {
        super(context);
        this.mDataList = mDataList;
    }

    @Override
    protected void initDrawTools() {
        //初始化矩形畫筆
        mRectPaint = new Paint();
        mRectPaint.setAntiAlias(true);
        mRectPaint.setColor(mNormalColor);
        mRectPaint.setStyle(Paint.Style.FILL);

        //初始化坐標(biāo)系畫筆
        mCoordinatePaint = new Paint();
        mCoordinatePaint.setAntiAlias(true);
        mCoordinatePaint.setColor(Color.RED);
        mCoordinatePaint.setStyle(Paint.Style.STROKE);
        mCoordinatePaint.setStrokeWidth(5f);

        //初始化文字畫筆
        mTextPaint = new Paint();
        mTextPaint.setAntiAlias(true);
        mTextPaint.setColor(Color.parseColor("#C2185B"));
        mTextPaint.setTextSize(40);
        if (mDataList == null || mDataList.isEmpty()) {
            return;
        }
        //先取出數(shù)據(jù)中所有的文字測量每個文字尺寸,并把每個文字尺寸信息記錄在對應(yīng)的rect中踪栋,通過paint.getTextBounds(text,start,end,rect),
        // 最后把每個文字的rect保存在集合中
        for (Pair<String, Float> pair : mDataList) {
            Rect textBound = new Rect();
            mTextPaint.getTextBounds(pair.first, 0, pair.first.length(), textBound);
            mTextBounds.add(textBound);
        }
        //創(chuàng)建矩陣對象
        mMatrix = new Matrix();
    }

    @Override
    protected void fetchDefAttrValues(Context context, AttributeSet attrs, int defStyleAttr) {
        //該方法用于接收自定義屬性的值焙格,本例子還沒來得及添加自定義屬性
        mNormalColor = Color.parseColor("#ff9900");
        mPressColor = Color.parseColor("#ff0000");
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//      super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(1080, 1000);//測量這里指定大小尺寸的畫布即控件大小
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (mMatrix != null) {
            mMatrix.reset();
        }

        mWidth = w;
        mHeight = h;

        mGlobalRegion = new Region(-w, -h, w, h);//創(chuàng)建全局的region

        mPointList.clear();//重置折線圖中點(diǎn)集合
        mRegionList.clear();//重置region集合
        for (int i = 0; i < mDataList.size(); i++) {
            //根據(jù)每個矩形需要添加一個間隔距離,第一個矩形left就是間隔夷都,第二個就是第一個矩形的left + 間隔
            //反推第N個就是第 N-1 個矩形的left + 間隔眷唉。那么這個left =  mGap * (i + 1) + mRectWidth * i
            float left = mGap * (i + 1) + mRectWidth * i;
            //top就是按比例算予颤,負(fù)數(shù)因為繪制處于Y的負(fù)半軸,mHeight-200是為了留出坐標(biāo)軸頂點(diǎn)距離控件底部距離為200.
            float top = -mDataList.get(i).second * (mHeight - 200);
            float right = left + mRectWidth;
            //-mCoordinatePaint.getStrokeWidth()一個細(xì)微處理為了防止矩形繪制冬阳,會蓋住底部坐標(biāo)軸
            float bottom = -mCoordinatePaint.getStrokeWidth();

            //創(chuàng)建折線圖中每個點(diǎn)蛤虐,每個點(diǎn)位置也正處于矩形寬度中點(diǎn)位置
            Point point = new Point(left + mRectWidth / 2, top);
            mPointList.add(point);

            //為每個矩形創(chuàng)建一個path
            Path path = new Path();
            //向每個path中添加一個矩形
            path.addRect(left, top, right, bottom, Path.Direction.CW);
            //創(chuàng)建一個region
            Region region = new Region();
            //在全局的mGlobalRegion中裁剪出對應(yīng)path的矩形范圍,并把相應(yīng)的范圍信息保存在region
            region.setPath(path, mGlobalRegion);
            //把每個path添加全局的mPath中
            mPath.addPath(path);
            //保存每個region信息到集合中
            mRegionList.add(region);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        initDrawTools();//初始化繪制工具
        initCoordinateCanvas(canvas);//初始化畫布坐標(biāo)軸

        drawTextList(canvas);//繪制文字集合
        if (isShowHistogram) {//繪制直方圖
            drawHistogram(canvas);
        }

        if (isShowPolyline) {//繪制折線圖
            drawPolyline(canvas);
        }

    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        float[] pts = new float[2];
        pts[0] = event.getX();
        pts[1] = event.getY();
        mMatrix.mapPoints(pts);//利用Matrix矩陣實(shí)現(xiàn)觸摸坐標(biāo)系向繪制坐標(biāo)系轉(zhuǎn)化

        int touchX = (int) pts[0];
        int touchY = (int) pts[1];

        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_UP:
                mClickPosition = findTouchPoint(touchX, touchY);
                if (mClickPosition != -1) {
                    invalidate();
                    Toast.makeText(getContext(), String.format(Locale.US, "當(dāng)前選中: %s 數(shù)據(jù)為: %f", mDataList.get(mClickPosition).first, mDataList.get(mClickPosition).second), Toast.LENGTH_SHORT).show();
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    //根據(jù)觸摸點(diǎn)的坐標(biāo)找到對應(yīng)的position
    private int findTouchPoint(int touchX, int touchY) {
        int position = -1;
        for (int i = 0; i < mRegionList.size(); i++) {
            Region region = mRegionList.get(i);
            if (region.contains(touchX, touchY)) {//region中提供一個非常不錯的API肝陪,region.contains(x,y),可以判斷傳入點(diǎn)的坐標(biāo)是否落入當(dāng)前的region內(nèi)驳庭,返回true or false
                position = i;//觸摸點(diǎn)落入對應(yīng)region也就找到對應(yīng)region所在position
                return position;
            }
        }
        return position;
    }

    private void drawTextList(Canvas canvas) {
        for (int i = 0; i < mRegionList.size(); i++) {
            canvas.drawText(mDataList.get(i).first, mRegionList.get(i).getBounds().left + (mRectWidth / 2 - mTextBounds.get(i).width() / 2), mTextBounds.get(i).height() + 20F, mTextPaint);
        }
    }

    //初始化坐標(biāo)系畫布
    private void initCoordinateCanvas(Canvas canvas) {
        canvas.drawColor(Color.parseColor("#eeeeee"));
        canvas.translate(100, mHeight - 100);//平移坐標(biāo)系
        // 獲取測量矩陣(逆矩陣) 由于繪制畫布的View坐標(biāo)系被平移了,
        // 但是所處的觸摸坐標(biāo)系并沒有改變氯窍,需要利用Matrix矩陣實(shí)現(xiàn)觸摸坐標(biāo)系向繪制坐標(biāo)系轉(zhuǎn)化
        if (mMatrix.isIdentity()) {
            canvas.getMatrix().invert(mMatrix);
        }

        float[] opts = {0, 0, mWidth - 200, 0, 0, 0, 0, -(mHeight - 200)};
        canvas.drawLines(opts, mCoordinatePaint);//繪制兩條線

        mCoordinatePaint.setStrokeCap(Paint.Cap.ROUND);
        mCoordinatePaint.setStrokeWidth(20f);
        canvas.drawPoint(0, 0, mCoordinatePaint);
    }

    //繪制直方圖
    private void drawHistogram(Canvas canvas) {
        canvas.drawPath(mPath, mRectPaint);//繪制最后mPath
        if (mClickPosition != -1) {//判斷點(diǎn)擊mClickPosition饲常,根據(jù)位置重新繪制點(diǎn)擊色的矩形
            mRectPaint.setColor(mPressColor);
            canvas.drawRect(mRegionList.get(mClickPosition).getBounds(), mRectPaint);
            mClickPosition = -1;//重置ClickPosition
        }
    }

    //繪制折線圖
    private void drawPolyline(Canvas canvas) {
        for (int i = 0; i < mPointList.size(); i++) {
            //繪制點(diǎn)交點(diǎn)
            mCoordinatePaint.setStrokeWidth(20f);
            canvas.drawPoint(mPointList.get(i).x, mPointList.get(i).y, mCoordinatePaint);
            //繪制連線
            if (i < mPointList.size() - 1) {
                mCoordinatePaint.setStrokeWidth(5f);
                canvas.drawLine(mPointList.get(i).x, mPointList.get(i).y, mPointList.get(i + 1).x, mPointList.get(i + 1).y, mCoordinatePaint);
            }
        }
    }

    class Point {
        private float x;
        private float y;

        public Point(float x, float y) {
            this.x = x;
            this.y = y;
        }
    }

}

image.png
image.png
image.png

結(jié)束

這個例子就算是講完,下篇博客咱們一起講下Path相關(guān)的內(nèi)容狼讨。一下子水了這么多贝淤,好渴喝水去了。對于還不是很不知道知識政供,可以先記住播聪。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市布隔,隨后出現(xiàn)的幾起案子离陶,更是在濱河造成了極大的恐慌,老刑警劉巖衅檀,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件招刨,死亡現(xiàn)場離奇詭異,居然都是意外死亡哀军,警方通過查閱死者的電腦和手機(jī)计济,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來排苍,“玉大人沦寂,你說我怎么就攤上這事√匝茫” “怎么了传藏?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長彤守。 經(jīng)常有香客問我毯侦,道長,這世上最難降的妖魔是什么具垫? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任侈离,我火速辦了婚禮,結(jié)果婚禮上筝蚕,老公的妹妹穿的比我還像新娘卦碾。我一直安慰自己铺坞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布洲胖。 她就那樣靜靜地躺著济榨,像睡著了一般。 火紅的嫁衣襯著肌膚如雪绿映。 梳的紋絲不亂的頭發(fā)上擒滑,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音叉弦,去河邊找鬼丐一。 笑死,一個胖子當(dāng)著我的面吹牛淹冰,可吹牛的內(nèi)容都是我干的钝诚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼榄棵,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了潘拱?” 一聲冷哼從身側(cè)響起疹鳄,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎芦岂,沒想到半個月后瘪弓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡禽最,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年腺怯,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片川无。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡呛占,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出懦趋,到底是詐尸還是另有隱情晾虑,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布仅叫,位于F島的核電站帜篇,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏诫咱。R本人自食惡果不足惜笙隙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坎缭。 院中可真熱鬧竟痰,春花似錦签钩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至假消,卻和暖如春柠并,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背富拗。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工臼予, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啃沪。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓粘拾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親创千。 傳聞我的和親對象是個殘疾皇子缰雇,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容