任意のViewのフィレット化を実現

14966 ワード

前言:
             View     ,     View    ,                   , , ImageView    ,   RoundImageView;                , , ListView    ,   RefreshListView. 

          ,                   .        ,        .

最近Androidの新しいいくつかのコントロールを研究して、明らかにこれらの実現がもたらした重大なプログラミング思想を感じることができる.例えば、RefreshLayoutは、ドロップダウン・リフレッシュの論理を1つのレイアウトに抽象化すると、このレイアウトに追加されたコントロールはドロップダウン・リフレッシュの動作を有する.CardViewは、すべてのビューコントロールをフィレット化することができます.1枚の画像に限らずMetroの多くのコントロールは、このような考え方に基づいています.ナイフを動かすのは目の前だけでなく、病気を治すには根を下ろさなければなりません.
本文
本論文で提供する新しいコントロールはCardViewの改良であり、CardViewを使用したことがある学生は、低バージョンの携帯電話で示された欠陥を知っているはずだ.コンテンツにpaddingを追加することで、背景が丸くなっただけで、コンテンツは依然として四方八方に広がっている.任意のシステムでコンテンツを丸められるコントロールを実現する方法はありますか?答えは肯定的だ.考えは何ですか.Canvasという描画キャンバスを深く研究すると、キャンバスの設定がどれだけ大きいか、内容がどれだけ大きいかがわかるはずです.また、このキャンバスにはクリップ領域を設けることもできる.
カットエリアは何ですか?クリップ領域は、コンテンツが表示できるウィンドウであり、クリップ領域の形状も、コンテンツが最終的に表示する形状を決定する.例えば、クリップ領域を円形に設定と、描いた内容は必ず円形であり、円形以外の場所では、描画されずに遮られる.これが私たちが今日お話しするこのコントロールの実現です.
クリップ領域に基づくポリゴンレイアウトを実現する.
円角だけではありませんよ~任意の形状、円形、楕円形、円角矩形、星形、5辺形を追加することができます....形は君次第だ.下に直接オリジナルコードを貼って参考にしてください.
package cn.andrewlu.app.customview;

import android.content.Context;
import android.content.res.TypedArray;
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.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.FrameLayout;

/**
 * Created by Andrewlu on 2016/4/6. 
 * CardView                 .         .
 * RoundView             .     :     ,  ,  .  .         0     .
 */
public class RoundView extends FrameLayout {
    private float mRadius = 0;
    private float mStrokeWidth = 0;
    private int mStrokeColor = Color.TRANSPARENT;
    private Path mBoundPath = null;
    private Type mType = Type.Rect;//         ,    type   ,        .

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

