【自定义View】根据鸿洋思路仿写知乎滑动广告

一、先贴上鸿洋大神的网址:

     点我传送


二、所学所获

     1.思路:

        1、根据View在ListView或RecyclerView中的滑动,根据滑动的dY值显示ImageView的不同部分。

        2、ImageView显示不同部分使用画布的translate方法进行。


      2.学习知识点:

        (1) ImageView中获取图片,使用getDrawable();

        (2)从drawable转化成bitmap的方法:

             1、获取drawable的宽高,drawable.getIntrinsicWidth 和 drawable.getIntrinsicHeight;

             2、新建一个Bitmap,通过Bitmap.create();传入drawable的宽高

             3、新建一个Canvas,传入这个Bitmap

             4、drawable调用setBound方法,目的是取drawable画在画布上的大小

             5、drawble.draw(canvas),这样drawable就画在了bitmap上

         (3)获取RecyclerView的高度,需要调用它的LayoutManager.getHeight

         (4)画布的偏移方法translate

         (5)画布在偏移前调用save方法,然后再偏移translate,最后在restore,这样画布就还原到了save之前的状态!


三、实现方法:

       1、自定义一个ImageView继承AppCompatImageView,这样就不用去担心获取不到图片了

       2、利用onSizeChange去获取ImageView的宽高,并且给图片设置一个最小的偏移Y值就是ImageView的高度

       3、获取图片的尺寸大小,用RectF去获取

       这样就得到了:1、图片显示的最小高度(ImageView的高度)

                                    2、Bitmap的尺寸,放在RectF中了

        

 @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        minY = h;

        mBitmap = drawable2Bitmap();

        mBitmapRectf = new RectF(0,0,w,((float)mBitmap.getHeight()) / mBitmap.getWidth() * w);
    }

    private Bitmap drawable2Bitmap(){
        Drawable drawable = getDrawable();
        if(drawable == null){
            return null;
        }

        if(drawable instanceof BitmapDrawable){
            return ((BitmapDrawable) drawable).getBitmap();
        }

        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();

        Bitmap bitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0,0,w,h);
        drawable.draw(canvas);
        return bitmap;
    }

         4、对外暴露一个设置偏移量的接口,这里算法有很多,我选择的是获取控件相对于父布局顶部的距离:

       

    public void setDy(float dy, float maxHeight){
        //dy为RecyclerView.height - view.getTop,这样往上滑的时候dy越来越大

        //从底部往上时,还没有完全显示时,不偏移
        if(dy < minY){
            mDy = 0;
        } else if(dy > maxHeight){ //滑动到RecyclerView顶部了也就不再偏移了(再偏移就出去了!)
            mDy = mBitmapRectf.height() - minY;
        } else {
            //计算从图片完全显示到图片滑到顶部的百分比,这样刚好滑到最上面时,图片偏移到最大
            mDy = (dy - minY) / (maxHeight - minY) * (mBitmapRectf.height() - minY);
            if(mDy > mBitmapRectf.height() - minY){
                mDy = mBitmapRectf.height() - minY;
            }
        }
        invalidate();
    }

           5、在onDraw方法中偏移:

           

    @Override
    protected void onDraw(Canvas canvas) {
//        super.onDraw(canvas);

        if(getDrawable() == null) return;
        canvas.save();
        canvas.translate(0, -mDy);
        canvas.drawBitmap(mBitmap,null,mBitmapRectf,null);
        canvas.restore();
    }

         6、在RecyclerView的滑动监听中监听滑动事件:

        

        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                int first = mManager.findFirstVisibleItemPosition();
                int last = mManager.findLastVisibleItemPosition();
                for (int i = first; i <= last ; i++){
                    View view = mManager.findViewByPosition(i);
                    AdImageView adImageView = (AdImageView) view.findViewById(R.id.imageView);
                    if(adImageView.getVisibility() == View.VISIBLE){
                        adImageView.setDy(mManager.getHeight() - view.getTop(), mManager.getHeight());
                    }
                }
            }
        });


    最后贴上AdImageView的完整代码:

public class AdImageView extends AppCompatImageView{

    private int minY;

    private Bitmap mBitmap;
    private RectF mBitmapRectf;

    private float mDy;

    public AdImageView(Context context) {
        this(context,null);
    }

    public AdImageView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public AdImageView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);

        minY = h;

        mBitmap = drawable2Bitmap();

        mBitmapRectf = new RectF(0,0,w,((float)mBitmap.getHeight()) / mBitmap.getWidth() * w);
    }

    private Bitmap drawable2Bitmap(){
        Drawable drawable = getDrawable();
        if(drawable == null){
            return null;
        }

        if(drawable instanceof BitmapDrawable){
            return ((BitmapDrawable) drawable).getBitmap();
        }

        int w = drawable.getIntrinsicWidth();
        int h = drawable.getIntrinsicHeight();

        Bitmap bitmap = Bitmap.createBitmap(w,h, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0,0,w,h);
        drawable.draw(canvas);
        return bitmap;
    }

    public void setDy(float dy, float maxHeight){
        //dy为RecyclerView.height - view.getTop,这样往上滑的时候dy越来越大

        //从底部往上时,还没有完全显示时,不偏移
        if(dy < minY){
            mDy = 0;
        } else if(dy > maxHeight){ //滑动到RecyclerView顶部了也就不再偏移了(再偏移就出去了!)
            mDy = mBitmapRectf.height() - minY;
        } else {
            //计算从图片完全显示到图片滑到顶部的百分比,这样刚好滑到最上面时,图片偏移到最大
            mDy = (dy - minY) / (maxHeight - minY) * (mBitmapRectf.height() - minY);
            if(mDy > mBitmapRectf.height() - minY){
                mDy = mBitmapRectf.height() - minY;
            }
        }
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
//        super.onDraw(canvas);

        if(getDrawable() == null) return;
        canvas.save();
        canvas.translate(0, -mDy);
        canvas.drawBitmap(mBitmap,null,mBitmapRectf,null);
        canvas.restore();
    }
}


      

猜你喜欢

转载自blog.csdn.net/vicwudi/article/details/78868719