天天看點

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

轉載請标明出處: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

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果
Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

<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

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果
Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

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的布局就省了,後面會貼源碼。

效果圖:

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

效果還是不錯的~如果隻需要簡單展示幾張圖檔,直接用就可以了。

下面準備進入正題,horizontalscrollview不管裡面多少view都是不會回收的,當達到一定量的時候會發生oom,下面介紹如何改寫horizontalscollview實作文章開始所說的效果。

2、自定義horizontalscrollview

思想:

1、首先根據螢幕的大小和item的大小,計算可以一個螢幕最多可以加載多少個item,然後加載該數量item。

2、當使用者右滑(從右向左),滑動到一定距離時,加載下一張,删除第一張

3、當使用者左滑(從左向右),滑動到一定距離時,加載上一張,删除最後一張

看下最後的效果圖:

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

為了增加一定的趣味,做了一個類似上面的相冊效果,支援拖動時自動變化,和點選變化~~是不是很贊~

1、首先看布局檔案:

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果
Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

    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

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果
Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

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、下面先看用法:

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果
Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

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類

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果
Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

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的特性~~~~

最後貼一下旋轉螢幕後的效果圖:

Android 自定義 HorizontalScrollView 打造再多圖檔(控件)也不怕 OOM 的橫向滑動效果

可以看出,不僅是做相冊,還是圖檔輪播想過都是剛剛的!

如果你的項目中需要用到gallery類似的效果,果斷使用上例嘗試吧~~

各位看官沒事點個贊,留個言呗~

源碼點選下載下傳

繼續閱讀