關於View,你應該知道的一切

1.什麼是View
View是Android系統中所有控件的基類,不管是TextView,還是Button,甚至是LinearLayout和RelativeLayout都有共同的基類View。除了View還有ViewGroup,ViewGroup翻譯成中就是控件組,顧名思義,他是一組控件。也就是一組View。
View體系結構圖
View的繼承關係
View的繼承關係
Activity中onCreate()方法中的setContentView()到底幹了啥
Android的每個控件都會在界面裏面佔據一塊矩形區域,在Android裏面控件大致被分爲兩類,一類是View,一類是ViewGroup。ViewGroup作爲父控件可以包含很多View控件,並且管理View。通常情況下,Activity中使用setContentView()方法來設置一個佈局。
這裏寫圖片描述
根據上圖我們可以看出每個Activity中都包含一個Window對象,通常Window由PhoneWindow來實現了,PhoneWindow將一個DecorView設置成整個窗口的根View,DecorView封裝了窗口的一些常用方法,在顯示上它將屏幕分爲兩個部分,一個是TitleView,一個ContentView,所謂的ContentView是一個ID爲content的FrameLayout,也就是說Android的根佈局是一個FrameLayout,我們可以通過DDMS的Dump來分析Xml
這裏寫圖片描述
看到上面的分析結果,證明我沒騙你吧。
根據上圖我們可以看到黃色的FrameLayout和綠色的FrameLayout的父佈局就是DecorView,其中黃色的FrameLayout是設置TitleView也就是ActionBar,綠色FrameLayout是設置內容的。這就是我們在去掉標題欄的方法要在setContnetView之前調用的原因。去標題欄的方法如下

 requestWindowFeature(Window.FEATURE_NO_TITLE)

什麼是MotionEvent
手指在觸摸屏幕產生一系列的事件,都是通過MotionEvent對象來傳遞的,包括觸摸時候和移動時候的座標

  • ACTION_DOWN 手指剛剛接觸屏幕
  • ACTION_MOVE 手指在屏幕上滑動
  • ACYION_UP 手指離開屏幕
    通過MotionEvent對象我們可以拿到事件發生時的座標,系統提供了兩組獲取方式getX/getY和getRawX/getRawY,他們的區別很簡單,getX/getY返回的是當前View左上角的x和y座標getRawX和getRawY,是返回當前屏幕左上角的x和y軸座標。

什麼是TouchSlop
是系統能夠識別最小的滑動距離,最小滑動距離是個常量,和設備有關,獲取方式

ViewConfiguration.get(this).getScaledTouchSlop();

View的位置參數
View位置主要有四個頂點來決定的,它的的四個屬性分別是: left top rght buttom
位置關係圖
根據上述關係圖,就能很快的計算出View的寬高

width=right-left;
height=buttom-top;

什麼是VelocityTracker
速度追蹤,用於追蹤手指在滑動過程中的速度,包含水平方向和垂直方向。使用方法如下,需要在onTouchEvent方法中調用

VelocityTracker velocityTracker=VelocityTracker.obtain();
velocityTracker.addMovement(event);

當前追蹤了當前速度,就可以使用下面方法來獲取當前速度

 velocityTracker.computeCurrentVelocity(1000);
 int x= (int) velocityTracker.getXVelocity();
 int y= (int) velocityTracker.getYVelocity();

獲取水平方向和垂直方向的速度前一定要調用computeCurrentVelocity(1000); 指的是一秒內滑動距離(單位時間內滑動的距離就是速度)
當不需要的時候需要回收(一般在ACTION_UP的時候就需要回收了)

 velocityTracker.clear();
 velocityTracker.recycle();

