Paint API - Xfermode 및 PorterDuff에 대한 자세한 설명(3)
이 섹션 소개:
이전 섹션에서 Xfermode의 셋째 아들인 PorterDuff에 대해 배웠습니다. Xfermode 구성 방법에는 하나의 매개변수인 PorterDuff.Mode가 있습니다. 문서를 확인하기 위해 코드를 직접 작성했습니다. 18개의 다양한 믹싱 모드 중 18개는 새로 추가된 ADD 및 OVERLAY 모드입니다! 물론, 단순히 알고 있다는 사실을 확인하는 것만으로는 충분하지 않습니다. 이 섹션에서는 실제로 PorterDuff.Mode를 사용하여 제공하는 방법을 익히는 데 도움이 되는 예제를 작성하겠습니다. 이 셔플 모드! 이 섹션에 가져온 예는 원형 및 둥근 모서리 그래픽의 구현입니다!
In 2.3.4 ImageView(이미지 보기) 드디어 가장 간단한 것을 설명했습니다. 원형 ImageView 그리기의 구현은 그림에서 클립패스를 호출하여 원을 자르는 것입니다!
이 섹션은 PorterDuff.Mode의 DST_IN 모드를 사용하여 구현되었습니다. 이제 이 섹션을 시작하겠습니다. 추신: 이 섹션의 예는 Hongyang의 작업에서 가져온 것입니다. - Android 렌더링 다이어그램 및 구현 프로세스 분석:
작업 후 렌더링 다이어그램:
위는 우리가 달성하려는 효과입니다. 이
PorterDuff.Mode.DST_IN 모드를 통해! 구현 프로세스를 분석해 보겠습니다.
- 1단계: Xfermode는 그래프의 두 레이어에 지나지 않습니다. 먼저 그려진 것을 DST 그래프(대상 그래프)라고 하고 나중에 그리는 것을 SRC 그래프(원본 그래프)라고 합니다. 원이나 둥근 모서리의 경우 먼저 표시할 이미지(DST)를 그릴 수 있으며 여기서는 src 속성을 통해 설정합니다. 그런 다음 원과 필렛(SRC)을 그립니다. 표시하려는 부분이 교차하는 부분이며 그림 부분의 내용입니다. 그러니 선택하세요: DST_IN 모드!
- 2단계: 이제 원리를 알았으니 다음으로 ImageView 사용자 정의와 관련된 문제를 고려해야 합니다.
- 그리고 싶은 뷰가 둥글거나 원형이라면 판단할 속성을 추가해야 하며 둥근 모서리에도 원. 코너 반경. 매개변수를 사용하여 사용자 정의 속성(attrs.xml)을 사용한 다음 보기 구성 방법을 사용자 정의할 수 있습니다. 이 매개변수를 제거하세요!
- 그런 다음 이미지 크기를 계산합니다. 우선원을 설정하는 경우 너비와 높이를 일관되게 만들어야 하며 최소값에 따라 onMesure() 메서드를 사용할 수 있습니다. 너비와 높이를 얻으려면 getMeasuredXxx()를 호출하고, 어느 것이 더 작은지 확인하고, setMeasuredDimension(x, x)를 호출하여 너비와 높이를 설정하세요! 그런 다음 onDraw() 메서드에서 이미지 너비와 높이를 가져온 다음 이미지 너비와 높이, 뷰 너비와 높이에 따라 크기 조정 비율을 계산합니다. 그림의 너비와 높이가 View의 너비와 높이와 일치하지 않으면 결과 그림의 너비와 높이는 View의 너비와 높이보다 커야 하므로 더 큰 값을 취하세요!
- 다음은 그림 그리기입니다. 그런 다음 브러시를 초기화한 후 setXfermode를 다음으로 설정합니다. PorterDuff.Mode.DST_IN, 먼저 그림을 그린 다음 그래픽을 그립니다.
- 마지막으로 는 이미지 캐싱에 관한 몇 가지 사항입니다. 여기서 WeakReference는 Draw에서 매번 메모리 할당을 피하기 위해 이미지를 캐시하는 데 사용됩니다. 그리고 다시 그려서 마침내 무효화에서 캐시를 지웁니다!
일반적인 구현 과정은 위와 같습니다. 과정을 알고 나면 코드를 보는 것이 훨씬 간단해질 것입니다!
2. 코드 구현:
사용자 정의 컨트롤 속성: res/attrs.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CircleImageView"> <attr name="Radius" format="dimension"/> <attr name="type"> <enum name="circle" value="0"/> <enum name="round" value="1"/> </attr> </declare-styleable> </resources>
그런 다음 사용자 정의 ImageView: CircleImageView.java:
/** * Created by Jay on 2015/10/25 0025. */ public class CircleImageView extends ImageView { private Paint mPaint; private Xfermode mXfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN); private Bitmap mMaskBitmap; private WeakReference<Bitmap> mWeakBitmap; //图片相关的属性 private int type; //类型,圆形或者圆角 public static final int TYPE_CIRCLE = 0; public static final int TYPE_ROUND = 1; private static final int BODER_RADIUS_DEFAULT = 10; //圆角默认大小值 private int mBorderRadius; //圆角大小 public CircleImageView(Context context) { this(context, null); } public CircleImageView(Context context, AttributeSet attrs) { super(context, attrs); mPaint = new Paint(); mPaint.setAntiAlias(true); //取出attrs中我们为View设置的相关值 TypedArray tArray = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView); mBorderRadius = tArray.getDimensionPixelSize(R.styleable.CircleImageView_Radius, BODER_RADIUS_DEFAULT); type = tArray.getInt(R.styleable.CircleImageView_type, TYPE_CIRCLE); tArray.recycle(); } public CircleImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (type == TYPE_CIRCLE) { int width = Math.min(getMeasuredWidth(), getMeasuredHeight()); setMeasuredDimension(width, width); //设置当前View的大小 } } @Override protected void onDraw(Canvas canvas) { //在缓存中取出bitmap Bitmap bitmap = mWeakBitmap == null ? null : mWeakBitmap.get(); if (bitmap == null || bitmap.isRecycled()) { //获取图片宽高 Drawable drawable = getDrawable(); int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); if (drawable != null) { bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas drawCanvas = new Canvas(bitmap); float scale = 1.0f; if (type == TYPE_ROUND) { scale = Math.max(getWidth() * 1.0f / width, getHeight() * 1.0f / height); } else { scale = getWidth() * 1.0F / Math.min(width, height); } //根据缩放比例,设置bounds,相当于缩放图片了 drawable.setBounds(0, 0, (int) (scale * width), (int) (scale * height)); drawable.draw(drawCanvas); if (mMaskBitmap == null || mMaskBitmap.isRecycled()) { mMaskBitmap = getBitmap(); } mPaint.reset(); mPaint.setFilterBitmap(false); mPaint.setXfermode(mXfermode); //绘制形状 drawCanvas.drawBitmap(mMaskBitmap, 0, 0, mPaint); //bitmap缓存起来,避免每次调用onDraw,分配内存 mWeakBitmap = new WeakReference<Bitmap>(bitmap); //绘制图片 canvas.drawBitmap(bitmap, 0, 0, null); mPaint.setXfermode(null); } } if (bitmap != null) { mPaint.setXfermode(null); canvas.drawBitmap(bitmap, 0.0f, 0.0f, mPaint); return; } } //缓存Bitmap,避免每次OnDraw都重新分配内存与绘图 @Override public void invalidate() { mWeakBitmap = null; if (mWeakBitmap != null) { mMaskBitmap.recycle(); mMaskBitmap = null; } super.invalidate(); } //定义一个绘制形状的方法 private Bitmap getBitmap() { Bitmap bitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); //抗锯齿 paint.setColor(Color.BLACK); if (type == TYPE_ROUND) { canvas.drawRoundRect(new RectF(0, 0, getWidth(), getHeight()), mBorderRadius, mBorderRadius, paint); } else { canvas.drawCircle(getWidth() / 2, getWidth() / 2, getWidth() / 2, paint); } return bitmap; } }
마지막으로 레이아웃 파일에서 호출합니다: activity_main. xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.jay.xfermodedemo1.CircleImageView android:layout_width="160dp" android:layout_height="240dp" android:layout_margin="10dp" android:src="@mipmap/ic_bg_meizi2" app:type="circle" /> <com.jay.xfermodedemo1.CircleImageView android:layout_width="160dp" android:layout_height="280dp" android:layout_margin="10dp" android:src="@mipmap/ic_bg_meizi1" app:Radius="30dp" app:type="round" /> </LinearLayout>
좋아, 코드를 한 번 이해하지 못하더라도 두 번 읽으면 이해할 수 있을 것이다~
3 이 섹션의 코드 샘플을 다운로드하세요:
이 섹션 요약 :
이 섹션에서는 Xfermode 및 PorterDuff의 첫 번째 적용 예를 설명하고 DST_IN 모드를 설정하여 둥글고 둥근 이미지 ImageView의 사용자 정의, 이미 PorterDuff의 간단한 응용 프로그램에 모두가 익숙하다고 생각합니다. 쇠가 뜨거울 때 쳐보세요. 다음에는 실력을 연습할 수 있는 예문을 작성해보겠습니다~알겠습니다. 감사합니다~