Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android---UI---RecyclerView實現瀑布流(2)

android---UI---RecyclerView實現瀑布流(2)

編輯:關於Android編程

前言:

前面介紹了瀑布流的基本實現,實際上瀑布流還有一些事件需要監聽。比如點擊事件,下拉和上拉事件。

這裡接著上次的 android—UI—RecyclerView實現瀑布流(1)


添加Item點擊事件:

參考文章:為RecyclerView添加item的點擊事件

RecyclerView側重的是布局的靈活性,雖說可以替代ListView但是連基本的點擊事件都沒有,這篇文章就來詳細講解如何為RecyclerView的item添加點擊事件,順便復習一下觀察者模式

最終目的

所以我們的目的就是要模擬ListView的setOnItemClickListener()方法,調用者只須調用類似於setOnItemClickListener的東西就能獲得被點擊item的相關數據。

實現原理:

為RecyclerView的每個子item設置setOnClickListener,然後在onClick中再調用一次對外封裝的接口,將這個事件傳遞給外面的調用者。而“為RecyclerView的每個子item設置setOnClickListener”在Adapter中設置。其實直接在onClick中也能完全處理item的點擊事件,但是這樣會破壞代碼的邏輯。

實現步驟:

在這裡為了不影響前面的數據,重寫一個適配器adapter:

MyAdapter.java

具體步驟如下:

1.繼承接口 OnClickListener

public class MyAdapter extends RecyclerView.Adapter implements View.OnClickListener{ }

2.在MyAdapter中定義如下接口,模擬ListView的OnItemClickListener:

//定義接口
public static interface OnRecyclerViewItemClickListener {
        void onItemClick(View view , String data);
    }

3.聲明一個這個接口的變量:

private OnRecyclerViewItemClickListener mOnItemClickListener = null;

4.和前面的ViewHolder不同,這裡使用了自定義的ViewHolder

ViewHoider的作用就是一個儲存器,所以自定義的ViewHolder繼承於

RecyclerView.ViewHolder—RecyclerView強制使用ViewHolder,而在ListView裡面,ViewHolder只是作為一個優化的選項。

//自定義的ViewHolder,持有每個Item的的所有界面元素
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
        public ImageView mImgView;
        public ViewHolder(View view){
            super(view);
            mTextView = (TextView) view.findViewById(R.id.masonry_item_title);
            mImgView=(ImageView) view.findViewById(R.id.masonry_item_img);
        }
    }

5.在onCreateViewHolder()中為每個item添加點擊事件

@Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup,  int viewType) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.item, viewGroup, false);
        ViewHolder vh = new ViewHolder(view);
        //將創建的View注冊點擊事件
        view.setOnClickListener(this);
        return vh;
    }

6.將點擊事件轉移給外面的調用者:(這也是繼承接口OnClickListener必須實現的方法)

@Override
    public void onClick(View v) {
        if (mOnItemClickListener != null) {
            //注意這裡使用getTag方法獲取數據
            mOnItemClickListener.onItemClick(v,(String)v.getTag());
        }
    }

7.前面一步有個getTag方法獲取數據,那就有個setTag方法對應。在onBindViewHolder()方法裡面。

@Override
    public void onBindViewHolder(ViewHolder viewHolder,  int position) {
        viewHolder.mTextView.setText(products.get(position).getTitle());
        viewHolder.mImgView.setImageResource(products.get(position).getImg());
        //將數據保存在itemView的Tag中,以便點擊時進行獲取
        viewHolder.itemView.setTag(products.get(position).getTitle());
    }

8.最後暴露給外面的調用者,定義一個設置Listener的方法():

public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }

總的MyAdapter代碼:

public class MyAdapter extends RecyclerView.Adapter implements View.OnClickListener{
    private List products;
    public MyAdapter(List list) {
        products = list;
    }

    private OnRecyclerViewItemClickListener mOnItemClickListener = null;