View的測量
在一些娛樂性節目中,常常出現了一個人描述物體,另一個人畫出該物體。那麼系統是如何畫出View的,首先我們得告訴它View的規格吧。在View繪製之前,必須進行測量。Android提供了一個功能強大的輔助類MeasureSpec,通過它能幫助我們完成View的測量
測量有三種模式
EXACTLY
即精準的測量模式,當控件的layout_width和layout_height爲具體值時(layout_width=”100dp”),系統使用就是這個精準模式
AT_MOST
即最大值模式,當控件的layout_width=”wrap_content”,控件大小一般隨着子控件的大小變化而變化,此時控件大小隻要不超過父控件即可。
UNSPECIFIED
這個屬性一般在繪製自定義View的時候使用
View默認的onMeasure()方法只支持EXACTLY,如果想讓你的控件支持warp_content屬性,就必須重寫onMeasure()方法,
模版代碼,當specMode爲EXACTLY模式,直接使用specSize的值,當爲其他兩種模式時候,需要給他一個默認大小的值。如果爲warp_content屬性,即AT_MOST模式需要取出我們指定大小和specSize中最小的值作爲測量值

     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = measure(widthMeasureSpec);
        int height = measure(heightMeasureSpec);
        setMeasuredDimension(width, height);
    }

    public int measure(int measureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200; //默認值
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

View的繪製
當測量好了一個View之後,就可以通過重寫onDraw()方法,並使用Canvas對象來繪製圖形。

  • 畫圓
         paint.setColor(Color.GREEN);
         paint.setStyle(Paint.Style.FILL);
         //圓心xy軸座標,圓半徑
         canvas.drawCircle(getWidth() / 2, getHeight() / 2, 200, paint);
         paint.setColor(Color.RED);
         li(canvas, 1, getWidth() / 2, getHeight() / 2);
  • 畫線
          paint.setColor(Color.BLACK);
          paint.setStrokeWidth(10);
          paint.setStyle(Paint.Style.FILL);
          //開始的xy座標,結束的xy座標
          canvas.drawLine(100, 100, 400, 100, paint);
          //該線的起點是100,終點爲400,中心點的位置是 (stopX-startX)/2+距離左邊的距離=中心點位置
          li(canvas, 2, (400 - 100) / 2 + 100, 100);
  • 畫點
         paint.setStrokeWidth(50);
         paint.setColor(Color.YELLOW);
         paint.setStyle(Paint.Style.FILL);
         //點的xy軸座標
         canvas.drawPoint(200, 200, paint);
         li(canvas, 3, 200, 200);
  • 畫矩形
        paint.setColor(Color.BLUE);
        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.FILL);
        Rect rect = new Rect(500, 100, 800, 400);
        canvas.drawRect(rect, paint);
        li(canvas, 4, (800 - 500) / 2 + 500, (400 - 100) / 2 + 100);        
  • 畫圓角矩形
        paint.setColor(Color.DKGRAY);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(10);
        RectF rectF = new RectF(100, 400, 400, 600);
        //矩形的位置,矩形的圓角大小
        canvas.drawRoundRect(rectF, 10, 30, paint);
        li(canvas, 5, (400 - 100) / 2 + 100, (600 - 400) / 2 + 400);
  • 畫橢圓
        RectF r1 = new RectF(100, 800, 300, 1100);
        paint.setColor(Color.GRAY);
        canvas.drawOval(r1, paint);
        li(canvas, 6, (300 - 100) / 2 + 100, (1100 - 800) / 2 + 800);
  • 畫圓弧
        paint.setColor(Color.LTGRAY);
        paint.setStrokeWidth(10);
        paint.setStyle(Paint.Style.STROKE);
        RectF r2 = new RectF(100, 1200, 400, 1500);
        //圓弧的位置,弧度的起始角度,結束角度,是否封口,畫筆
        canvas.drawArc(r2, 0, 180, false, paint);
        li(canvas, 7, (400 - 100) / 2 + 100, (1500 - 1200) / 2 + 1200);



        paint.setStrokeWidth(2);
        paint.setStyle(Paint.Style.STROKE);
        RectF r3 = new RectF(300, 1000, 600, 1300);
        canvas.drawArc(r3, 0, 180, true, paint);
        li(canvas, 8, (600 - 300) / 2 + 300, (1300 - 1000) / 2 + 1000);

具體效果看這兒
這裏寫圖片描述

這裏貼上關於文字的繪製代碼

    public void li(Canvas canvas, int i, int x, int y) {
        paint.setTextSize(50);
        float textWidth = paint.measureText(String.valueOf(i)) / 2;
        float textHeight = (paint.descent() + paint.ascent()) / 2;
        paint.setStrokeWidth(1);
        paint.setStyle(Paint.Style.FILL);
        paint.setColor(Color.RED);
        canvas.drawText(String.valueOf(i), x - textWidth, y - textHeight, paint);
    }

關於View繪製的實際案例
交替圓環這裏寫圖片描述
代碼如下交替圓環

/**
 * Created by xiongchengguang on 2016/7/9.
 */
public class CircleView extends View {
    private static final int DEFAULT_FIRSTCOLOR = 0xff12ffee;
    private static final int DEFAULT_SECONCOLOR = 0xffeeff0e;
    private static final int DEFAULT_WIDTH = 50;
    private static final int DEFAULT_SPEED = 20;

    private int firstColor;
    private int seconColor;
    private int width;
    private int speed;

    private Paint paint = new Paint();
    private int progress = 10;

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

    public void init(AttributeSet attrs) {
        TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.CircleView);
        firstColor = typedArray.getColor(R.styleable.CircleView_firstColor, DEFAULT_FIRSTCOLOR);
        seconColor = typedArray.getColor(R.styleable.CircleView_secondColor, DEFAULT_SECONCOLOR);
        width = (int) typedArray.getDimension(R.styleable.CircleView_circleWidth, px2dp(DEFAULT_WIDTH));
        speed = typedArray.getInt(R.styleable.CircleView_speed, DEFAULT_SPEED);
        typedArray.recycle();

        new Thread(new Runnable() {

            public void run() {
                while (true) {
                    progress++;
                    if (progress == 360) {
                        progress = 0;
                    }
                    postInvalidate();
                    SystemClock.sleep(speed);
                }

            }
        }).start();
    }

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

    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int centent = getWidth() / 2;
        int radius = centent - width / 2;
        paint.setStrokeWidth(width);
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        RectF rectF = new RectF(centent - radius, centent - radius, centent + radius, centent + radius);
        paint.setColor(firstColor);
        canvas.drawCircle(centent, centent, radius, paint);
        paint.setColor(seconColor);
        canvas.drawArc(rectF, 0, progress, false, paint);
        paint.setStrokeWidth(1);
        paint.setColor(Color.LTGRAY);
        paint.setTextSize(20);
        canvas.drawRect(rectF, paint);
        String text = ((int) 100 / 360) + progress + "%";
        float textWidth = paint.measureText(text) / 2;
        float textHeight = (paint.descent() + paint.ascent()) / 2;
        canvas.drawText(text, centent - textWidth, centent - textHeight, paint);
    }

    public int px2dp(int val) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, val, getResources().getDisplayMetrics());
    }

    public int px2sp(int val) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, val, getResources().getDisplayMetrics());
    }
}

波紋效果

/**
 * Created by xiongchengguang on 2016/7/9.
 */
public class MoireView extends View {
    private int mMaxRadius;
    private int speed = 200;
    private long duration = 2000;
    private Paint paint;
    private List<Circle> arr = new ArrayList<>();
    private float x, y;

    public MoireView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(2);
        paint.setColor(Color.LTGRAY);
        paint.setAntiAlias(true);
        new Thread(new Runnable() {

            public void run() {
                while (true) {
                    newCircle();
                    SystemClock.sleep(speed);
                }
            }
        }).start();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        x = event.getRawX();
        y = event.getRawY();
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Iterator<Circle> iterator = arr.iterator();
        while (iterator.hasNext()) {
            try {
                Circle circle = iterator.next();
                if (System.currentTimeMillis() - circle.currentTimeMillis < duration) {
                    paint.setAlpha(circle.getAlpha());
                    canvas.drawCircle(x, y, circle.getRadius(), paint);
                } else {
                    iterator.remove();
                }
            } catch (Exception e) {
                e.printStackTrace();
                break;
            }
        }
        invalidate();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mMaxRadius = Math.min(w, h);
    }

    public void newCircle() {
        Circle circle = new Circle();
        arr.add(circle);
    }

    class Circle {
        private long currentTimeMillis;

        public Circle() {
            currentTimeMillis = System.currentTimeMillis();
        }

        public int getAlpha() {
            float per = (System.currentTimeMillis() - currentTimeMillis) / duration;
            return (int) ((1F - per) * 255);
        }

        public float getRadius() {
            float per = (System.currentTimeMillis() - currentTimeMillis) * 1.0f / duration;
            return per * mMaxRadius;
        }
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章