1. 程式人生 > >手把手教你做開源專案MyMeiZi 二(使用RecyclerView+Glide打造瀑布流)

手把手教你做開源專案MyMeiZi 二(使用RecyclerView+Glide打造瀑布流)

Glide

一個專注於平滑滾動的圖片載入和快取庫,相信大家也不會陌生,話說我以前一直使用的是Fresco,這次也是為了學習使用了Glide,誰上誰下這個不好區分,但是Glide更輕量級,究竟選擇哪個還要看各位自己了

Github上我們可以看到關於Glide的用法。

// For a simple view:
@Override public void onCreate(Bundle savedInstanceState) {
  ...
  ImageView imageView = (ImageView) findViewById(R.id.my_image_view);

  Glide.with(this
).load("http://goo.gl/gEgYUd").into(imageView); } // For a simple image list: @Override public View getView(int position, View recycled, ViewGroup container) { final ImageView myImageView; if (recycled == null) { myImageView = (ImageView) inflater.inflate(R.layout.my_image_view, container, false
); } else { myImageView = (ImageView) recycled; } String url = myUrls.get(position); Glide .with(myFragment) .load(url) .centerCrop() .placeholder(R.drawable.loading_spinner) .crossFade() .into(myImageView); return myImageView; }

非常的簡單,with裡面可以傳Activity,Fragment,Glide會自動根據你的生命週期來判斷暫停和載入,load可以傳
load

centerCrop和fitCenter就和設定ImageView的scaleType一樣,into就是你的ImageView控制元件了。

RecyclerView

更加靈活的ListView,拔插式的設計能輕而易舉的完成許多效果(拖動排序,滑動刪除),而且使用StaggeredGridLayoutManager可以輕鬆的完成瀑布流樣式

我們今天的主題就是使用Glide+RecyclerView完成MyMeiZi中的老司機介面
老司機
關於從網上用RxJava和Retrofit拿資料我們上一章已經講過了這一次我們直接開始完成UI吧.手把手教你做開源專案MyMeiZi 一( RxJava + Retrofit)

既然RecyclerView自帶瀑布流我們就輕輕鬆鬆的完成吧!

  @Override
    protected ItemHolder onAdapterCreateViewHolder(ViewGroup viewGroup, int viewType) {
        DriverView driverView = new DriverView(mContext);
        ItemHolder itemHolder = new ItemHolder(driverView);
        return itemHolder;
    }

    @Override
    protected void onAdapterBindViewHolder(ItemHolder viewHolder, int position) {
        viewHolder.driverView.setData(mData.get(position));
    }

因為圖片高度不一樣所以才能完成瀑布流樣式,那麼我們該怎麼判斷Item適當的高度呢,因為Item的寬度是固定的,所以我們可以根據寬度得到適當的高度(這叫自問自答).

<android.support.v7.widget.CardView
        android:id="@+id/view_card_feed"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:scrollbars="vertical"
        app:cardCornerRadius="5dp">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <ImageView
                android:id="@+id/view_img_feed"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:scaleType="centerCrop"
                />

        </RelativeLayout>

    </android.support.v7.widget.CardView>

用Glide override和asBitmap的方法得到一個寬度為Item寬度高度為原始尺寸的圖片

   Glide.with(getContext()).load(data.getUrl()).asBitmap().fitCenter().override(mBinding.viewImgFeed.getWidth(), BitmapImageViewTarget.SIZE_ORIGINAL).into(new DriverViewTarget(mBinding.viewImgFeed));

在BitmapImageViewTarget中對Item進行寬高的調整

  private class DriverViewTarget extends BitmapImageViewTarget {

        public DriverViewTarget(ImageView view) {
            super(view);
        }

        @Override
        public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                int viewWidth = mBinding.viewImgFeed.getWidth();
                float scale = resource.getWidth() / (viewWidth * 1.0f);
                int viewHeight = (int) (resource.getHeight() * scale);
                setCardViewLayoutParams(viewWidth, viewHeight);
            super.onResourceReady(resource, glideAnimation);
        }
    }

恩,完美,執行一下!
run
看上去是沒啥問題,瀑布流的樣子也都出來了,不過慢滑和快滑時明顯有一個重新排列的過程,這是因為每次劃出來時會從快取或者網上重新填充,而這個填充快慢就不一定了,可能Item填充順序和第一次載入完畢時不一樣,高度的計算順序也不一樣,這也就導致瞭如圖中的效果,幾個Item會不斷的變換位置~

果然什麼事都沒有那麼簡單啊,不過我們已經知道這是因為高度計算的問題,那麼我們可以新建立一個SizeModel類,來儲存我們第一次載入時計算的高度,以後判斷這個SizeModel如果存在可以直接用width和Height而不用再根據Bitmap重新計算一次,這也是本章的核心內容!!!!

public class SizeModel {

    private String url;
    private int height;
    private int width;

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public boolean isNull() {
        return height == 0 || width == 0;
    }

    public void setSize(int width, int height) {
        this.width = width;
        this.height = height;
    }
}

使用方法,首先在獲取到網路資料時,把圖片的url傳給SizeModel,然後傳給Adapter

  @Override
        public void onNext(GankIoModel.ResultsEntity resultsEntity) {
            mData.add(resultsEntity);
            SizeModel sizeModel = new SizeModel();
            sizeModel.setUrl(resultsEntity.getUrl());
            mSizeData.add(sizeModel);
            feedAdapter.setSizeModel(mSizeData);
            feedAdapter.notifyAdapterItemInserted(mData.size());
        }

然後再由Adapter傳給我們的控制元件

 @Override
    protected void onAdapterBindViewHolder(ItemHolder viewHolder, int position) {
        viewHolder.driverView.setData(mData.get(position), mSizeModel.get(position));
    }

最後我們在控制元件中做處理判斷

 if (!mSizeModel.isNull()) {
            setCardViewLayoutParams(mSizeModel.getWidth(), mSizeModel.getHeight());
        }


 private void setCardViewLayoutParams(int width, int height) {
        ViewGroup.LayoutParams layoutParams = mBinding.viewImgFeed.getLayoutParams();
        layoutParams.width = width;
        layoutParams.height = height;
        mBinding.viewImgFeed.setLayoutParams(layoutParams);
    }


 @Override
        public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
            if (mSizeModel.isNull()) {
                int viewWidth = mBinding.viewImgFeed.getWidth();
                float scale = resource.getWidth() / (viewWidth * 1.0f);
                int viewHeight = (int) (resource.getHeight() * scale);
                setCardViewLayoutParams(viewWidth, viewHeight);
                mSizeModel.setSize(viewWidth, viewHeight);
            }
            super.onResourceReady(resource, glideAnimation);
        }

再次執行~
run

ok,看起來效果還是不錯的,本章內容就到此為止啦