    public RoundView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    /**
    *       ,      ,  ,    ,     .  CardView      .
    **/
    public RoundView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        setWillNotDraw(false);
        TypedArray a = context.obtainStyledAttributes(attrs,
                R.styleable.RoundView);
        mRadius = a.getDimension(R.styleable.RoundView_radius, mRadius);
        mStrokeWidth = a.getDimension(R.styleable.RoundView_strokeWidth,
                mStrokeWidth);
        mStrokeColor = a.getColor(R.styleable.RoundView_strokeColor,
                mStrokeColor);
        int shape = a.getInt(R.styleable.RoundView_shape, mType.getType());
        mType = Type.from(shape);
        a.recycle();
    }

    public void setRadius(float radius) {
        if (mRadius == radius)
            return;
        this.mRadius = radius;
        postInvalidate();
    }

    public float getRadius() {
        return mRadius;
    }

    public void setStrokeWidth(float strok) {
        this.mStrokeWidth = strok;
        postInvalidate();
    }

    public float getStrokeWidth() {
        return mStrokeWidth;
    }

    public void setStrokeColor(int strokeColor) {
        this.mStrokeColor = strokeColor;
    }

    public int getStrokeColor() {
        return mStrokeColor;
    }

    //draw()     View              ,             ,           .
    public void draw(Canvas canvas) {
        beforeDraw(canvas);
        super.draw(canvas);
    }
    //dispatchDraw                     .         ,    .
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        afterDraw(canvas);
    }

    //                ,              Path. 
    private void beforeDraw(Canvas canvas) {
        Rect rect = new Rect();
        getLocalVisibleRect(rect);
        mBoundPath = onCaculatePath(rect);
        canvas.clipPath(mBoundPath);

        Log.i("RoundView", "beforeDraw");
    }

    //     ,           .
    private void afterDraw(Canvas canvas) {
        Rect rect = new Rect();
        getLocalVisibleRect(rect);
        //       .
        Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
        p.setStyle(Paint.Style.STROKE);
        p.setColor(mStrokeColor);
        p.setStrokeWidth(mStrokeWidth);
        Path path = onGetPathStroke(rect, mBoundPath);
        if (path == null)
            return;
        canvas.drawPath(path, p);
    }

    protected Path onCaculatePath(Rect r) {
        switch (mType) {
        case Rect:
            return caculateRoundRectPath(r);
        case Circle:
            return caculateCirclePath(r);
        case Oval:
            return caculateOvalPath(r);
        }
        return caculateRoundRectPath(r);
    }

    protected Path onGetPathStroke(Rect r, Path boundPath) {
        switch (mType) {
        case Circle:
            return getCirclePathWithinStroke(r, boundPath);
        default:
            return getPathWithinStroke(r, boundPath);
        }
    }

    //  path     ,       .
    private Path getPathWithinStroke(Rect r, Path path) {
        if (mStrokeWidth <= 0)
            return path;

        //      ,      .
        int minWidth = r.width() > r.height() ? r.height() : r.width();
        if (minWidth <= 0)
            return null;

        if (mStrokeWidth >= minWidth / 2)
            mStrokeWidth = minWidth / 2.5f;

        Path p = new Path();
        Matrix matrix = new Matrix();
        float scaleX = (r.width() - mStrokeWidth / 2) / r.width();
        float scaleY = (r.height() - mStrokeWidth / 2) / r.height();

        matrix.setScale(scaleX, scaleY, r.centerX(), r.centerY());
        path.transform(matrix, p);
        return p;
    }

    private Path getCirclePathWithinStroke(Rect r, Path path) {
        if (mStrokeWidth <= 0)
            return path;
        //      ,      .
        int minWidth = r.width() > r.height() ? r.height() : r.width();
        if (minWidth <= 0)
            return null;

        if (mStrokeWidth >= minWidth / 2)
            mStrokeWidth = minWidth / 2.5f;

        Path p = new Path();
        Matrix matrix = new Matrix();
        float scale = (minWidth - mStrokeWidth / 2) / minWidth;

        matrix.setScale(scale, scale, r.centerX(), r.centerY());
        path.transform(matrix, p);
        return p;
    }

    //         ,            .    .
    private Path caculateRoundRectPath(Rect r) {
        Path path = new Path();
        float radius = getRadius();
        float elevation = 0;
        path.addRoundRect(new RectF(r.left + elevation, r.top + elevation,
                r.right - elevation, r.bottom - elevation), radius, radius,
                Path.Direction.CW);
        return path;
    }

    private Path caculateCirclePath(Rect r) {
        Path path = new Path();
        int radius = r.width() > r.height() ? r.height() / 2 : r.width() / 2;
        path.addCircle(r.left + radius, r.top + radius, radius,
                Path.Direction.CW);
        return path;
    }

    private Path caculateOvalPath(Rect r) {
        Path path = new Path();
        path.addOval(new RectF(r), Path.Direction.CW);
        return path;
    }

    public enum Type {
        Rect(0), Circle(1), Oval(2);
        private int type;

        Type(int type) {
            this.type = type;
        }

        public int getType() {
            return this.type;
        }

        public static Type from(int type) {
            switch (type) {
            case 0:
                return Rect;
            case 1:
                return Circle;
            case 2:
                return Oval;
            default:
                return Rect;
            }
        }
    }
}