轉載請标明出處:http://blog.csdn.net/lmj623565791/article/details/38140505
自從gallery被谷歌廢棄以後,google推薦使用viewpager和horizontalscrollview來實作gallery的效果。的确horizontalscrollview可以實作gallery的效果,但是horizontalscrollview存在一個很大的問題,如果你僅是用來展示少量的圖檔,應該是沒問題的,但是如果我希望horizontalscrollview可以想viewpager一樣,既可以綁定資料集(動态改變圖檔),還能做到,不管多少圖檔都不會oom(viewpager内部一直初始化,回收,至多隻保持3個view)。本篇部落格首先介紹horizontalscrollview的簡單用法,然後會在此基礎上進行擴充,自定義horizontalscrollview實作我們上面提到的效果,類似一屏可以顯示多個view的viewpager,再多的圖檔也不怕oom。
1、horizontalscrollview的簡單用法
horizontalscrollview其實是framelayout的子類,是以内部隻能有一個直接的子view。我們用來做gallery效果,首選當然是linearlayout,然後方向設定為水準。
1、布局檔案:
[html] view
plaincopy
<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"
>
<horizontalscrollview
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_gravity="center_vertical"
android:background="#aa444444"
android:scrollbars="none" >
<linearlayout
android:id="@+id/id_gallery"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:orientation="horizontal" >
</linearlayout>
</horizontalscrollview>
</linearlayout>
很簡單,就一個horizontalscrollview内部有個水準方向的linearlayout
mainactivity:
[java] view
package com.example.zhy_horizontalscrollview;
import android.app.activity;
import android.os.bundle;
import android.view.layoutinflater;
import android.view.view;
import android.view.window;
import android.widget.imageview;
import android.widget.linearlayout;
import android.widget.textview;
public class mainactivity extends activity
{
private linearlayout mgallery;
private int[] mimgids;
private layoutinflater minflater;
@override
protected void oncreate(bundle savedinstancestate)
{
super.oncreate(savedinstancestate);
requestwindowfeature(window.feature_no_title);
setcontentview(r.layout.activity_main);
minflater = layoutinflater.from(this);
initdata();
initview();
}
private void initdata()
mimgids = new int[] { 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 };
private void initview()
mgallery = (linearlayout) findviewbyid(r.id.id_gallery);
for (int i = 0; i < mimgids.length; i++)
{
view view = minflater.inflate(r.layout.activity_index_gallery_item,
mgallery, false);
imageview img = (imageview) view
.findviewbyid(r.id.id_index_gallery_item_image);
img.setimageresource(mimgids[i]);
textview txt = (textview) view
.findviewbyid(r.id.id_index_gallery_item_text);
txt.settext("some info ");
mgallery.addview(view);
}
}
很簡單,我預先準備了一些圖檔直接放在了drawble下,然後循環加入horizontalscrollview的linearlayout中即可,item的布局就省了,後面會貼源碼。
效果圖:
效果還是不錯的~如果隻需要簡單展示幾張圖檔,直接用就可以了。
下面準備進入正題,horizontalscrollview不管裡面多少view都是不會回收的,當達到一定量的時候會發生oom,下面介紹如何改寫horizontalscollview實作文章開始所說的效果。
2、自定義horizontalscrollview
思想:
1、首先根據螢幕的大小和item的大小,計算可以一個螢幕最多可以加載多少個item,然後加載該數量item。
2、當使用者右滑(從右向左),滑動到一定距離時,加載下一張,删除第一張
3、當使用者左滑(從左向右),滑動到一定距離時,加載上一張,删除最後一張
看下最後的效果圖:
為了增加一定的趣味,做了一個類似上面的相冊效果,支援拖動時自動變化,和點選變化~~是不是很贊~
1、首先看布局檔案:
android:background="@android:color/white"
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_horizontalscrollview.myhorizontalscrollview
android:id="@+id/id_horizontalscrollview"
android:layout_gravity="bottom"
android:background="@android:color/white"
</com.example.zhy_horizontalscrollview.myhorizontalscrollview>
沒任何變化,除了把類名改成了我們自定義的類~
2、為了和國際接軌,我們也搞個adapter,類似baseadapter
import java.util.list;
import android.content.context;
import android.view.viewgroup;
import android.widget.baseadapter;
public class horizontalscrollviewadapter
private context mcontext;
private list<integer> mdatas;
public horizontalscrollviewadapter(context context, list<integer> mdatas)
this.mcontext = context;
minflater = layoutinflater.from(context);
this.mdatas = mdatas;
public int getcount()
return mdatas.size();
public object getitem(int position)
return mdatas.get(position);
public long getitemid(int position)
return position;
public view getview(int position, view convertview, viewgroup parent)
viewholder viewholder = null;
if (convertview == null)
viewholder = new viewholder();
convertview = minflater.inflate(
r.layout.activity_index_gallery_item, parent, false);
viewholder.mimg = (imageview) convertview
viewholder.mtext = (textview) convertview
convertview.settag(viewholder);
} else
viewholder = (viewholder) convertview.gettag();
viewholder.mimg.setimageresource(mdatas.get(position));
viewholder.mtext.settext("some info ");
return convertview;
private class viewholder
imageview mimg;
textview mtext;
3、下面先看用法:
import java.util.arraylist;
import java.util.arrays;
import android.graphics.color;
import com.example.zhy_horizontalscrollview.myhorizontalscrollview.currentimagechangelistener;
import com.example.zhy_horizontalscrollview.myhorizontalscrollview.onitemclicklistener;
private myhorizontalscrollview mhorizontalscrollview;
private horizontalscrollviewadapter madapter;
private imageview mimg;
private list<integer> 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));
mimg = (imageview) findviewbyid(r.id.id_content);
mhorizontalscrollview = (myhorizontalscrollview) findviewbyid(r.id.id_horizontalscrollview);
madapter = new horizontalscrollviewadapter(this, mdatas);
//添加滾動回調
mhorizontalscrollview
.setcurrentimagechangelistener(new currentimagechangelistener()
{
@override
public void oncurrentimgchanged(int position,
view viewindicator)
{
mimg.setimageresource(mdatas.get(position));
viewindicator.setbackgroundcolor(color
.parsecolor("#aa024da4"));
}
});
//添加點選回調
mhorizontalscrollview.setonitemclicklistener(new onitemclicklistener()
@override
public void onclick(view view, int position)
{
mimg.setimageresource(mdatas.get(position));
view.setbackgroundcolor(color.parsecolor("#aa024da4"));
}
});
//設定擴充卡
mhorizontalscrollview.initdatas(madapter);
用起來是不是有點像listview,初始化資料擴充卡,然後設定資料擴充卡,然後就是設定各種回調~~
如果僅僅是一堆圖檔展示,類似商品切換,更見簡單,就不需要設定滾動監聽和點選監聽了~
4、最後看自定義的myhorizontalscrollview類
import java.util.hashmap;
import java.util.map;
import android.util.attributeset;
import android.util.displaymetrics;
import android.util.log;
import android.view.motionevent;
import android.view.view.onclicklistener;
import android.view.windowmanager;
import android.widget.horizontalscrollview;
public class myhorizontalscrollview extends horizontalscrollview implements
onclicklistener
/**
* 圖檔滾動時的回調接口
*
* @author zhy
*/
public interface currentimagechangelistener
void oncurrentimgchanged(int position, view viewindicator);
* 條目點選時的回調
public interface onitemclicklistener
void onclick(view view, int pos);
private currentimagechangelistener mlistener;
private onitemclicklistener monclicklistener;
private static final string tag = "myhorizontalscrollview";
* horizontallistview中的linearlayout
private linearlayout mcontainer;
* 子元素的寬度
private int mchildwidth;
* 子元素的高度
private int mchildheight;
* 目前最後一張圖檔的index
private int mcurrentindex;
* 目前第一張圖檔的下标
private int mfristindex;
* 目前第一個view
private view mfirstview;
* 資料擴充卡
* 每螢幕最多顯示的個數
private int mcountonescreen;
* 螢幕的寬度
private int mscreenwitdh;
* 儲存view與位置的鍵值對
private map<view, integer> mviewpos = new hashmap<view, integer>();
public myhorizontalscrollview(context context, attributeset attrs)
super(context, attrs);
// 獲得螢幕寬度
windowmanager wm = (windowmanager) context
.getsystemservice(context.window_service);
displaymetrics outmetrics = new displaymetrics();
wm.getdefaultdisplay().getmetrics(outmetrics);
mscreenwitdh = outmetrics.widthpixels;
protected void onmeasure(int widthmeasurespec, int heightmeasurespec)
super.onmeasure(widthmeasurespec, heightmeasurespec);
mcontainer = (linearlayout) getchildat(0);
* 加載下一張圖檔
protected void loadnextimg()
// 數組邊界值計算
if (mcurrentindex == madapter.getcount() - 1)
return;
//移除第一張圖檔,且将水準滾動位置置0
scrollto(0, 0);
mviewpos.remove(mcontainer.getchildat(0));
mcontainer.removeviewat(0);
//擷取下一張圖檔,并且設定onclick事件,且加入容器中
view view = madapter.getview(++mcurrentindex, null, mcontainer);
view.setonclicklistener(this);
mcontainer.addview(view);
mviewpos.put(view, mcurrentindex);
//目前第一張圖檔小标
mfristindex++;
//如果設定了滾動監聽則觸發
if (mlistener != null)
notifycurrentimgchanged();
* 加載前一張圖檔
protected void loadpreimg()
//如果目前已經是第一張,則傳回
if (mfristindex == 0)
//獲得目前應該顯示為第一張圖檔的下标
int index = mcurrentindex - mcountonescreen;
if (index >= 0)
// mcontainer = (linearlayout) getchildat(0);
//移除最後一張
int oldviewpos = mcontainer.getchildcount() - 1;
mviewpos.remove(mcontainer.getchildat(oldviewpos));
mcontainer.removeviewat(oldviewpos);
//将此view放入第一個位置
view view = madapter.getview(index, null, mcontainer);
mviewpos.put(view, index);
mcontainer.addview(view, 0);
view.setonclicklistener(this);
//水準滾動位置向左移動view的寬度個像素
scrollto(mchildwidth, 0);
//目前位置--,目前第一個顯示的下标--
mcurrentindex--;
mfristindex--;
//回調
if (mlistener != null)
notifycurrentimgchanged();
* 滑動時的回調
public void notifycurrentimgchanged()
//先清除所有的背景色,點選時會設定為藍色
for (int i = 0; i < mcontainer.getchildcount(); i++)
mcontainer.getchildat(i).setbackgroundcolor(color.white);
mlistener.oncurrentimgchanged(mfristindex, mcontainer.getchildat(0));
* 初始化資料,設定資料擴充卡
* @param madapter
public void initdatas(horizontalscrollviewadapter madapter)
this.madapter = madapter;
// 獲得擴充卡中第一個view
final view view = madapter.getview(0, null, mcontainer);
// 強制計算目前view的寬和高
if (mchildwidth == 0 && mchildheight == 0)
int w = view.measurespec.makemeasurespec(0,
view.measurespec.unspecified);
int h = view.measurespec.makemeasurespec(0,
view.measure(w, h);
mchildheight = view.getmeasuredheight();
mchildwidth = view.getmeasuredwidth();
log.e(tag, view.getmeasuredwidth() + "," + view.getmeasuredheight());
// 計算每次加載多少個view
mcountonescreen = mscreenwitdh / mchildwidth+2;
log.e(tag, "mcountonescreen = " + mcountonescreen
+ " ,mchildwidth = " + mchildwidth);
//初始化第一螢幕的元素
initfirstscreenchildren(mcountonescreen);
* 加載第一屏的view
* @param mcountonescreen
public void initfirstscreenchildren(int mcountonescreen)
mcontainer.removeallviews();
mviewpos.clear();
for (int i = 0; i < mcountonescreen; i++)
view view = madapter.getview(i, null, mcontainer);
mcontainer.addview(view);
mviewpos.put(view, i);
mcurrentindex = i;
public boolean ontouchevent(motionevent ev)
switch (ev.getaction())
case motionevent.action_move:
// log.e(tag, getscrollx() + "");
int scrollx = getscrollx();
// 如果目前scrollx為view的寬度,加載下一張,移除第一張
if (scrollx >= mchildwidth)
loadnextimg();
// 如果目前scrollx = 0, 往前設定一張,移除最後一張
if (scrollx == 0)
loadpreimg();
break;
return super.ontouchevent(ev);
public void onclick(view v)
if (monclicklistener != null)
for (int i = 0; i < mcontainer.getchildcount(); i++)
mcontainer.getchildat(i).setbackgroundcolor(color.white);
monclicklistener.onclick(v, mviewpos.get(v));
public void setonitemclicklistener(onitemclicklistener monclicklistener)
this.monclicklistener = monclicklistener;
public void setcurrentimagechangelistener(
currentimagechangelistener mlistener)
this.mlistener = mlistener;
首先,加載第一個item,根據item的寬計算目前螢幕可以加載多少張圖檔,然後初始化第一屏的圖檔,接下來就是從寫ontouchevent,在其中監聽使用者的action_move,然後根據移動的距離加載前一張或者後一張,同時動态移除不可見的view,回收記憶體~~~~
代碼中有個map專門存儲view和posion的,主要是為了給點選回調提供目前的view的位置,有點類似:android 自定義 viewpager 打造千變萬化的圖檔切換效果裡面的map的巧妙用法~~
是不是完全實作了viewpager和horizontalscrollview的合體~~~horizontalscrollview的效果,viewpager的特性~~~~
最後貼一下旋轉螢幕後的效果圖:
可以看出,不僅是做相冊,還是圖檔輪播想過都是剛剛的!
如果你的項目中需要用到gallery類似的效果,果斷使用上例嘗試吧~~
各位看官沒事點個贊,留個言呗~
源碼點選下載下傳