    //define interface
    public static interface OnRecyclerViewItemClickListener {
        void onItemClick(View view , String data);
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
        View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.masonry_item, viewGroup, false);
        ViewHolder vh = new ViewHolder(view);
        //將創建的View注冊點擊事件
        view.setOnClickListener(this);
        return vh;
    }

    @Override
    public void onBindViewHolder(ViewHolder viewHolder,  int position) {
        viewHolder.mTextView.setText(products.get(position).getTitle());
        viewHolder.mImgView.setImageResource(products.get(position).getImg());
        //將數據保存在itemView的Tag中,以便點擊時進行獲取
        viewHolder.itemView.setTag(products.get(position).getTitle());
    }

    @Override
    public void onClick(View v) {
        if (mOnItemClickListener != null) {
            //注意這裡使用getTag方法獲取數據
            mOnItemClickListener.onItemClick(v,(String)v.getTag());
        }
    }

    public void setOnItemClickListener(OnRecyclerViewItemClickListener listener) {
        this.mOnItemClickListener = listener;
    }


    //獲取數據的數量
    @Override
    public int getItemCount() {
        return products.size();
    }
    //自定義的ViewHolder,持有每個Item的的所有界面元素
    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mTextView;
        public ImageView mImgView;
        public ViewHolder(View view){
            super(view);
            mTextView = (TextView) view.findViewById(R.id.masonry_item_title);
            mImgView=(ImageView) view.findViewById(R.id.masonry_item_img);
        }
    }
}

然後在Activity中調用即可

adapter.setOnItemClickListener(new MyAdapter.OnRecyclerViewItemClickListener(){
            @Override
            public void onItemClick(View view , String data){
                Toast.makeText(MainActivity.this, data, Toast.LENGTH_SHORT).show();
            }
        });

測試:

這裡寫圖片描述

當然還可以添加點擊樣式:drawable添加顏色選擇器:

item_bg.xml



    
    

設置item的背景色為item_bg即可

這裡寫圖片描述

總結:

以上所有步驟都發生在自定義的adapter中,典型的觀察者模式,有點繞的地方在於,這裡涉及到兩個觀察者模式的使用,view的setOnClickListener本來就是觀察者模式,我們將這個觀察者模式的事件監聽傳遞給了我們自己的觀察者模式。

上拉和下拉事件的監聽:

在下拉刷新,上拉加載的事件中,第一步就是要監聽上啦和下拉事件。

這個很簡單,由於自帶了OnScrollListener方法

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                Log.d("ScrollListener",dx+","+dy+"");
            }
        });

位置變化:

這裡寫圖片描述

可以看出上拉為負下拉為正。<喎?/kf/ware/vc/" target="_blank" class="keylink">vc3Ryb25nPjwvY29kZT48L3A+DQo8aDIgaWQ9"上拉加載">上拉加載:

首先判斷是否已經到底部:

判斷方法1:使用StaggeredGridLayoutManager獲取最後一個的位置,判斷是否在屏幕底部。

StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
               if (lastPositions == null) {
                   lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
               }
               staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
               lastVisibleItemPosition = findMax(lastPositions);

判斷方法2: RecyclerView每加載一個item都會調用一次onBindViewHolder方法,並且只在item由不可見變為可見的時候才會調用此方法。我們可以通過onBindViewHolder方法來判斷是否已經到達列表的底部。–>RecyclerView滑動到底部自動加載

public void onBindViewHolder(CollectionViewHolder holder, int position) {
        holder.fillData(mData.get(position));
        if(position == getItemCount()-1){//已經到達列表的底部
            loadMoreData();
        }
    }

這裡采用第一種方法:

具體如下:

設置兩個變量:

      //最後一個的位置

    private int[] lastPositions;


     //最後一個可見的item的位置

    private int lastVisibleItemPosition;

滑動事件監聽:

recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                int visibleItemCount = layoutManager.getChildCount();
                int totalItemCount = layoutManager.getItemCount();
                Log.i("onScrollStateChanged", "visibleItemCount" + visibleItemCount);
                Log.i("onScrollStateChanged", "lastVisibleItemPosition" + lastVisibleItemPosition);
                Log.i("onScrollStateChanged", "totalItemCount" + totalItemCount);
                Log.i("newstate","newstate"+newState);
                if (visibleItemCount > 0 && newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItemPosition == totalItemCount - 2) {
                    Log.d("----------","到底了");

                }

            }

            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                //Log.d("ScrollListener",dx+","+dy+"");
                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
                StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                if (lastPositions == null) {
                    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                }
                staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
                lastVisibleItemPosition = findMax(lastPositions);

            }
        });

首先在onScrolled方法裡監聽滾動事件的最後一個Item的位置。

1.獲取View的layoutManager。

    RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
    taggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;

2.getSpanCount()返回所包含的 Item 總個數—為什麼是個數組?

經過測試,lastPositions的長度和設置的列有關,設置3列,數組長度也為三。但是測試時候,在findLastVisibleItemPositions 方法之前,值都為0.經過findLastVisibleItemPositions 方法之後就能找到每一列的最大位置。—這裡是27,28,29。然後比較,輸出最大的位置。

if (lastPositions == null) {
                    lastPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                }

3.通過位置判斷哪個才是真正的最後一個

staggeredGridLayoutManager.findLastVisibleItemPositions(lastPositions);
lastVisibleItemPosition = findMax(lastPositions);

自定義findMax方法

private int findMax(int[] lastPositions) {
        int max = lastPositions[0];
        for (int value : lastPositions) {
            if (value > max) {
                max = value;
            }
        }
        return max;
    }

繼續onScrollStateChanged()方法

1.同樣先

                RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();

2.通過布局管理器獲取:總Item數和可見item數

int visibleItemCount = layoutManager.getChildCount();
                int totalItemCount = layoutManager.getItemCount();

3.判斷是否到底:lastVisibleItemPosition == totalItemCount - 1 因為是位置是從0開始的,所以比總數小1.

if (visibleItemCount > 0 && newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItemPosition == totalItemCount - 1) {
                    Log.d("----------","到底了");

                }

測試–

這裡寫圖片描述

數據加載:

在適配器MyAdapter中添加兩個方法:

//增加item
    public void addData(int position,String text,int Bitmap) {
        products.add(new Product(Bitmap, text));
        notifyItemInserted(position);
    }
   //刪除item
    public void removeData(int position) {
        products.remove(position);
        notifyItemInserted(position);
    }

在拉到底部的時候調用:

if (visibleItemCount > 0 && newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItemPosition == totalItemCount - 1) {
                    Log.d("----------","到底了");
                    adapter.addData(totalItemCount,"添加",R.drawable.ic_9);

                }

測試:

這裡寫圖片描述

這裡寫圖片描述

下拉刷新:

下拉刷新要判斷是否在頂部:使用SwipeRefreshLayout控件不需要判斷是否在頂部,因為SwipeRefreshLayout控件默認在頂部才會刷新數據。

所以下面的方法可以作廢。

同樣的道理:findFirstVisibleItemPositions方法可以返回當前視圖每一列可見的第一個item的位置。判斷返回最小的是否等於0,等於就是到頂部了。

private int[] firstPositions;
private int firstVisibleItemPosition;
if (firstPositions == null) {
                    firstPositions = new int[staggeredGridLayoutManager.getSpanCount()];
                }
staggeredGridLayoutManager.findFirstVisibleItemPositions(firstPositions);
firstVisibleItemPosition = findMin(firstPositions);
private int findMin(int[] firstPositions) {
        int min = firstPositions[0];
        for (int value : firstPositions) {
            Log.d("xxxx",value+"");
            if (value < min) {
                min = value;
            }
        }
if(firstVisibleItemPosition==0){
                    Log.d("----------","到頂了");

                }

實現刷新功能:

SwipeRefreshLayout 是谷歌公司推出的用於下拉刷新的控件.

更改主布局文件為:




  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved