天天看點

Android使用RecyclerView和StaggeredGridLayoutManager實作瀑布流效果-實作7、實作StaggeredFormalViewHolder完成正常資料展示

版權聲明:本文為部落客原創文章,未經部落客允許不得轉載。https://blog.csdn.net/sinat_25074703/article/details/82981668

經過建構基礎代碼架構、建立StaggeredAdapter擴充卡、實作空界面效果之後,瀑布流的實作隻是一個在StaggeredAdapter中添加ViewType類型的問題,這個類型就是FORMAL_ITEM對應正常的伺服器資料,當然建立新的ViewHolder也是必須的。

7、實作StaggeredFormalViewHolder完成正常資料展示

7.1 建立StaggeredFormalViewHolder

萬事開頭難,先從簡單的布局出發,這是隻有一張商品圖檔和一個商品描述的簡單展示,采用垂直線性布局,如下:

Android使用RecyclerView和StaggeredGridLayoutManager實作瀑布流效果-實作7、實作StaggeredFormalViewHolder完成正常資料展示

可見,隻有一個ImageView和一個TextView垂直放置,在展示時通過保持圖檔寬高相等且占據去除填充的寬度的一半(因為隻有2列,是以是一半),這樣由于描述TextView字元串長短不同,就會造成包裹它的TextView的高度不一緻,進而形成錯落有緻地效果,這就是我們想要的瀑布流。 在../src/main/res/layout上右擊,選擇New—>Layout resource file,在彈出的對話框的File name中輸入布局檔案名staggered_formal_item,Root element選項輸入LinearLayout單擊OK按鈕,完成staggered_formal_item.xml的布局檔案建立,如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_weight="1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/product_img"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_margin="5dp"
        android:layout_gravity="center_horizontal"
        android:scaleType="centerCrop"
        android:src="@drawable/staggered_formal_img"/>

    <TextView
        android:id="@+id/description_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="5dp"
        android:layout_gravity="center_horizontal"
        android:gravity="center"
        android:text="Formal Staggered Item"
        android:textSize="14sp"
        android:textColor="@android:color/holo_blue_dark"/>

</LinearLayout>
           

與建立StaggeredEmptyViewHolder一樣,右擊staggered包建立StaggeredFormalViewHolder,并加載staggered_formal_item.xml檔案,代碼如下:

package com.edwin.idea.staggered;

import android.content.Context;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;

import com.edwin.idea.R;

/**
 * Created by Edwin,CHEN on 2018/9/18.
 */

public class StaggeredFormalViewHolder extends RecyclerView.ViewHolder {

    private ImageView productImg;
    private TextView descriptionText;
    private Context mContext;

    public static StaggeredFormalViewHolder newInstance(ViewGroup viewGroup){
        View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.staggered_formal_item, viewGroup, false); // 注意:第三個參數是false,否則會異常,詳見6.1節
        return new StaggeredFormalViewHolder(itemView);
    }

    private StaggeredFormalViewHolder(View itemView) {
        super(itemView);
        // 注意:mContext不要在newInstance方法裡指派,會造成記憶體洩漏。
        mContext = itemView.getContext();
        productImg = (ImageView) itemView.findViewById(R.id.product_img);
        int width = getScreenWidthPx();
        // 注意:每行布局有2列,以下兩行代碼的意思是,
        // 每個圖檔的寬度為w = width /2 - marginLeft - marginRight,marginLeft == marginRight == 5
        int margin = dip2px(mContext, 5 * 4);
        int w = (width - margin) / 2;
        ViewGroup.LayoutParams layoutParams = productImg.getLayoutParams();
        layoutParams.width = w;
        layoutParams.height = w;
        productImg.setLayoutParams(layoutParams);
        descriptionText = (TextView) itemView.findViewById(R.id.description_text);
    }

    private int getScreenWidthPx() {
        WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }

    private int getScreenHeightPx() {
        WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.heightPixels;
    }

    /**
     * 根據手機的分辨率從 dp 的機關 轉成為 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根據手機的分辨率從 sp 的機關 轉成為 px(像素)
     */
    public static int sp2px(Context context, float spValue) {
        final float scale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * scale + 0.5f);
    }

    /**
     * 根據手機的分辨率從 px(像素) 的機關 轉成為 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

   /**
     * 在對itemView的childView添加點選監聽時,可以将v.getContext()傳入,因為從23.3.0開始它不再傳回Activity,而是傳回
     * TintContextWrapper
     * https://stackoverflow.com/questions/38814267/android-support-v7-widget-tintcontextwrapper-cannot-be-cast
     * @param context
     * @return
     */
    private Activity getActivity(Context context) {
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                return (Activity)context;
            }
            context = ((ContextWrapper)context).getBaseContext();
        }
        return null;
    }
}
           

