Android 使用自定義RecyclerView控件實現(xiàn)Gallery效果
上篇文章給大家介紹了Android 自定義 HorizontalScrollView 打造多圖片OOM 的橫向滑動效果。其實制作橫向滾動的不得不說另一個控件,就是Google官方最近新增加的RecyclerView,據(jù)說是ListView的升級版本,本篇文章,首先介紹RecyclerView的用法,然后經(jīng)行一定的分析;最后自定義一下RecyclerView實現(xiàn)我們需要的相冊效果。
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.RecyclerView android: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" > <ImageView android: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" /> <TextView android: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>
數(shù)據(jù)適配器:
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 extends
RecyclerView.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;
}
@Override
public int getItemCount()
{
return mDatas.size();
}
/**
* 創(chuàng)建ViewHolder
*/
@Override
public 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;
}
/**
* 設置值
*/
@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int i)
{
viewHolder.mImg.setImageResource(mDatas.get(i));
}
}
可以看到數(shù)據(jù)適配器與BaseAdapter比較發(fā)生了相當大的變化,主要有3個方法:
getItemCount 這個不用說,獲取總的條目數(shù)
onCreateViewHolder 創(chuàng)建ViewHolder
onBindViewHolder 將數(shù)據(jù)綁定至ViewHolder
可見,RecyclerView對ViewHolder也進行了一定的封裝,但是如果你仔細觀察,你會發(fā)出一個疑問,ListView里面有個getView返回View為Item的布局,那么這個Item的樣子在哪控制?
其實是這樣的,我們創(chuàng)建的ViewHolder必須繼承RecyclerView.ViewHolder,這個RecyclerView.ViewHolder的構(gòu)造時必須傳入一個View,這個View相當于我們ListView getView中的convertView (即:我們需要inflate的item布局需要傳入)。
還有一點,ListView中convertView是復用的,在RecyclerView中,是把ViewHolder作為緩存的單位了,然后convertView作為ViewHolder的成員變量保持在ViewHolder中,也就是說,假設沒有屏幕顯示10個條目,則會創(chuàng)建10個ViewHolder緩存起來,每次復用的是ViewHolder,所以他把getView這個方法變?yōu)榱薿nCreateViewHolder。有興趣的自己打印下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;
@Override
protected 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));
}
}
使用起來也很方便,唯一的區(qū)別就是要設置LayoutManager,目前只有一個實現(xiàn)類,就是LinearLayoutManager,可以設置為水平或者垂直。
最后效果圖:

效果很不錯,這就是RecyclerView的基本用法了,但是你會發(fā)現(xiàn)一個坑爹的地方,竟然沒有提供setOnItemClickListener這個回調(diào),要不要這么坑爹。。。
2、為RecyclerView添加OnItemClickListener回調(diào)
雖然它沒有提供,但是添加個OnItemClickListener對我們來說還不是小菜一碟~
我決定在Adapter中添加這個回調(diào)接口:
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 extends
RecyclerView.Adapter<GalleryAdapter.ViewHolder>
{
/**
* ItemClick的回調(diào)接口
* @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;
}
@Override
public int getItemCount()
{
return mDatas.size();
}
@Override
public 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;
}
@Override
public void onBindViewHolder(final ViewHolder viewHolder, final int i)
{
viewHolder.mImg.setImageResource(mDatas.get(i));
//如果設置了回調(diào),則設置點擊事件
if (mOnItemClickLitener != null)
{
viewHolder.itemView.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v)
{
mOnItemClickLitener.onItemClick(viewHolder.itemView, i);
}
});
}
}
}
很簡單,創(chuàng)建一個接口,提供一個設置入口,然后在onBindViewHolder中判斷即可。
最后在主Activity中設置監(jiān)聽:
mAdapter = new GalleryAdapter(this, mDatas);
mAdapter.setOnItemClickLitener(new OnItemClickLitener()
{
@Override
public void onItemClick(View view, int position)
{
Toast.makeText(MainActivity.this, position+"", Toast.LENGTH_SHORT)
.show();
}
});
mRecyclerView.setAdapter(mAdapter);
好了,這樣就行了,看效果圖:

效果還是不錯的,接下來我想改成相冊效果,即上面顯示一張大圖,下面的RecyclerView做為圖片切換的指示器。
3、自定義RecyclerView實現(xiàn)滾動時內(nèi)容聯(lián)動
首先修改下布局:
布局文件:
<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" > <FrameLayout android:layout_width="fill_parent" android:layout_height="0dp" android:layout_weight="1" > <ImageView android: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.MyRecyclerView android: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>
添加一個顯示大圖的區(qū)域,把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;
/**
* 滾動時回調(diào)的接口
*/
private OnItemScrollChangeListener mItemScrollChangeListener;
public void setOnItemScrollChangeListener(
OnItemScrollChangeListener mItemScrollChangeListener)
{
this.mItemScrollChangeListener = mItemScrollChangeListener;
}
public interface OnItemScrollChangeListener
{
void onChange(View view, int position);
}
@Override
protected 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));
}
}
@Override
public 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);
}
}
定義了一個滾動時回調(diào)的接口,然后在onTouchEvent中,監(jiān)聽ACTION_MOVE,用戶手指滑動時,不斷把當前第一個View回調(diào)回去~
關(guān)于我咋知道getChildAt(0)和getChildPosition()可用,起初我以為有g(shù)etFirstVisibleItem這個方法,后來發(fā)現(xiàn)么有;但是發(fā)現(xiàn)了getRecycledViewPool()看名字我覺得是Viewholder那個緩存隊列,我想那么直接取這個隊列的第一個不就是我要的View么,后來沒有成功。我就觀察它內(nèi)部的View,最后發(fā)現(xiàn),第一個顯示的始終是它第一個child,至于getChildPosition這個看方法就看出來了。
現(xiàn)在的效果:

