Introdução a esta seção:
Esta seção continua trazendo a Explicação Detalhada da API do Canvas (Parte 2) da Explicação Detalhada da Série de Desenhos do Android. Hoje vamos explicar a família de métodos ClipXxx no Canvas! Podemos ver que existem três tipos de métodos Clip fornecidos a nós no documento: clipPath ( ), clipRect ( ), clipRegion ( );
Por meio de diferentes combinações de Path, Rect e Region, quase qualquer forma de área de recorte pode ser suportada!
Caminho : Pode ser uma curva aberta ou fechada, um conjunto complexo de gráficos compostos por linhas
Ret : área retangular
Região : Pode ser entendida como uma combinação de regiões, por exemplo, duas regiões podem ser somadas, subtraídas e confundidas!
Region.Op define os tipos de operações inter-regionais suportadas por Region! Falaremos sobre isso mais tarde, e outra coisa a dizer, o recorte que normalmente entendemos pode ser para recortar os gráficos existentes, mas no Android, o recorte do Canvas deve ser feito antes de desenhar a imagem. Se o Canvas for desenhado após o desenho Se você executar o Clip, isso não afetará os gráficos já desenhados. Lembre-se de que o Clip é para a Tela, não para os gráficos! Bem, não BB, apenas comece esta seção!
Documentação oficial da API : Canvas
1. Explicação detalhada do método de combinação Region.Op
Na verdade, a dificuldade nada mais é do que essa: Region representa uma região, ou seja, uma determinada área fechada na camada Canvas! Claro, quando você tiver tempo, você pode deduzir lentamente essa classe sozinho, e geralmente prestamos atenção apenas a um de seus valores de enumeração: Op
Vamos dar uma olhada no papel de cada valor de enumeração: Assumimos duas regiões de recorte A e B, então chamamos o valor de enumeração correspondente a Region.Op:
DIFERENÇA : A faixa de diferença de A e B , ou seja, A - B, será exibido apenas o conteúdo do desenho dentro desta faixa;
INTERSECT : Ou seja, o intervalo de interseção de A e B , apenas o conteúdo do desenho dentro deste intervalo será exibido
UNION : Ou seja, será exibido o intervalo de união de A e B , ou seja, será exibido o conteúdo do desenho do intervalo compreendido por ambos;
XOR : O intervalo de complemento de A e B , neste exemplo, o intervalo de A exceto B, apenas o conteúdo do desenho dentro deste intervalo será exibido;
REVERSE_DIFFERENCE : O intervalo da diferença entre B e A , ou seja, B - A, será exibido apenas o conteúdo do desenho dentro deste intervalo;
SUBSTITUIR : Independente do estado de coleta de A e B, a faixa de B será exibida em sua totalidade.Caso haja uma interseção com A, a faixa de interseção de A será percorrida;
Se você aprendeu coleções, então desenhar um Venn (diagrama de Venn) ficará claro, você não aprendeu? Tudo bem, vamos escrever um exemplo para tentar o resultado correspondente~! Escreva um método para inicializar o pincel e desenhar um retângulo:
private void init() { mPaint = new Paint(); mPaint.setAntiAlias(verdadeiro); mPaint.setStrokeWidth(6); mPaint.setColor(getResources().getColor(R.color.blush)); } private void drawScene(Canvas canvas){ canvas.drawRect(0, 0, 200, 200, mPaint); }
Op.DIFERENÇA:
canvas.clipRect(10, 10, 110, 110); //o primeiro canvas.clipRect(50, 50, 150, 150, Region.Op.DIFFERENCE); //o segundo drawScene(canvas);
resultado :
Começando em (10,10) e (50,50) sucessivamente, dois retângulos de 100*100 foram cortados e o resultado do corte é:
A diferença de A e B = A - (a interseção de A e B)
Op.INTERSECT:
canvas.clipRect(10, 10, 110, 110); //o primeiro canvas.clipRect(50, 50, 150, 150, Region.Op.INTERSECT); //o segundo drawScene(canvas);
resultado :
Começando em (10,10) e (50,50) sucessivamente, dois retângulos de 100*100 são cortados e o resultado do corte é: Interseção de A e B = Parte da interseção de A e B
Op. UNIÃO:
canvas.clipRect(10, 10, 110, 110); //o primeiro canvas.clipRect(40, 40, 140, 140, Region.Op.UNION); //o segundo drawScene(canvas);
resultado :
Começando em (10,10) e (50,50) sucessivamente, dois retângulos 100*100 são cortados, e o resultado do corte é: união de A e B = área de A + área de B
Op. XOR :
canvas.clipRect(10, 10, 110, 110); //o primeiro canvas.clipRect(50, 50, 150, 150, Region.Op.XOR); //o segundo drawScene(canvas);
resultado :
Começando em (10,10) e (50,50) sucessivamente, dois retângulos de 100*100 são cortados e o resultado do corte é: Conjunto complementar de A e B = coleção de A e B - coleção de interseção de A e B
Op.REVERSE_DIFFERENCE:
canvas.clipRect(10, 10, 110, 110); //o primeiro canvas.clipRect(50, 50, 150, 150, Region.Op.REVERSE_DIFFERENCE); //o segundo drawScene(canvas);
resultado :
Começando em (10,10) e (50,50) sucessivamente, dois retângulos 100*100 são cortados, e o resultado do corte é: diferença de B e A = B - interseção de A e B
Op.SUBSTITUIR
canvas.clipRect(10, 10, 110, 110); //o primeiro canvas.clipRect(50, 50, 150, 150, Region.Op.REPLACE); //o segundo drawScene(canvas);
resultado :
Iniciado em (10,10) e (50,50) sucessivamente, corte dois retângulos de 100*100, o resultado do corte é: Independentemente do status da coleção de A e B, o intervalo de B será exibido em sua totalidade, se Se houver uma interseção com A, a faixa de interseção de A será percorrida;
Exemplo de uso de 2.Region.Op:
Os exemplos referem-se a: Android 2D Graphics Learning (2), Canvas Part 2, Canvas Clipping and Region, RegionIterator
Diagrama de efeito de execução :
A parte principal do código MyView.java:
/** * Criado por Jay em 10/11/2015 0010. */ public class MyView extends View{ private Bitmap mBitmap = null; private int limitLength = 0; // private int width; private int height; private static final int CLIP_HEIGHT = 50; private boolean status = HIDE;//Mostra ou esconde o status, o primeiro é HIDE private static final boolean SHOW = true;//Mostra a imagem private static final boolean HIDE = false;//Oculta a imagem public MyView( Context context ) { this(context, null); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.img_meizi); limitLength = largura = mBitmap.getWidth(); altura = mBitmap.getHeight(); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onDraw(Canvas canvas) { Region region = new Region(); int i = 0; while (i * CLIP_HEIGHT <= altura) {//计算clip的区域 if (i % 2 == 0) { region.union(new Rect(0, i * CLIP_HEIGHT, limitLength, (i + 1) * CLIP_HEIGHT)) ; } outro { region.union(new Rect(width - limitLength, i * CLIP_HEIGHT, width, (i + 1) * CLIP_HEIGHT)); } i++; } canvas.clipRegion(region); canvas.drawBitmap(mBitmap, 0, 0, new Paint()); if (status == HIDE) {//Se limitLength estiver oculto neste momento -= 10; if(limitLength <= 0) status=SHOW; } else {//Se limitLength for exibido neste momento += 5; if(limitLength >= largura) status=OCULTAR; } invalidar(); } }
Realizar análise :
Obtenha a largura e a altura durante a inicialização e, em seguida, faça um loop. Pode-se entender que a imagem é dividida em linhas. A condição do loop é: i * a altura de cada linha não é maior que a altura e, em seguida, as linhas são divididas em dois casos. A união da região é chamada. Na verdade, é apenas o método de corte do UNINO. Finalmente, ele julga se a imagem é exibida neste momento e lida com as situações ocultas e exibidas de maneira diferente e, finalmente, chama invalidate( ) para redesenhar! É bem simples, entenda você mesmo~
Outra coisa a dizer: a transformação da tela não tem efeito no clipRegion
3. Explicação detalhada do método clipRect:
clipRect fornece sete métodos sobrecarregados:
Os parâmetros são introduzidos da seguinte forma :
rect : objeto Rect, usado para definir o intervalo da área de recorte, Rect e RectF têm funções semelhantes, mas a precisão e os métodos fornecidos são diferentes
esquerda : a posição esquerda da área de recorte retangular
top : a posição superior da área de recorte retangular
direita : a posição certa da área de recorte retangular
inferior : a posição inferior da área de recorte retangular
op : o método de combinação da área de recorte
Os quatro valores acima podem ser ponto flutuante ou inteiro
Exemplo de uso :
mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(Color.BLACK); mPaint.setTextSize(60); canvas.translate(300,300); canvas.clipRect(100, 100, 300, 300); //Define o intervalo de exibição canvas.drawColor(Color.WHITE); //Fundo branco canvas.drawText("Double 11, continue a comer minha comida de cachorro...", 150, 300, mPaint); //Desenha uma linha
Resultado da execução :
Do exemplo acima, não sei se você encontrou? clipRect será afetado pela transformação Canvas, e a área branca é a área que não gasta, então o que clipRect clips é a tela, e nosso desenho é executado nessa tela recortada! Exceder esta área não será exibido!
4. Explicação detalhada do método clipPath:
Comparado com clipRect, clipPath tem apenas dois métodos sobrecarregados, e o método de uso é muito simples, basta desenhar um caminho sozinho e passá-lo!
Exemplo de uso :
Aqui está um exemplo de reutilização do ImageView circular que escrevemos no ImageView antes ~
Código de implementação :
ImageView personalizado: RoundImageView.java
/** * Criado por coder-pig em 18/07/2015 0018. */ public class RoundImageView extends ImageView { private Bitmap mBitmap; privado Rect mRect = new Rect(); private PaintFlagsDrawFilter pdf = new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG); private Paint mPaint = new Paint(); Caminho privado mPath=novo Caminho(); public RoundImageView(Context context, AttributeSet attrs) { super(context, attrs); iniciar(); } //Exibição de bitmap public void setBitmap(Bitmap bitmap) { this.mBitmap = bitmap; } private void init() { mPaint.setStyle(Paint.Style.STROKE); mPaint.setFlags(Paint.ANTI_ALIAS_FLAG); mPaint.setAntiAlias(true);// 抗锯尺 } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if(mBitmap == null) { return; } mRect.set(0,0,getWidth(),getHeight()); canvas.save(); canvas.setDrawFilter(pdf); mPath.addCircle(getWidth() / 2, getWidth() / 2, getHeight() / 2, Path.Direction.CCW); canvas.clipPath(mPath, Region.Op.REPLACE); canvas.drawBitmap(mBitmap, nulo, mRect, mPaint); canvas.restore(); } }
Código do layout: activity_main.xml :
<com.jay.demo.imageviewdemo.RoundImageView android:id="@+id/img_round" android:layout_width="200dp" android:layout_height="200dp" android:layout_margin="5px"/>
MainActivity.java :
public class MainActivity extends AppCompatActivity { private RoundImageView img_round; @Override void protegido onCreate(Pacote salvadoInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); img_round = (RoundImageView) findViewById(R.id.img_round); Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.meinv); img_round.setBitmap(bitmap); } }
Além disso, o ImageView arredondado feito por este método terá aliasing óbvio. Mesmo se você definir anti-aliasing para Paint e Canvas, é inútil~ Se você tiver requisitos altos, poderá usar Xfermode-PorterDuff para definir a mistura de imagem para alcançá-lo . Basicamente não há aliasing. Visível: Paint API do Android - Explicação detalhada do Xfermode e PorterDuff (3)