zoukankan      html  css  js  c++  java
  • 自定义View实现圆角化/自定义view实现图片的裁剪

    一、自定义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;
        }
    }
    ChannelViewWidget

    实现效果:

    图片压缩资料参考:

    1.bitmap的六种压缩方式,Android图片压缩

  • 相关阅读:
    简单dp总结
    一、极限总结
    最短路径之差分约束
    软工个人总结
    BETA事后总结
    BETA(7)
    BETA(6)
    BETA(5)
    Go 中的字符串相关操作
    Go 中的异常/错误处理
  • 原文地址:https://www.cnblogs.com/fangg/p/11083230.html
Copyright © 2011-2022 走看看