檔案中的有幾個工具方法是我們此次項目中用不到的,但是為了避免在工作中需要用到類似方法,一并添加到該檔案中,友善随時使用。這裡面還有一個大問題,就是圖檔顯示使用的是ImageView,在我們的demo中這不會有什麼問題,但是在實際的開發中,直接從伺服器下載下傳的圖檔是不可控的,大的可以達到上百兆,甚至幾個G都是有可能的,這樣必然引發OOM,當然一些第三方庫已經幫我們解決了這個問題,後期再做介紹。

7.2建立model模型StaggeredVO,以适配伺服器資料

StaggeredFormalViewHolder是為了展示伺服器正常資料的,由于各個公司的伺服器傳回結構都是自定義的,其結構頁千差萬别,我們本章的目的是實作瀑布流效果,當然還有一個比較重要的原因是我也不擅長搭建伺服器 ????。是以,這裡直接模拟伺服器最終傳回結構,根據我們第一章提到的目标,我們最終至少要拿到3個元素:圖檔和文本描述,當然還有标簽。他們對應三個字段,目前itemView未展示标簽!在staggered檔案夾下建立StaggeredVO.java檔案,其代碼如下:

package com.edwin.idea.staggered;

import java.io.Serializable;

/**
 * Created by Edwin,CHEN on 2018/9/18.
 */

public class StaggeredVO implements Serializable {
    private String price;
    private String description;
    private String imgUrl;

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getImgUrl() {
        return imgUrl;
    }

    public void setImgUrl(String imgUrl) {
        this.imgUrl = imgUrl;
    }
}

           

看到Serializable接口了嗎,它是用來實作對象的序列化的,在開發中我們如果要使用Intent或Binder傳遞某個對象時,就必須對其進行序列号,否則會報異常。

有了鈔票模闆可以印鈔,有了伺服器資料模型就可以模拟資料,這個過程我們選擇在StaggeredFragment.java檔案中的initData中完成。

7.3模拟伺服器資料

首先在StaggeredFragment.java檔案中聲明一個list泛型清單,如下:

StaggeredVO模闆是List清單的泛型類型,保證建立的資料都是符合要求的,比如我們隻要美元。然後在initData方法中模拟50條伺服器資料,如下:

@Override
     protected void initData() {
         list = new ArrayList<>();
         for (int i = 0; i < 50; i++) {
             StaggeredVO staggeredVO = new StaggeredVO();
             staggeredVO.setPrice("$ " + i); // 注意這裡是有價格的,隻是沒顯示
             if (i % 2 == 1) {
                 staggeredVO.setDescription("Staggered Formal Item " + i);
             }
             list.add(staggeredVO);
         }
    }
           

這樣,伺服器資料就模拟結束了,接下來完成資料的展示工作吧!

7.4完成正常資料展示,實作瀑布流效果

在本章開篇時提到,瀑布流的實作隻是一個在StaggeredAdapter中添加新的ViewType類型的問題,這個新的類型和伺服器資料模闆StaggeredVO、StaggeredFormalViewHolder、每一條模拟資料時一一對應的。

首先,在StaggeredAdapter.java中建立靜态常量FORMAL_ITEM對應StaggeredFormalViewHolder,代碼如下:

private static final int FORMAL_ITEM = 1000;
private static final int EMPTY_ITEM = 1001;
           

當然,我們之前已經建立了一個空的ItemType類型,對應代碼中的EMPTY_ITEM,那麼如何區分這兩個類型呢?

還是要從空的情況入手,通常有如下兩個條件:

  1. list == null; 代碼中沒有對list 指派,比如在StaggeredFragment檔案中initData()是空實作
  2. list != null, 但是list.size() == 0;伺服器什麼都沒傳回

這樣,我們區分開了這兩個類型,就可以添加新的ViewType了。

還記得 6.2 節空資料展示時提到的RecyclerView.Adapter的4個方法的實作周期嗎?如下圖:

Android使用RecyclerView和StaggeredGridLayoutManager實作瀑布流效果-實作7、實作StaggeredFormalViewHolder完成正常資料展示

接着,我們就根據這四個方法的加載順序來添加FORMAL_ITEM。

在StaggeredAdapter.java中聲明list清單代表伺服器資料,代碼如下:

修改getItemCount()方法,将上面的1、2條件引入,代碼如下:

@Override
    public int getItemCount() {
        Log.d(TAG, "=====getItemCount");
        if (list == null || list.size() == 0) {
            // 空也要占一個item
            return 1;
        } else {
            return list.size();
        }
    }
           

修改getItemViewType方法,同樣引入1、2條件,代碼如下:

@Override
    public int getItemViewType(int position) {
        Log.d(TAG, "===getItemViewType");
        if (list == null || list.size() == 0) {
            // 空也要占一個item
            return EMPTY_ITEM;
        } else {
            return FORMAL_ITEM;
        }
    }
           

注意,這裡的position對應目前加載的1條伺服器資料,對此你有什麼想法嗎?通過調用list.get(position)會有美妙的未來

修改onCreateViewHolder方法,代碼如下:

@Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d(TAG, "======onCreateViewHolder");

        if (viewType == FORMAL_ITEM) {
            return StaggeredFormalViewHolder.newInstance(parent);
        }
        return StaggeredEmptyViewHolder.newInstance(parent);
    }
           

由于getItemViewType已經傳回了不同的ViewType類型,這裡就不必引入條件1、2了。如果再添加類型,隻需要加else if條件傳回對應ViewHolder就可以了,你想到這一層了嗎?

修改onBindViewHolder,還記得它的作用嗎?四個字:滑動更新。代碼如下:

@Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        Log.d(TAG, "======onBindViewHolder");
        StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        if (list == null || list.size() == 0) {
            // 空也要占一個item
            layoutParams.setFullSpan(true);            ((StaggeredEmptyViewHolder)holder).itemView.setLayoutParams(layoutParams);
        } else {
            layoutParams.setFullSpan(false);
            ((StaggeredFormalViewHolder)holder).onBind(list.get(position));
        }
    }
           

最後一行代碼報異常了,原因是我們并未在StaggeredFormalViewHolder建立onBind()方法。這裡有兩點需要注意:

  1. position對應的是目前某條伺服器資料
  2. onBindViewHolder更新的是整個itemView的UI

    基于以上兩點,我們需要将整條資料傳給StaggeredFormalViewHolder,每一條資料對應一個StaggeredVO執行個體,那麼在StaggeredFormalViewHolder.java檔案中添加onBind(StaggeredVO vo)方法如下:

/**
     * 重新整理itemView并對其子view填充資料
     * @param vo
     */
    public void onBind(StaggeredVO vo) {
        if (!TextUtils.isEmpty(vo.getImgUrl())) {
            productImg.setImageURI(Uri.parse(vo.getImgUrl()));
        }
        if (!TextUtils.isEmpty(vo.getDescription())) {
            descriptionText.setText(vo.getDescription());
            descriptionText.setVisibility(View.VISIBLE);
        } else {
            descriptionText.setVisibility(View.GONE);
        }
    }
           

StaggeredFormalViewHolder.java的代碼如下:

package com.edwin.idea.staggered;

import android.app.Activity;
import android.content.Context;
import android.content.ContextWrapper;
import android.net.Uri;
import android.support.v7.widget.RecyclerView;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;

import com.edwin.idea.R;

/**
 * Created by Edwin,CHEN on 2018/9/18.
 */

public class StaggeredFormalViewHolder extends RecyclerView.ViewHolder {

    private ImageView productImg;
    private TextView descriptionText;
    private Context mContext;

    public static StaggeredFormalViewHolder newInstance(ViewGroup viewGroup){
        View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.staggered_formal_item, viewGroup, false);
        return new StaggeredFormalViewHolder(itemView);
    }

    private StaggeredFormalViewHolder(View itemView) {
        super(itemView);
        mContext = itemView.getContext();
        productImg = (ImageView) itemView.findViewById(R.id.product_img);
        int width = getScreenWidthPx();
        int margin = dip2px(mContext, 5 * 4);
        int w = (width - margin) / 2;
        ViewGroup.LayoutParams layoutParams = productImg.getLayoutParams();
        layoutParams.width = w;
        layoutParams.height = w;
        productImg.setLayoutParams(layoutParams);
        descriptionText = (TextView) itemView.findViewById(R.id.description_text);
    }

    /**
     * 重新整理itemView并對其子view填充資料
     * @param vo
     */
    public void onBind(StaggeredVO vo) {
        if (!TextUtils.isEmpty(vo.getImgUrl())) {
            productImg.setImageURI(Uri.parse(vo.getImgUrl()));
        }
        if (!TextUtils.isEmpty(vo.getDescription())) {
            descriptionText.setText(vo.getDescription());
            descriptionText.setVisibility(View.VISIBLE);
        } else {
            descriptionText.setVisibility(View.GONE);
        }
    }

    private int getScreenWidthPx() {
        WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.widthPixels;
    }

    private int getScreenHeightPx() {
        WindowManager windowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
        DisplayMetrics displayMetrics = new DisplayMetrics();
        windowManager.getDefaultDisplay().getMetrics(displayMetrics);
        return displayMetrics.heightPixels;
    }

    /**
     * 根據手機的分辨率從 dp 的機關 轉成為 px(像素)
     */
    public static int dip2px(Context context, float dpValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dpValue * scale + 0.5f);
    }

    /**
     * 根據手機的分辨率從 sp 的機關 轉成為 px(像素)
     */
    public static int sp2px(Context context, float spValue) {
        final float scale = context.getResources().getDisplayMetrics().scaledDensity;
        return (int) (spValue * scale + 0.5f);
    }

    /**
     * 根據手機的分辨率從 px(像素) 的機關 轉成為 dp
     */
    public static int px2dip(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    /**
     * 在對itemView的childView添加點選監聽時,可以将v.getContext()傳入,因為從23.3.0開始它不再傳回Activity,而是傳回
     * TintContextWrapper
     * https://stackoverflow.com/questions/38814267/android-support-v7-widget-tintcontextwrapper-cannot-be-cast
     * @param context
     * @return
     */
    private Activity getActivity(Context context) {
        while (context instanceof ContextWrapper) {
            if (context instanceof Activity) {
                return (Activity)context;
            }
            context = ((ContextWrapper)context).getBaseContext();
        }
        return null;
    }
}
           

修改了以上4個方法解決了異常之後,我們隻需要将伺服器模拟資料list設定給adapter的list就可以完成資料展示了。這是多麼大的一個難題啊,我突然間不知道怎麼完成了,你會嗎?

好像是在StaggeredAdapter類中添加一個setData方法,然後在StaggeredFragment中調用它就好了。

在StaggeredAdapter類中,選中list字段Command + N(或者右鍵滑鼠,點選Generate),在彈出的GenerateMenu清單中選中Setter選項,産生setList方法,将setList改成setData即可。

private List<StaggeredVO> list = new ArrayList<>(50);
	    
	/**
     * 将伺服器資料設定給Adapter
     * @param list
     */
	public void setData(List<StaggeredVO> list) {
        this.list = list;
    }

           

修改StaggeredFormalViewHolder.java後的代碼如下:

package com.edwin.idea.staggered;

import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Edwin,CHEN on 2018/9/18.
 */

public class StaggeredAdapter extends RecyclerView.Adapter {

    private static final String TAG = StaggeredAdapter.class.getSimpleName();

    private static final int FORMAL_ITEM = 1000;
    private static final int EMPTY_ITEM = 1001;

    private List<StaggeredVO> list = new ArrayList<>(50);

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        Log.d(TAG, "======onCreateViewHolder");

        if (viewType == FORMAL_ITEM) {
            return StaggeredFormalViewHolder.newInstance(parent);
        }
        return StaggeredEmptyViewHolder.newInstance(parent);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        Log.d(TAG, "======onBindViewHolder");
        StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        if (list == null || list.size() == 0) {
            // 空也要占一個item
            layoutParams.setFullSpan(true);
            ((StaggeredEmptyViewHolder)holder).itemView.setLayoutParams(layoutParams);
        } else {
            layoutParams.setFullSpan(false);
            // 這裡的itemView是RecyclerView.ViewHolder的final public字段,可以直接使用
            // 它就是StaggeredFormalViewHolder對應布局檔案staggered_formal_item.xml的LinearLayout
            ((StaggeredFormalViewHolder) holder).itemView.setSelected(position == selectedIndex);
            ((StaggeredFormalViewHolder)holder).onBind(list.get(position));
       }
    }

    @Override
    public int getItemCount() {
        Log.d(TAG, "=====getItemCount");
        if (list == null || list.size() == 0) {
            // 空也要占一個item
            return 1;
        } else {
            return list.size();
        }
    }

    @Override
    public int getItemViewType(int position) {
        Log.d(TAG, "===getItemViewType");
        if (list == null || list.size() == 0) {
            // 空也要占一個item
            return EMPTY_ITEM;
        } else {
            return FORMAL_ITEM;
        }
    }

    /**
     * 将伺服器資料設定給Adapter
     * @param list
     */
    public void setData(List<StaggeredVO> list) {
        this.list = list;
    }
}
           

最後,在StaggeredFragment.java的onCreate()方法中調用setData(list)将伺服器資料設定給StaggeredAdapter代碼如下:

@Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        viewGroupRoot = (ViewGroup) inflater.inflate(R.layout.fragment_staggered, null);

        recyclerView = (RecyclerView) viewGroupRoot.findViewById(R.id.recycler_view);
        staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
        staggeredAdapter = new StaggeredAdapter();
        initData();//别忘了調用initData()模拟伺服器資料,實際中是從伺服器拿來的
        staggeredAdapter.setData(list);
        recyclerView.setLayoutManager(staggeredGridLayoutManager);
        recyclerView.setAdapter(staggeredAdapter);

        return viewGroupRoot;
    }
           

注意:别忘了調用initData()模拟伺服器資料,否則顯示的是空頁面

StaggeredFragment.java代碼如下:

package com.edwin.idea.staggered;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.edwin.idea.AbstractFragment;
import com.edwin.idea.R;

import java.util.ArrayList;
import java.util.List;

/**
 * Created by Edwin,CHEN on 2018/9/18.
 */

public class StaggeredFragment extends AbstractFragment {

    ViewGroup viewGroupRoot;
    RecyclerView recyclerView;
    StaggeredGridLayoutManager staggeredGridLayoutManager;
    StaggeredAdapter staggeredAdapter;

    List<StaggeredVO> list;

    public static Fragment newInstance(){
        return new StaggeredFragment();
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        viewGroupRoot = (ViewGroup) inflater.inflate(R.layout.fragment_staggered, null);

        recyclerView = (RecyclerView) viewGroupRoot.findViewById(R.id.recycler_view);
        staggeredGridLayoutManager = new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL);
        staggeredAdapter = new StaggeredAdapter();
        initData();
        staggeredAdapter.setData(list);
        recyclerView.setLayoutManager(staggeredGridLayoutManager);
        recyclerView.setAdapter(staggeredAdapter);

        return viewGroupRoot;
    }

     @Override
     protected void initData() {
         list = new ArrayList<>();
         for (int i = 0; i < 50; i++) {
             StaggeredVO staggeredVO = new StaggeredVO();
             staggeredVO.setPrice("$ " + i);
             if (i % 2 == 1) {
                 staggeredVO.setDescription("Staggered Formal Item " + i);
             }
             list.add(staggeredVO);
         }
    }
}
           

好了,Ctrl + R運作一下吧,下圖應該就是你心心念的瀑布流效果了~

Android使用RecyclerView和StaggeredGridLayoutManager實作瀑布流效果-實作7、實作StaggeredFormalViewHolder完成正常資料展示

介意我我說一句嗎?這效果不理想呢~ ## 7.6 添加點選事件 對于事件開發中,每一個itemView的點選事件是必不可少的,其實實作起來相當簡單。隻需要在onBindViewHolder中對itemView添加OnClickListener監聽事件,代碼如下:

((StaggeredFormalViewHolder) holder).itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(v.getContext(), "position" + position, Toast.LENGTH_SHORT).show();
                }
            });
           

7.7 添加選中事件

有事為了凸顯目前選中的itemView,做一些顔色上的特殊展示也是必須的,這個主要通過itemView.setSelected()方法來完成,該方法會觸發view的invalidate完成重繪,更改後onBindViewHolder代碼如下:

private int selectedIndex; // 添加目前被選中index

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
        Log.d(TAG, "======onBindViewHolder");
        StaggeredGridLayoutManager.LayoutParams layoutParams = new StaggeredGridLayoutManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        if (list == null || list.size() == 0) {
            // 空也要占一個item
            layoutParams.setFullSpan(true);
            ((StaggeredEmptyViewHolder) holder).itemView.setSelected(position == selectedIndex);
            ((StaggeredEmptyViewHolder)holder).itemView.setLayoutParams(layoutParams);
        } else {
            layoutParams.setFullSpan(false);
            ((StaggeredFormalViewHolder) holder).itemView.setSelected(position == selectedIndex);
            ((StaggeredFormalViewHolder)holder).onBind(list.get(position));
            ((StaggeredFormalViewHolder) holder).itemView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (selectedIndex != position) {
                        selectedIndex = position;
                    }
                    Toast.makeText(v.getContext(), "position = " + position, Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
           

可以通過更改itemView的背景色來完成驗證哦~

有詩雲:

醉裡挑燈看劍,夢回吹角連營。八百裡分麾下炙,五十弦翻塞外聲。沙場秋點兵。 馬作的盧飛快,弓如霹靂弦驚。了卻君王天下事,赢得生前身後名。可憐白發生!