Android之RecyclerView 实现真正的Gallery效果
簡介:
RecyclerView是support-v7包中的新組件,是一個強大的滑動組件,與經典的ListView相比,同樣擁有item回收復用的功能,但是直接把viewholder的實現封裝起來,用戶只要實現自己的viewholder就可以了,該組件會自動幫你回收復用每一個item。
它不但變得更精簡,也變得更加容易使用,而且更容易組合設計出自己需要的滑動布局。
要使用RecyclerView,請參考?,其實你也可以只下載一個jar包,添加到自己的libs里就能使用它了,Recycler.jar
使用它的理由:
RecyclerView?is a more advanced and flexible version of?ListView. This widget is a container for large sets of views that can be recycled and scrolled very efficiently. Use the?RecyclerView?widget when you have lists with elements that change dynamically.簡單說,它是ListView的進化,為了當你需要動態展示一組數據的時候就會需要它。
當然,如果只是動態展示數據,listview也可以做到,用它替代listview的原因有幾個: ·簡介中提到的它封裝了viewholder的回收復用。 ·RecyclerView使用布局管理器管理子view的位置(目前尚只提供了LinearLayoutManager),也就是說你再不用拘泥于ListView的線性展示方式,如果之后提供其他custom LayoutManager的支持,你能夠使用復雜的布局來展示一個動態組件。 ·自帶了ItemAnimation,可以設置加載和移除時的動畫,方便做出各種動態瀏覽的效果。
Google官方最近新增加的RecyclerView,據說是ListView的升級版本,本篇博客,首先介紹RecyclerView的用法,然后經行一定的分析;最后自定義一下RecyclerView實現我們需要的相冊效果。
1、RecyclerView的基本用法
首先主Activity的布局文件:<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent" ><android.support.v7.widget.RecyclerViewandroid:id="@+id/id_recyclerview_horizontal"android:layout_width="match_parent"android:layout_height="120dp"android:layout_centerVertical="true"android:background="#FF0000"android:scrollbars="none" /></RelativeLayout>Item的布局文件: <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="120dp"android:layout_height="120dp"android:background="@drawable/item_bg02" ><ImageViewandroid:id="@+id/id_index_gallery_item_image"android:layout_width="80dp"android:layout_height="80dp"android:layout_alignParentTop="true"android:layout_centerHorizontal="true"android:layout_margin="5dp"android:scaleType="centerCrop" /><TextViewandroid:id="@+id/id_index_gallery_item_text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_below="@id/id_index_gallery_item_image"android:layout_centerHorizontal="true"android:layout_marginBottom="5dp"android:layout_marginTop="5dp"android:textColor="#ff0000"android:text="some info"android:textSize="12dp" /></RelativeLayout>數據適配器: package com.example.zhy_horizontalscrollview03;import java.util.List;import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView;public class GalleryAdapter extendsRecyclerView.Adapter<GalleryAdapter.ViewHolder> {private LayoutInflater mInflater;private List<Integer> mDatas;public GalleryAdapter(Context context, List<Integer> datats){mInflater = LayoutInflater.from(context);mDatas = datats;}public static class ViewHolder extends RecyclerView.ViewHolder{public ViewHolder(View arg0){super(arg0);}ImageView mImg;TextView mTxt;}@Overridepublic int getItemCount(){return mDatas.size();}/*** 創建ViewHolder*/@Overridepublic ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i){View view = mInflater.inflate(R.layout.activity_index_gallery_item,viewGroup, false);ViewHolder viewHolder = new ViewHolder(view);viewHolder.mImg = (ImageView) view.findViewById(R.id.id_index_gallery_item_image);return viewHolder;}/*** 設置值*/@Overridepublic void onBindViewHolder(final ViewHolder viewHolder, final int i){viewHolder.mImg.setImageResource(mDatas.get(i));}} 可以看到數據適配器與BaseAdapter比較發生了相當大的變化,主要有3個方法:
getItemCount 這個不用說,獲取總的條目數
onCreateViewHolder 創建ViewHolder
onBindViewHolder 將數據綁定至ViewHolder
可見,RecyclerView對ViewHolder也進行了一定的封裝,但是如果你仔細觀察,你會發出一個疑問,ListView里面有個getView返回View為Item的布局,那么這個Item的樣子在哪控制?
其實是這樣的,我們創建的ViewHolder必須繼承RecyclerView.ViewHolder,這個RecyclerView.ViewHolder的構造時必須傳入一個View,這個View相當于我們ListView getView中的convertView (即:我們需要inflate的item布局需要傳入)。
還有一點,ListView中convertView是復用的,在RecyclerView中,是把ViewHolder作為緩存的單位了,然后convertView作為ViewHolder的成員變量保持在ViewHolder中,也就是說,假設沒有屏幕顯示10個條目,則會創建10個ViewHolder緩存起來,每次復用的是ViewHolder,所以他把getView這個方法變為了onCreateViewHolder。有興趣的自己打印下Log,測試下。
最后在Activity中使用:
package com.example.zhy_horizontalscrollview03;import java.util.ArrayList; import java.util.Arrays; import java.util.List;import android.app.Activity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.Window;public class MainActivity extends Activity {private RecyclerView mRecyclerView;private GalleryAdapter mAdapter;private List<Integer> mDatas;@Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);initDatas();//得到控件mRecyclerView = (RecyclerView) findViewById(R.id.id_recyclerview_horizontal);//設置布局管理器LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);mRecyclerView.setLayoutManager(linearLayoutManager);//設置適配器mAdapter = new GalleryAdapter(this, mDatas);mRecyclerView.setAdapter(mAdapter);}private void initDatas(){mDatas = new ArrayList<Integer>(Arrays.asList(R.drawable.a,R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e,R.drawable.f, R.drawable.g, R.drawable.h, R.drawable.l));}}使用起來也很方便,唯一的區別就是要設置LayoutManager,目前只有一個實現類,就是LinearLayoutManager,可以設置為水平或者垂直。
最后效果圖
效果很不錯,這就是RecyclerView的基本用法了,但是你會發現一個坑爹的地方,竟然沒有提供setOnItemClickListener這個回調,要不要這么坑爹。。。
2、為RecyclerView添加OnItemClickListener回調
雖然它沒有提供,但是添加個OnItemClickListener對我們來說還不是小菜一碟~
我決定在Adapter中添加這個回調接口:
package com.example.zhy_horizontalscrollview03;import java.util.List;import android.content.Context; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView;public class GalleryAdapter extendsRecyclerView.Adapter<GalleryAdapter.ViewHolder> {/*** ItemClick的回調接口* @author zhy**/public interface OnItemClickLitener{void onItemClick(View view, int position);}private OnItemClickLitener mOnItemClickLitener;public void setOnItemClickLitener(OnItemClickLitener mOnItemClickLitener){this.mOnItemClickLitener = mOnItemClickLitener;}private LayoutInflater mInflater;private List<Integer> mDatas;public GalleryAdapter(Context context, List<Integer> datats){mInflater = LayoutInflater.from(context);mDatas = datats;}public static class ViewHolder extends RecyclerView.ViewHolder{public ViewHolder(View arg0){super(arg0);}ImageView mImg;TextView mTxt;}@Overridepublic int getItemCount(){return mDatas.size();}@Overridepublic ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i){View view = mInflater.inflate(R.layout.activity_index_gallery_item,viewGroup, false);ViewHolder viewHolder = new ViewHolder(view);viewHolder.mImg = (ImageView) view.findViewById(R.id.id_index_gallery_item_image);return viewHolder;}@Overridepublic void onBindViewHolder(final ViewHolder viewHolder, final int i){viewHolder.mImg.setImageResource(mDatas.get(i));//如果設置了回調,則設置點擊事件if (mOnItemClickLitener != null){viewHolder.itemView.setOnClickListener(new OnClickListener(){@Overridepublic void onClick(View v){mOnItemClickLitener.onItemClick(viewHolder.itemView, i);}});}}}
很簡單,創建一個接口,提供一個設置入口,然后在onBindViewHolder中判斷即可。
最后在主Activity中設置監聽:
mAdapter = new GalleryAdapter(this, mDatas);mAdapter.setOnItemClickLitener(new OnItemClickLitener(){@Overridepublic void onItemClick(View view, int position){Toast.makeText(MainActivity.this, position+"", Toast.LENGTH_SHORT).show();}});mRecyclerView.setAdapter(mAdapter); 好了,這樣就行了,看效果圖:
效果還是不錯的,接下來我想改成相冊效果,即上面顯示一張大圖,下面的RecyclerView做為圖片切換的指示器。
3、自定義RecyclerView實現滾動時內容聯動
首先修改下布局:
布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical" ><FrameLayoutandroid:layout_width="fill_parent"android:layout_height="0dp"android:layout_weight="1" ><ImageViewandroid:id="@+id/id_content"android:layout_width="fill_parent"android:layout_height="fill_parent"android:layout_gravity="center"android:layout_margin="10dp"android:scaleType="centerCrop"android:src="@drawable/ic_launcher" /></FrameLayout><com.example.zhy_horizontalscrollview03.MyRecyclerViewandroid:id="@+id/id_recyclerview_horizontal"android:layout_width="match_parent"android:layout_height="120dp"android:layout_gravity="bottom"android:background="#FF0000"android:scrollbars="none" /></LinearLayout>添加一個顯示大圖的區域,把RecyclerView改為自己定義的。
然后看我們自定義RecyclerView的代碼:
package com.example.zhy_horizontalscrollview03;import android.content.Context; import android.support.v7.widget.RecyclerView; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View;public class CopyOfMyRecyclerView extends RecyclerView {public CopyOfMyRecyclerView(Context context, AttributeSet attrs){super(context, attrs);}private View mCurrentView;/*** 滾動時回調的接口*/private OnItemScrollChangeListener mItemScrollChangeListener;public void setOnItemScrollChangeListener(OnItemScrollChangeListener mItemScrollChangeListener){this.mItemScrollChangeListener = mItemScrollChangeListener;}public interface OnItemScrollChangeListener{void onChange(View view, int position);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b){super.onLayout(changed, l, t, r, b);mCurrentView = getChildAt(0);if (mItemScrollChangeListener != null){mItemScrollChangeListener.onChange(mCurrentView,getChildPosition(mCurrentView));}}@Overridepublic boolean onTouchEvent(MotionEvent e){if (e.getAction() == MotionEvent.ACTION_MOVE){mCurrentView = getChildAt(0);// Log.e("TAG", getChildPosition(getChildAt(0)) + "");if (mItemScrollChangeListener != null){mItemScrollChangeListener.onChange(mCurrentView,getChildPosition(mCurrentView));}}return super.onTouchEvent(e);}}定義了一個滾動時回調的接口,然后在onTouchEvent中,監聽ACTION_MOVE,用戶手指滑動時,不斷把當前第一個View回調回去~
關于我咋知道getChildAt(0)和getChildPosition()可用,起初我以為有getFirstVisibleItem這個方法,后來發現么有;但是發現了getRecycledViewPool()看名字我覺得是Viewholder那個緩存隊列,我想那么直接取這個隊列的第一個不就是我要的View么,后來沒有成功。我就觀察它內部的View,最后發現,第一個顯示的始終是它第一個child,至于getChildPosition這個看方法就看出來了。
現在的效果:
和我之前那個例子的效果是一模一樣的,不過,我還想做一些改變,我覺得Gallery或者說相冊的指示器,下面可能1000來張圖片,我不僅喜歡手指在屏幕上滑動時,圖片會自動切換。我還希望,如果我給指示器一個加速度,即使手指離開,下面還在滑動,上面也會聯動 。而且我還想做些優化,直接在ACTION_MOVE中回調,觸發的頻率太高了,理論上一張圖片只會觸發一次~~
4、優化與打造真正的Gallery效果
既然希望手指離開還能聯動,那么不僅需要ACTION_MOVE需要監聽,還得監聽一個加速度,速度到達一定值,然后繼續移動~~再理一理,需要這么麻煩么,不是能滾動么,那么應該有OnScrollListener啊,小看一把,果然有,哈哈哈~天助我也,下面看修改后的代碼:
package com.example.zhy_horizontalscrollview03;import android.content.Context; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.OnScrollListener; import android.util.AttributeSet; import android.view.View;public class MyRecyclerView extends RecyclerView implements OnScrollListener {/*** 記錄當前第一個View*/private View mCurrentView;private OnItemScrollChangeListener mItemScrollChangeListener;public void setOnItemScrollChangeListener(OnItemScrollChangeListener mItemScrollChangeListener){this.mItemScrollChangeListener = mItemScrollChangeListener;}public interface OnItemScrollChangeListener{void onChange(View view, int position);}public MyRecyclerView(Context context, AttributeSet attrs){super(context, attrs);// TODO Auto-generated constructor stubthis.setOnScrollListener(this);}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b){super.onLayout(changed, l, t, r, b);mCurrentView = getChildAt(0);if (mItemScrollChangeListener != null){mItemScrollChangeListener.onChange(mCurrentView,getChildPosition(mCurrentView));}}@Overridepublic void onScrollStateChanged(int arg0){}/*** * 滾動時,判斷當前第一個View是否發生變化,發生才回調*/@Overridepublic void onScrolled(int arg0, int arg1){View newView = getChildAt(0);if (mItemScrollChangeListener != null){if (newView != null && newView != mCurrentView){mCurrentView = newView ;mItemScrollChangeListener.onChange(mCurrentView,getChildPosition(mCurrentView));}}}}我放棄了重寫onTouchEvent方法,而是讓這個類實現RecyclerView.OnScrollListener接口,然后設置監聽,在onScrolled里面進行判斷。
至于優化:我使用了一個成員變化存儲當前第一個View,只有第一個View發生變化時才回調~~太完美了~
看MainActivity:
package com.example.zhy_horizontalscrollview03;import java.util.ArrayList; import java.util.Arrays; import java.util.List;import android.app.Activity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.Window; import android.widget.ImageView; import android.widget.Toast;import com.example.zhy_horizontalscrollview03.GalleryAdapter.OnItemClickLitener; import com.example.zhy_horizontalscrollview03.MyRecyclerView.OnItemScrollChangeListener;public class MainActivity extends Activity {private MyRecyclerView mRecyclerView;private GalleryAdapter mAdapter;private List<Integer> mDatas;private ImageView mImg ; @Overrideprotected void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.activity_main);mImg = (ImageView) findViewById(R.id.id_content);mDatas = new ArrayList<Integer>(Arrays.asList(R.drawable.a,R.drawable.b, R.drawable.c, R.drawable.d, R.drawable.e,R.drawable.f, R.drawable.g, R.drawable.h, R.drawable.l));mRecyclerView = (MyRecyclerView) findViewById(R.id.id_recyclerview_horizontal);LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);mRecyclerView.setLayoutManager(linearLayoutManager);mAdapter = new GalleryAdapter(this, mDatas);mRecyclerView.setAdapter(mAdapter);mRecyclerView.setOnItemScrollChangeListener(new OnItemScrollChangeListener(){@Overridepublic void onChange(View view, int position){mImg.setImageResource(mDatas.get(position));};});mAdapter.setOnItemClickLitener(new OnItemClickLitener(){@Overridepublic void onItemClick(View view, int position){ // Toast.makeText(getApplicationContext(), position + "", Toast.LENGTH_SHORT) // .show();mImg.setImageResource(mDatas.get(position));}});}}
代碼沒什么變化~多了個設置回調~
效果圖:
可以看到不僅支持手機在上面移動時的變化,如果我給了一個加速度,下面持續滾動,上面也會持續變化~~大贊~每張圖片回調一次,效率也相當不錯。
好了,看完這邊博客,相信大家對于RecyclerView有了一定的認識,甚至對于如何改造一個控件也多了一份了解~~
總結
以上是生活随笔為你收集整理的Android之RecyclerView 实现真正的Gallery效果的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Android之gallery 常见2种
- 下一篇: Android之ADB常用命令