一、自定义ReleativeLayout圆角化
实现:
1.在res目录中新建attrs.xml文件,自定义属性如下。
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="RoundBgRelativeLayout"> <attr name="borderRadius" format="dimension"/><!-- 半径--> <attr name="src" format="reference"/><!-- 图片资源--> </declare-styleable> </resources>
2.新建自定义Layout继承RelativeLayout,重写构造方法。
public class RoundBgRelativeLayout extends RelativeLayout { /** * 圆角大小 */ private int mRadius; /**背景图片*/ private Bitmap mSrc; public RoundBgRelativeLayout(Context context) { this(context,null); } public RoundBgRelativeLayout(Context context, AttributeSet attrs) { this(context, attrs,0); } public RoundBgRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); setWillNotDraw(false); TypedArray arr = context.getTheme().obtainStyledAttributes(attrs, R.styleable.RoundBgRelativeLayout, defStyleAttr, 0); int indexCount = arr.getIndexCount(); for (int i = 0; i < indexCount; i++) { int index = arr.getIndex(i); switch (index){ case R.styleable.RoundBgRelativeLayout_src: mSrc = BitmapFactory.decodeResource(getResources(), arr.getResourceId(index, 0)); break; case R.styleable.RoundBgRelativeLayout_borderRadius: mRadius= (int) arr.getDimension(index,20); break; default: break; } } arr.recycle(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); if(mSrc != null){ int width = getMeasuredWidth();//测量宽度 int height = getMeasuredHeight();//测量高度 mSrc = Bitmap.createScaledBitmap(mSrc,width,height,false); canvas.drawBitmap(createRoundImage(mSrc,width,height),0,0,null);//绘制圆角背景 } super.onDraw(canvas); } private Bitmap createRoundImage(Bitmap mSrc, int width, int height) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); Bitmap target = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(target); RectF rectF = new RectF(0,0,width,height); //绘制圆角矩形 canvas.drawRoundRect(rectF,mRadius,mRadius,paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));//该模式可以让两个重叠,并取交集 //绘制图片 canvas.drawBitmap(mSrc,0,0,paint); return target; } public void setBgResource(int r){ this.mSrc = BitmapFactory.decodeResource(getResources(),r); invalidate(); } }
实现原理:
主要靠PorterDuff.Mode.SRC_IN 这种模式,第一个图绘制为圆角矩形,第二个图绘制是个BItmap,两者取交集,就实现了圆形图片的效果。
PorterDuff.Mode 16种效果图,如下。
最终实现:
参考
1.鸿神:http://blog.csdn.net/lmj623565791/article/details/24555655
2.https://blog.csdn.net/fhkatuz674/article/details/39271581
二、自定义view实现图片的裁剪
这个必须记录一下,为了这个坑,耗费了一下午的时间,一直达不到想要的效果,裁剪出来的图一直带着黑色框,究其原因,竟然是我无意识的把Bitmap的分辨率从Bitmap.Config.ARGB_8888手贱改成了Bitmap.Config.ARGB_565的原因。
android中图片是以bitmap形式存在的,那么bitmap所占内存,直接影响到了应用所占内存大小。
其中,A代表透明度;R代表红色;G代表绿色;B代表蓝色。
- ALPHA_8:表示8位Alpha位图,即A=8,一个像素点占用1个字节,它没有颜色,只有透明度
- ARGB_4444:表示16位ARGB位图,即A=4,R=4,G=4,B=4,一个像素点占4+4+4+4=16位,2个字节
- ARGB_8888:表示32位ARGB位图,即A=8,R=8,G=8,B=8,一个像素点占8+8+8+8=32位,4个字节
- RGB_565:表示16位RGB位图,即R=5,G=6,B=5,它没有透明度,一个像素点占5+6+5=16位,2个字节
另外, bitmap经compress后保存jpg,原透明部分会自动填充为黑色,png则保持原色。
1.准备图片2张
2.实现
attr文件中定义属性:
<declare-styleable name="channelview">
<attr name="solidColor" format="reference|color" />
<attr name="src" format="reference" />
</declare-styleable>
主要代码如下:
mSrc为黑色图片,newBitmap为要裁剪的图片。
private Bitmap getImage(Bitmap bitmap, int width, int height) {
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
Bitmap target = mSrc.copy(Bitmap.Config.ARGB_8888, true);
Canvas canvas = new Canvas(target);
canvas.drawBitmap(mSrc,0,0,paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap,width,height,false);
canvas.drawBitmap(newBitmap,0,0,paint);
return target;
}
完整代码:
package com.ingtube.common.widget; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.view.LayoutInflater; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import com.bumptech.glide.Glide; import com.ingtube.common.R; import com.ingtube.common.util.YTSizeUtil; import java.util.ArrayList; import java.util.List; import io.reactivex.Observable; import io.reactivex.ObservableEmitter; import io.reactivex.ObservableOnSubscribe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.functions.Consumer; import io.reactivex.schedulers.Schedulers; import static android.graphics.Bitmap.Config.ARGB_8888; public class ChannelViewWidget extends FrameLayout { private Context context; private int solidColor; private static Bitmap mSrc; public ChannelViewWidget(Context context) { this(context,null); } public ChannelViewWidget(Context context, AttributeSet attrs) { super(context, attrs); this.context = context; TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.channelview); solidColor = typedArray.getColor(R.styleable.channelview_solidColor, getResources().getColor(android.R.color.transparent)); mSrc = BitmapFactory.decodeResource(getResources(), typedArray.getResourceId(R.styleable.channelview_src,R.drawable.ic_channel_icon_black)); } private Bitmap getImage(Bitmap bitmap, int width, int height) { Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); Bitmap target = mSrc.copy(Bitmap.Config.ARGB_8888, true); Canvas canvas = new Canvas(target); canvas.drawBitmap(mSrc,0,0,paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap,width,height,false); canvas.drawBitmap(newBitmap,0,0,paint); paint.setXfermode(null); return target; } public void setData(final List<String> channels){ if(channels == null || channels.size() == 0) return; Disposable subscribe = Observable.create(new ObservableOnSubscribe<List<Bitmap>>() { @Override public void subscribe(final ObservableEmitter<List<Bitmap>> e) throws Exception { List<Bitmap> bitmaps = new ArrayList<>(); for (int i = 0; i < channels.size(); i++) { Bitmap bitmap = Glide.with(context).asBitmap().load(channels.get(i)).submit((int)YTSizeUtil.Companion.dp2px(context,24),(int)YTSizeUtil.Companion.dp2px(context,24)).get(); bitmaps.add(i == 0?bitmap.copy(ARGB_8888,false):getImage(bitmap, (int) YTSizeUtil.Companion.dp2px(context, 24), (int) YTSizeUtil.Companion.dp2px(context, 24))); } e.onNext(bitmaps); e.onComplete(); } }) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Consumer<List<Bitmap>>() { @Override public void accept(List<Bitmap> bitmaps) throws Exception { if (bitmaps.size() == channels.size()) { for (int i = 0; i < bitmaps.size(); i++) { addView(addChildView(bitmaps.get(i), i, bitmaps.size())); } } } }, new Consumer<Throwable>() { @Override public void accept(Throwable throwable) throws Exception { throwable.printStackTrace(); } }); } private View addChildView(Bitmap bitmap, int index,int size) { View inflate = null; inflate= LayoutInflater.from(context).inflate(R.layout.exp_item_channel, this, false); ImageView ivChannel = inflate.findViewById(R.id.iv_channel_image); Glide.with(context).asBitmap().load(bitmap).circleCrop().into(ivChannel); ((LayoutParams) inflate.getLayoutParams()).rightMargin = (int) YTSizeUtil.Companion.dp2px(context, 20) * (size-index-1); return inflate; } }
实现效果:
图片压缩资料参考: