android 瀑布流 图片指定位置圆角

参考的博客地址

RecyclerView瀑布流的那些坑
Recyclerview之瀑布流分割线左右间距均等问题
自定义ImageView,实现圆角矩形、原型、固定宽高比样式
Android内存分析工具:Memory Profiler

2019-07-14 本编文章修改

今天我对这边博客进行了修改,因为我发现在使用CornerTransform圆角工具的时候会出现几个问题,
1、当你不设置imageView的android:scaleType为fitXY,图片会出现上边空白,或者是左右空白的情况,无法填充父容器
2、设置了imageView的android:scaleType为fitXY,第一次加载数据图片处于正常的状态,第二次下拉刷新会导致图片放大拉伸(个别图片)
以上问题都是adapter缓存影响,它并没有去执行CornerTransform的代码,此次修改我会保留部分之前写的,然后文章末尾部分,贴出自己搞的demo。公司的代码跟我自己写的代码是有很多出入的,所以有些问题并没有在我这个demo里面出现

问题点
bug.png
解决办法

1、自定义ImageView,对图片进行一个缩放,每次加载图片的时候获取图片的宽高,与我们要向用户展示的宽高做一个缩放比,缩放比我们需要取最大的值

学习到的知识点

1、BitmapShader是着色器,当我们创建出一个bitmap的时候,需要使用它为其上色
2、Matrix缩放图片,有点类似拉着图片的右下角来进行扩大缩小
3、我们缩放的图片是原图片的bitmap,要先将Drawable转为bitmap,bitmap的宽高都要采用Drawable的,当然了我们最后向用户展示的宽高会和我们原图的宽高不同,所以我们需要对原图的进行缩放

自定义Imageview代码展示
public class MyImageView2 extends android.support.v7.widget.AppCompatImageView {
    //最后确认的宽高
    private RectF drawRectF;
    private Paint mPaint;
    private Matrix matrix;
    private BitmapShader bitmapShader;
    private boolean topLeftRightCorner,bottomLeftRightCorner;//定义上面与下面的是否为圆角
    private int radius = 5;
    public MyImageView2(Context context) {
        this(context,null);
    }

    public MyImageView2(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public MyImageView2(Context context,  AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context,attrs);
    }

    private void init(Context context,  AttributeSet attrs){
        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.MyImageView2);
        topLeftRightCorner = typedArray.getBoolean(R.styleable.MyImageView2_topLeftRightCorner,false);
        bottomLeftRightCorner = typedArray.getBoolean(R.styleable.MyImageView2_bottomLeftRightCorner,false);
        radius = typedArray.getInt(R.styleable.MyImageView2_radius,5);
        typedArray.recycle();
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        matrix = new Matrix();
        radius = dip2px(radius);
    }

    public int getRadius() {
        return radius;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        drawRectF = new RectF(0,0,w,h);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Drawable sourCeDrawable = getDrawable();
        if (sourCeDrawable==null){
            return;
        }
        Bitmap sourceBitMap =  drawable2Bitmap(sourCeDrawable);
        //BitmapShader 为着色器
        bitmapShader = new BitmapShader(sourceBitMap,Shader.TileMode.CLAMP,Shader.TileMode.CLAMP);
       // float scaleMax = 1.0f,scaleX=1.0f,scaleY=1.0f;
        float scaleMax = 1.0f;
        // 如果图片的宽或者高与view的宽高不匹配,计算出需要缩放的比例;缩放后的图片的宽高,一定要大于我们view的宽高;所以我们这里取大值;
        if (getWidth() != sourceBitMap.getWidth() || getHeight() !=sourceBitMap.getHeight()){
           /* scaleX = (float) getWidth() / (float)sourceBitMap.getWidth();
            scaleY = (float)getHeight() / (float)sourceBitMap.getHeight();*/
            scaleMax = Math.max((float) getWidth() / (float)sourceBitMap.getWidth(),(float)getHeight() / (float)sourceBitMap.getHeight());
        }

        //对我们创建出来的bitmap进行缩放
        matrix.setScale(scaleMax,scaleMax);
        bitmapShader.setLocalMatrix(matrix);
        mPaint.setShader(bitmapShader);
        //纠正圆角
//        int radius = (int) (getRadius() * scaleMax);
        //画出我们需要的直角图形
        canvas.drawRoundRect(drawRectF,radius,radius,mPaint);

        if (topLeftRightCorner){
            //左边底部
            canvas.drawRect(0,canvas.getHeight() - radius ,radius,canvas.getHeight(),mPaint);
            //右边底部
            canvas.drawRect(canvas.getWidth() - radius, canvas.getHeight() - radius,canvas.getWidth(),canvas.getHeight(),mPaint);
        }

        if (bottomLeftRightCorner){
            //左边顶部
            canvas.drawRect(0,0,radius,radius,mPaint);
            //右边顶部
            canvas.drawRect(canvas.getWidth() - radius,0,canvas.getWidth(),radius,mPaint);
        }
    }

    public  int dip2px(float dipValue){
        final float scale = Resources.getSystem().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }
    //Drawable 转bitmap
    private Bitmap drawable2Bitmap(Drawable drawable){
        //获取 Drawable 的内部宽高,包含 padding
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
        //创建一个w,h的bitmap
        Bitmap bitmap = Bitmap.createBitmap(width,height,Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        //设置绘画区域
        drawable.setBounds(0,0,width,height);
        drawable.draw(canvas);
        return bitmap;
    }
}

topLeftRightCorner,bottomLeftRightCorner表示上部份和下部分是否为圆角,radius是圆角的半径

attrs.xml



    
        
        
        
    

xml


    
    
    

gitHub:https://github.com/caocao123/loadMoae

以下部分是之前的代码和描述
需求

公司最近说要做瀑布流的效果,要求显示的图片顶部才有圆角,底部是没有圆角的,如下效果图


正确.png
学习到的知识点

1、view.getLayoutParams().getSpanIndex()是获取某一个View左右空白的下标
2、解决上拉,下拉闪烁问题

 int start = findsModeList.size();//findsModeList表示是全局的list
 findsModeList.addAll(list);//这里的list表示是从服务器返回的10条item数据集合
 if (p!=1){//加载更多
    adapter.notifyItemInserted(start);
  }else {//下拉刷新
     adapter.notifyDataSetChanged();
  }

3、解决从底部滑动到顶部,导致的换位问题

 refreshRecyclerView.getRecyclerView().addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                int[] first = new int[2];
                staggeredGridLayoutManager.findFirstCompletelyVisibleItemPositions(first);
                if (newState == RecyclerView.SCROLL_STATE_IDLE && (first[0] == 1 || first[1] == 1)) {
                    staggeredGridLayoutManager.invalidateSpanAssignments();//刷新间隔
                }
            }
        });
实现思路与部分代码

1、在item.xml里面,父容器与ImageView的layout_width都要为match_parent


    
.....其它控件

2、为了防止会出现错位的问题,我们需要后台提供每张图片的高度,宽度,然后动态的设置ImageView的LayoutParams.height

3、在拿到后台给我们的数据的时候,我们需要对数据进行处理,因为有可能后台给的高度是1080px、或者是1920px等,我们在展示ImageView的时候,是不会展示这么高的图片出来

    //图片最大的高度,图片最小的高度
    private int IMAGE_MAXH = 0;
    private int IMAGE_MINH = 0;
    private int IMAGEDEFAULT = 0;//每个Item的宽度
   IMAGE_MAXH = this.getResources().getDimensionPixelOffset(R.dimen.dp_215);
   IMAGE_MINH = this.getResources().getDimensionPixelOffset(R.dimen.dp_172);
//设置decoration
    StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
    StaggeredGridLayoutSpaceItemDecoration dividler = new StaggeredGridLayoutSpaceItemDecoration(getResources().getDimensionPixelOffset(R.dimen.dp_10), 2);
    int phoneWidth = ScreentUtil.getInstance().getScreenWidth(mContext);//获取手机的横项PX
//一般来说,我们现实瀑布流,每个Item之间都会有间隔,所以我这里dp_10,表示的是Item之间的间隔
    IMAGEDEFAULT = (phoneWidth - getResources().getDimensionPixelOffset(R.dimen.dp_10) * 3) / 2;//得到每个Item的宽度

我们需要给图片设置一个最大的高度,与最小的高度,现在我们有1点可以确认的就是每个item的宽度是多少了,float 缩放比 = 每个Item宽度/服务器给的宽度,然后我们就可以算出我们需要展示的高度了

             if (list.size() > 0) {
                    for (FindsMode findsMode : list) {
                        findsMode.setBanner(SPUtils.returnHaveHttpoHttps(findsMode.getBanner()));
                        findsMode.setHead_pic(SPUtils.returnHaveHttpoHttps(findsMode.getHead_pic()));
                        float scale = (float) IMAGEDEFAULT / (float) findsMode.getBanner_w();
                        int showHeight = (int) (findsMode.getBanner_h() * scale);
                        if (showHeight >= IMAGE_MAXH) {
                            showHeight = IMAGE_MAXH;
                        } else if (showHeight <= IMAGE_MINH) {
                            showHeight = IMAGE_MINH;
                        }else {
                            showHeight = IMAGE_MAXH;
                        }
                        findsMode.setShowBannerH(showHeight);
                    }
                }
 CornerTransform  transformation = new CornerTransform(SPMobileApplication.getInstance(),findFragment.getActivity().getResources().getDimensionPixelSize(R.dimen.dp_5));
 //只是绘制左上角和右上角圆角
 transformation.setExceptCorner(false, false, true, true);

@Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        FindsMode findsMode = list.get(position);
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) holder.imageView.getLayoutParams();
        layoutParams.width = LinearLayout.LayoutParams.MATCH_PARENT;
        layoutParams.height = findsMode.getShowBannerH();//设置算出的高度
        holder.imageView.setLayoutParams(layoutParams);
        //设置圆角
        Glide.with(SPMobileApplication.getInstance()).
                load(findsMode.getBanner()).
                asBitmap().
                diskCacheStrategy(DiskCacheStrategy.SOURCE).
                transform(transformation).
                into(holder.imageView);

期间遇到的问题

1、列表中图片没有显示出来,需要重新adapter一下才会出现

//最开始我想的是图片是需要一个固定的宽高,否则会导致界面错乱
        LinearLayout.LayoutParams layoutParams = (LinearLayout.LayoutParams) holder.imageView.getLayoutParams();
        layoutParams.height = findsMode.getShowBannerH();
        layoutParams.width = holder.itemView.getWidth();//问题在这里,这里获取的宽度是0
        holder.imageView.setLayoutParams(layoutParams);

2、顶部圆角只有1边显示出来了


错误.png
圆角工具

工具博客地址:https://blog.csdn.net/mchenys/article/details/80284132

public class CornerTransform implements Transformation {
    private BitmapPool mBitmapPool;

    private float radius;

    private boolean exceptLeftTop, exceptRightTop, exceptLeftBottom, exceptRightBotoom;

    /**
     * 除了那几个角不需要圆角的
     *
     * @param leftTop
     * @param rightTop
     * @param leftBottom
     * @param rightBottom
     */
    public void setExceptCorner(boolean leftTop, boolean rightTop, boolean leftBottom, boolean rightBottom) {
        this.exceptLeftTop = leftTop;
        this.exceptRightTop = rightTop;
        this.exceptLeftBottom = leftBottom;
        this.exceptRightBotoom = rightBottom;
    }

