Androidはクリエイティブ広告のように遊ぶことができますか?

19399 ワード

本文はすでに私の公衆番号hongyangAndroidのオリジナルで先発しました.
一、概説
この間、非常に特色のある広告の表現方法を見たようです.つまり、リストページには、あるItemが背後の広告図を表示し、リストがスクロールするにつれて、すべての画像が徐々に表示されます.
ちょうど见た时に実现したいと思って、ずっと怠け者で、公众号の楽屋もどのように実现するかを闻く人がいて、今日みんなに说明して、もちろん、今いくつかのカスタマイズのViewはすでに难题ではありませんて、だから本文の说明はいくつか実现の考え方の导きをして、そんなに退屈な文章ではないと信じて、みんなに対して一定の助けがあることを望みます.
うん、今はもうこの効果が見つからないことを知っていて、いくつかの歴史バージョンを試しても見つからなかったので、実現した効果図を貼るしかありません.
効果図は以下の通りです.
2選1、どの効果図が好きですか~~
二、考え方
では、他のものを捨てて、本稿の目標を確定します.
リストに画像を表示することを実装します.
上へスクロール:画像が現れたばかりの時に上部部分を表示し、スクロール部分がすべて下へスクロールするにつれて:画像が現れたばかりの時に下部部分を表示し、スクロール部分がすべてを表示するにつれて
すなわち、リストをスクロールするときに、画像の表示部分を変更する必要があります.
2つのポイント:
  • リストスクロールのdyをキャプチャし、ListViewでもRecyclerViewでも
  • が可能だと信じています.
  • 画像の表示部分の変化はcanvasを利用することができます.translate

  • 結合すると、リストのスクロールdyをリスニングし、私たちの画像コントロールに伝え、translateを設定し、描画します.
    ここまで来ると、考えがはっきりしていて、これはきっとできるに違いない.
    初歩案:Viewをカスタマイズし、自分でbitmapを描き、setDy(dy)を外部に露出し、dyに基づいてcanvasオフセットを再描画すればよい.
    初歩的な案があって、ほとんど慌てないで、それでは更に考えますか?
    ImageViewなど、既存のコントロールを利用できますか?
    間違いなく、画像を受け入れる属性を宣言するのを省いて、サブクラスを作成して、依然としてsrcを設定することで使用します.
    それはImageViewを継承して実現してからにしよう.
    コードを開始する前に、私の公衆番号を押してください.
    Android関連技術に専念~
    三、実現
    まず、偽のリストを書きます.RVがますます増えているので、RecyclerViewを使いましょう.
    レイアウト
    プライマリ・レイアウト・ファイル:RecyclerView 1
    "1.0" encoding="utf-8"?>
    "http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:id="@+id/id_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
     />
    
    

    itemレイアウトファイル:
    xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/item_bg"
        android:gravity="center">
    
        <com.imooc.rvimageads.AdImageViewVersion1
            android:id="@+id/id_iv_ad"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            android:scaleType="matrix"
            android:src="@mipmap/grsm"
            android:visibility="gone" />
    
        <TextView
            android:layout_margin="12dp"
            android:id="@+id/id_tv_title"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="  title"
            android:textSize="16dp"
            android:textStyle="bold" />
    
        <TextView
            android:id="@+id/id_tv_desc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@id/id_tv_title"
            android:layout_marginLeft="12dp"
            android:layout_marginRight="12dp"
            android:layout_marginBottom="12dp"
            android:text="    " />
    
    RelativeLayout>
    
    

    簡単ですが、AdImageViewVersion1クラスにかかわらず、これは私たちの具体的な実装クラスになります.レイアウトファイルを使用すると、itemレイアウトファイルが1つしか使用されず、visible、gone制御によって異なる形態が表示されることがわかります.
    Activity
    
    public class MainActivity extends AppCompatActivity {
    
        private RecyclerView mRecyclerView;
        private LinearLayoutManager mLinearLayoutManager;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            mRecyclerView = findViewById(R.id.id_recyclerview);
    
            List mockDatas = new ArrayList<>();
            for (int i = 0; i < 100; i++) {
                mockDatas.add(i + "");
            }
    
            mRecyclerView.setLayoutManager(mLinearLayoutManager = new LinearLayoutManager(this));
    
            mRecyclerView.setAdapter(new CommonAdapter(MainActivity.this,
                    R.layout.item,
                    mockDatas) {
                @Override
                protected void convert(ViewHolder holder, String o, int position) {
                    if (position > 0 && position % 6 == 0) {
                        holder.setVisible(R.id.id_tv_title, false);
                        holder.setVisible(R.id.id_tv_desc, false);
                        holder.setVisible(R.id.id_iv_ad, true);
                    } else {
                        holder.setVisible(R.id.id_tv_title, true);
                        holder.setVisible(R.id.id_tv_desc, true);
                        holder.setVisible(R.id.id_iv_ad, false);
                    }
                }
            });
    }
           
    

    データを設定しただけで、Adapterはここで使いました.
    compile 'com.zhy:base-rvadapter:3.0.3'
    

    自分の好きなAdapterパッケージクラスを勝手に使ってもいいです.
    ここまで、1つのリストページが表示され、6つおきに画像として表示されます.
    スクリーンショットしないで、脳は...
    今から本格的に実現します.
    カスタムAdImageView
    public class AdImageViewVersion1 extends AppCompatImageView {
        public AdImageViewVersion1(Context context, @Nullable AttributeSet attrs) {
            super(context, attrs);
        }
    
        private RectF mBitmapRectF;
        private Bitmap mBitmap;
    
        private int mMinDy;
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
    
            mMinDy = h;
            Drawable drawable = getDrawable();
    
            if (drawable == null) {
                return;
            }
    
            mBitmap = drawableToBitamp(drawable);
            mBitmapRectF = new RectF(0, 0,
                    w,
                    mBitmap.getHeight() * w / mBitmap.getWidth());
    
        }
    
    
        private Bitmap drawableToBitamp(Drawable drawable) {
            if (drawable instanceof BitmapDrawable) {
                BitmapDrawable bd = (BitmapDrawable) drawable;
                return bd.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;
        }
    
    	// ...       
    }
    

    描画するのでdrawableをbitmapに変換し、デフォルトでは最下部を表示するので、コントロールの高さという最小のオフセットが必要です.
    これらのことは、私たちはonSizeChangedでした.
    また,現在のコントロール幅に基づいてbitmapをスケーリングし,スケーリング後の寸法をmBitmapRectFに存在させて描画した.
    次に描画します.描画中、translateを利用して描画領域を制御していたことを覚えています.そのため、setDyメソッドを外部に暴露しなければなりません.so、私たちのコードは大体次のようになっています.
    private int mDy;
    
    public void setDy(int dy) {
    
        if (getDrawable() == null) {
            return;
        }
        mDy = dy - mMinDy;
        if (mDy <= 0) {
            mDy = 0;
        }
        if (mDy > mBitmapRectF.height() - mMinDy) {
            mDy = (int) (mBitmapRectF.height() - mMinDy);
        }
        invalidate();
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        if (mBitmap == null) {
            return;
        }
        canvas.save();
        canvas.translate(0, -mDy);
        canvas.drawBitmap(mBitmap, null, mBitmapRectF, null);
        canvas.restore();
    }
    

    setDyの時、私たちは境界判断をして、最小の情況、私たちは-mMinDyをオフセットして、画像の底を表示します.最大の場合、画像の高さ-mMinDyをオフセットし、上部を表示します.
    そこで,伝達された値について最小値と最大値の判断を行った.
    では、描くときは簡単です.まずtranslate dy距離を引いてから描きます.
    ここまでカスタマイズしたView部分が終わり、コードが少ない~
    RecyclerViewと組み合わせる
    次は、RecyclerViewでスクロールするときにdyを入力すればいいです.
    mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
    
            int fPos = mLinearLayoutManager.findFirstVisibleItemPosition();
            int lPos = mLinearLayoutManager.findLastCompletelyVisibleItemPosition();
            for (int i = fPos; i <= lPos; i++) {
                View view = mLinearLayoutManager.findViewByPosition(i);
                AdImageViewVersion1 adImageView = view.findViewById(R.id.id_iv_ad);
                if (adImageView.getVisibility() == View.VISIBLE) {
                    adImageView.setDy(mLinearLayoutManager.getHeight() - view.getTop());
                }
            }
        }
    });
    

    addOnScrollListenerで傍受し、スクロールすると表示されているすべてのItemを取得し、画像が表示されているItemを見つけます.次いでsetDyが呼び出され、dyの値はmLinearLayoutManager.getHeight() - view.getTop()であり、Viewが最底部から現れたときは0であり、Viewが最頂部に達したときは現在のrvの高さである.
    setDyが入力した値を合理的に利用して、移動差をしたり、表示領域を上から下にしたりすることができます.
    これで完成~~
    一言で実現:スクロール時にdyを絶えず変更し、translateで描画すればよい.
    四、もう一度考えてみる
    ここまではもう実現しましたが、あなたは満足していますか?考えてないの?
    コードを振り返ると、このコードは、drawableToBitampが非常に不快に見え、メモリを消費している部分でもあるようです.考えてみましょう
    自分がDrawableで描くことができるのに、なぜbitmapに変えるのでしょうか.
    なるほど、ImageView自体がDrawableを描いているようですが、私たちが制御しなければならないのは、このDrawableの描画範囲が十分に大きく、コントロール自体の幅に影響されず、画像が潰されることです.
    そんな方法があるようです.
    drawable.setBounds();
    

    それは簡単です.drawable 2 bitmapのコードを除去し、元の描画を直接利用すればいいです.私たちが唯一しなければならないのはboundsを設定し、translate dyを作ることです.
    完全なコード:
    public class AdImageView extends AppCompatImageView {
        //       
        
        private int mDx;
        private int mMinDx;
    
        public void setDx(int dx) {
            if (getDrawable() == null) {
                return;
            }
            mDx = dx - mMinDx;
            if (mDx <= 0) {
                mDx = 0;
            }
            if (mDx > getDrawable().getBounds().height() - mMinDx) {
                mDx = getDrawable().getBounds().height() - mMinDx;
            }
            invalidate();
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            mMinDx = h;
        }
    
        public int getDx() {
            return mDx;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
    
            Drawable drawable = getDrawable();
            int w = getWidth();
            int h = (int) (getWidth() * 1.0f / drawable.getIntrinsicWidth() * drawable.getIntrinsicHeight());
            drawable.setBounds(0, 0, w, h);
            canvas.save();
            canvas.translate(0, -getDx());
            super.onDraw(canvas);
            canvas.restore();
        }
    }
    

    短いコードで実現したので、目が楽になりました~~
    効果図を貼り付けます.
    効果図は主に字を読んで、あなたは知っています!
    さて、本編のまとめ:
  • は、効果を見ると、まずそれを分割し、キーを見つけ、各キーに対して実行可能性を考慮することができます.
  • 各点が実行可能であると判断すると、基本的なスキームが出てくる.
  • には基本的な案がありますから、焦らずに書いて、改善の余地があるかどうかを考えてみましょう.

  • 例は簡単だ
    github.com/hongyangAnd…