和我之前那個例子的效果是一模一樣的,不過,我還想做一些改變,我覺得Gallery或者說相冊的指示器,下面可能1000來張圖片,我不僅喜歡手指在屏幕上滑動時,圖片會自動切換。我還希望,如果我給指示器一個加速度,即使手指離開,下面還在滑動,上面也會聯(lián)動 。而且我還想做些優(yōu)化,直接在ACTION_MOVE中回調(diào),觸發(fā)的頻率太高了,理論上一張圖片只會觸發(fā)一次~~
4、優(yōu)化與打造真正的Gallery效果
既然希望手指離開還能聯(lián)動,那么不僅需要ACTION_MOVE需要監(jiān)聽,還得監(jiān)聽一個加速度,速度到達一定值,然后繼續(xù)移動~~再理一理,需要這么麻煩么,不是能滾動么,那么應該有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 stub
this.setOnScrollListener(this);
}
@Override
protected 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));
}
}
@Override
public void onScrollStateChanged(int arg0)
{
}
/**
*
* 滾動時,判斷當前第一個View是否發(fā)生變化,發(fā)生才回調(diào)
*/
@Override
public 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方法,而是讓這個類實現(xiàn)RecyclerView.OnScrollListener接口,然后設置監(jiān)聽,在onScrolled里面進行判斷。
至于優(yōu)化:我使用了一個成員變化存儲當前第一個View,只有第一個View發(fā)生變化時才回調(diào)~~太完美了~
看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 ;
@Override
protected 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()
{
@Override
public void onChange(View view, int position)
{
mImg.setImageResource(mDatas.get(position));
};
});
mAdapter.setOnItemClickLitener(new OnItemClickLitener()
{
@Override
public void onItemClick(View view, int position)
{
// Toast.makeText(getApplicationContext(), position + "", Toast.LENGTH_SHORT)
// .show();
mImg.setImageResource(mDatas.get(position));
}
});
}
}
代碼沒什么變化~多了個設置回調(diào)~
效果圖:

可以看到不僅支持手機在上面移動時的變化,如果我給了一個加速度,下面持續(xù)滾動,上面也會持續(xù)變化~~大贊~每張圖片回調(diào)一次,效率也相當不錯。
好了,看完這邊博客,相信大家對于RecyclerView有了一定的認識,甚至對于如何改造一個控件也多了一份了解~~
如果覺得不錯,就留個言或者點個贊,表示對我的支持
總結(jié)
以上所述是小編給大家介紹的Android 使用自定義RecyclerView 實現(xiàn)Gallery效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
- Android實現(xiàn)漂亮的Gallery畫廊
- Android中Gallery和ImageSwitcher的使用實例
- Android TV開發(fā):實現(xiàn)3D仿Gallery效果的實例代碼
- Android自定義Gallery控件實現(xiàn)3D圖片瀏覽器
- Android開發(fā)中畫廊視圖Gallery的兩種使用方法分析
- Android高級組件Gallery畫廊視圖使用方法詳解
- Android UI控件之Gallery實現(xiàn)拖動式圖片瀏覽效果
- Android開發(fā)實現(xiàn)Gallery畫廊效果的方法
- Android使用gallery和imageSwitch制作可左右循環(huán)滑動的圖片瀏覽器
- Android之Gallery使用例子
- Android使用Gallery實現(xiàn)照片拖動的特效
相關(guān)文章
Android之RecyclerView輕松實現(xiàn)下拉刷新和加載更多示例
本篇文章主要介紹了Android之RecyclerView輕松實現(xiàn)下拉刷新和加載更多示例,非常具有實用價值,需要的朋友可以參考下。2017-02-02
Android開發(fā)MQTT協(xié)議的模型及通信淺析
這篇文章主要W為大家介紹了Android開發(fā)MQTT協(xié)議的模型及通信淺析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-03-03
Android開發(fā)中RecyclerView組件使用的一些進階技講解
RecyclerView是Android 5.0以來新加入的一個組件,基本上全面優(yōu)于ListView,這里我們將來關(guān)注Android開發(fā)中RecyclerView組件使用的一些進階技講解:2016-06-06
Android實現(xiàn)輪播圖無限循環(huán)效果
這篇文章主要為大家詳細介紹了Android實現(xiàn)輪播圖無限循環(huán)效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02
使用User Agent分辨出Android設備類型的安全做法
這篇文章主要介紹了使用User Agent分辨出Android設備類型的安全做法,本文得出的結(jié)論是當你依據(jù)檢測UA來判斷Android手機設備,請同時檢查android和mobile兩個字符串,需要的朋友可以參考下2015-01-01
android-wheel控件實現(xiàn)三級聯(lián)動效果
這篇文章主要為大家詳細介紹了android-wheel控件實現(xiàn)三級聯(lián)動效果的代碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-10-10
android為ListView每個Item上面的按鈕添加事件
本篇文章主要介紹了android為ListView每個Item上面的按鈕添加事件,有興趣的同學可以了解一下。2016-11-11