    public CornerTransform(Context context, float radius) {
        this.mBitmapPool = Glide.get(context).getBitmapPool();
        this.radius = radius;
    }

    public float getRadius() {
        return radius;
    }
    
    @Override
    public Resource transform(Resource resource, int outWidth, int outHeight) {
        //最好是以resource的宽/高作为最后的宽/高。这样显示出来的结果不会变形
        Bitmap source =  resource.get();
        int finalWidth, finalHeight;
        float ratio; //输出目标的宽高或高宽比例
        if (outWidth > outHeight) { //输出宽度>输出高度,求高宽比
            ratio = (float) outHeight / (float) outWidth;
            finalWidth = source.getWidth();
            finalHeight = (int) ((float) source.getWidth() * ratio); //固定原图宽度,求最终高度
            if (finalHeight > source.getHeight()) { //求出的最终高度>原图高度,求宽高比
                ratio = (float) outWidth / (float) outHeight;
                finalHeight = source.getHeight();
                finalWidth = (int) ((float) source.getHeight() * ratio);//固定原图高度,求最终宽度
            }
        } else if (outWidth < outHeight) { //输出宽度 < 输出高度,求宽高比
            ratio = (float) outWidth / (float) outHeight;
            finalHeight = source.getHeight();
            finalWidth = (int) ((float) source.getHeight() * ratio);//固定原图高度,求最终宽度
            if (finalWidth > source.getWidth()) { //求出的最终宽度 > 原图宽度,求高宽比
                ratio = (float) outHeight / (float) outWidth;
                finalWidth = source.getWidth();
                finalHeight = (int) ((float) source.getWidth() * ratio);
            }
        } else { //输出宽度=输出高度
            finalHeight = source.getHeight();
            finalWidth = finalHeight;
        }

        float radiusC = getRadius() * ((float) finalHeight / (float) outHeight);
        Bitmap outBitmap = this.mBitmapPool.get(finalWidth, finalHeight, Bitmap.Config.ARGB_8888);
        if (outBitmap == null) {
            outBitmap = Bitmap.createBitmap(finalWidth, finalHeight, Bitmap.Config.ARGB_8888);
        }

        Canvas canvas = new Canvas(outBitmap);
        Paint paint = new Paint();
        //关联画笔绘制的原图bitmap
        BitmapShader shader = new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
        //计算中心位置,进行偏移
        int width = (source.getWidth() - finalWidth) / 2;
        int height = (source.getHeight() - finalHeight) / 2;
       //将大图进行平移的操作
        if (width != 0 || height != 0) {
            Matrix matrix = new Matrix();
            matrix.setTranslate((float) (-width), (float) (-height));
            shader.setLocalMatrix(matrix);
        }

        paint.setShader(shader);
        paint.setAntiAlias(true);
        RectF rectF = new RectF(0.0F, 0.0F, (float) canvas.getWidth(), (float) canvas.getHeight());
        canvas.drawRoundRect(rectF, radiusC, radiusC, paint); //先绘制圆角矩形

        if (exceptLeftTop) { //左上角不为圆角
            canvas.drawRect(0, 0, radiusC, radiusC, paint);
        }
        if (exceptRightTop) {//右上角不为圆角
            canvas.drawRect(canvas.getWidth() - radiusC, 0, canvas.getWidth(), radiusC, paint);
        }

        if (exceptLeftBottom) {//左下角不为圆角
            canvas.drawRect(0, canvas.getHeight() - radiusC, radiusC, canvas.getHeight(), paint);
        }

        if (exceptRightBotoom) {//右下角不为圆角
            canvas.drawRect(canvas.getWidth() - radiusC, canvas.getHeight() - radiusC, canvas.getWidth(), canvas.getHeight(), paint);
        }

        return BitmapResource.obtain(outBitmap, this.mBitmapPool);
    }


    @Override
    public String getId() {
        return this.getClass().getName();
    }
}

你可能感兴趣的:(android 瀑布流 图片指定位置圆角)