欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android利用HorizontalScrollView仿ViewPager设计简单相册

程序员文章站 2024-03-01 14:17:52
最近学习了一个视频公开课,讲到了利用horizontalscrollview仿viewpager设计的一个简单相册,其实主要用了viewpager缓存的思想。此篇文章参考:...

最近学习了一个视频公开课,讲到了利用horizontalscrollview仿viewpager设计的一个简单相册,其实主要用了viewpager缓存的思想。此篇文章参考:android自定义horizontalscrollview打造超强gallery效果(这篇文章与公开课的讲的大致一样)

 这里简单说一下viewpager的缓存机制

       1.进入viewpager时,加载当前页和后一页;

       2.当滑动viewpager至下一页时,加载后一页,此时第一页是不会销毁的,同时加载当前页的下一页。

其实就是默认加载3页,当前页,前一页和后一页。

而此horizontalscrollview是默认加载两页的,这个要注意,不然调度代码会让人晕。

话不多说,上代码:

代码结构如下图:

Android利用HorizontalScrollView仿ViewPager设计简单相册

一个view,一个adapter,一个mainactivity,相信不用解释,大家也相当清楚了,典型的mvc模式~

package com.ssa.horizontalscrollview.myview; 
 
import java.util.hashmap; 
import java.util.map; 
 
import com.ssa.horizontalscrollview.myutils.displayutil; 
 
import android.content.context; 
import android.graphics.color; 
import android.util.attributeset; 
import android.util.log; 
import android.view.motionevent; 
import android.view.view; 
import android.view.view.onclicklistener; 
import android.widget.horizontalscrollview; 
import android.widget.linearlayout; 
 
public class galleryhorizontalscrollview extends horizontalscrollview implements 
    onclicklistener { 
  private linearlayout mcontainer;// myhorizontalscrollview中的linearlayout 
  private int mchildwidth;// 子元素的宽度 
  private int mchildheight;// 子元素的高度 
 
  private int malllastindex;// 当前的最后一张的index 
  private int mdisplaylastindex;// 当前显示的最后一张的index 
  private int mallfirstindex;// 当前的第一张index 
 
  private galleryhorizontalscrollviewadapter madapter;// 数据适配器 
  private int mscreenwidth;// 屏幕的宽度 
 
  private int mcountonescreen; 
 
  private map<view, integer> mviewpos = new hashmap<view, integer>(); 
 
  private oncurrentimagechangelistener moncurrentimagechangelistener; 
 
  private onclickimagechangelistener monclickimagechangelistener; 
 
  public void setmoncurrentimagechangelistener( 
      oncurrentimagechangelistener mlistener) { 
    this.moncurrentimagechangelistener = mlistener; 
  } 
 
  public void setmonclickimagelistener(onclickimagechangelistener mlistener) { 
    this.monclickimagechangelistener = mlistener; 
  } 
 
  /** 
   * 图片滚动时回调接口 
   */ 
  public interface oncurrentimagechangelistener { 
    void oncurrentimgchanged(int position, view view); 
  } 
 
  /** 
   * 点击图片时回调接口 
   */ 
  public interface onclickimagechangelistener { 
    void onclickimagechangelistener(int position, view view); 
  } 
 
  public galleryhorizontalscrollview(context context, attributeset attrs) { 
    super(context, attrs); 
    // 获取屏幕宽度 
    mscreenwidth = getresources().getdisplaymetrics().widthpixels; 
  } 
 
  /** 
   * 初始化数据,设置适配器 
   */ 
  public void initdata(galleryhorizontalscrollviewadapter madapter) { 
    this.madapter = madapter; 
    mcontainer = (linearlayout) getchildat(0); 
    final view view = madapter.getview(0, null, mcontainer); 
    mcontainer.addview(view); 
    if (mchildheight == 0 && mchildwidth == 0) { 
      /*int w = view.measurespec.makemeasurespec(0, 
          view.measurespec.unspecified); 
      int h = view.measurespec.makemeasurespec(0, 
          view.measurespec.unspecified);*/ 
      /** 
       * 上面注释掉的是一位老师的写法,但我查了好多资料,用参数0和view.measurespec.unspecified是一种不太优美的做法; 
       * 好的做法应该是 
       * 当view为match_parent时,无法测量出view的大小(任玉刚大神讲的,确实是这么一回事,这个具体的原因要结合源码分析,可以看一下任大神的博客) 
       * 当view宽高为具体的数值时,比如100px: 
       * int w =view.measurespec.makemeasurespec(100, view.measurespec.exactly); 
       * int h =view.measurespec.makemeasurespec(100, view.measurespec.exactly); 
       * view.measure(w, h); 
       * 当view宽高为wrap_content时: 
       * int w =view.measurespec.makemeasurespec((1<<30)-1, view.measurespec.at_most); 
       * int h =view.measurespec.makemeasurespec((1<<30)-1, view.measurespec.at_most); 
       * view.measure(w, h); 
       * 
       * 我的此view高度为固定的150dip,宽度为wrap_content 
       */ 
      int heightpx = displayutil.dip2px(getcontext(), 150); 
      int w =view.measurespec.makemeasurespec((1<<30)-1, view.measurespec.at_most); 
      int h =view.measurespec.makemeasurespec(heightpx, view.measurespec.exactly); 
      view.measure(w, h); 
      mchildheight = view.getmeasuredheight(); 
      mchildwidth = view.getmeasuredwidth(); 
      // 计算每次加载多少个item 
      mdisplaylastindex = mscreenwidth / mchildwidth; 
      mcountonescreen = mdisplaylastindex + 1; 
      initfirstscreenchildren(mdisplaylastindex + 1); 
 
    } 
  } 
 
  /** 
   * 加载第一屏的元素 
   * 
   * @param mdisplaycountonescreen 
   */ 
  private void initfirstscreenchildren(int mdisplaycountonescreen) { 
    mcontainer = (linearlayout) getchildat(0); 
    mcontainer.removeallviews(); 
    mviewpos.clear(); 
    for (int i = 0; i < mdisplaycountonescreen; i++) { 
      view view = madapter.getview(i, null, mcontainer); 
      // 待完善的点击事件 
      view.setonclicklistener(this); 
      mcontainer.addview(view); 
      mviewpos.put(view, i); 
      malllastindex = i; 
    } 
 
    // 初始化并刷新界面 
    if (null != moncurrentimagechangelistener) { 
      notifycurrentimgchanged(); 
    } 
  } 
 
  private void notifycurrentimgchanged() { 
    // 先清除所有的背景颜色,点击时设置为蓝色 
    for (int i = 0; i < mcontainer.getchildcount(); i++) { 
      mcontainer.getchildat(i).setbackgroundcolor(color.white); 
    } 
    moncurrentimagechangelistener.oncurrentimgchanged(mallfirstindex, 
        mcontainer.getchildat(0)); 
  } 
 
  @override 
  public boolean ontouchevent(motionevent ev) { 
    /* 
     * log.e("x", getx()+""); log.e("childx", 
     * mcontainer.getchildat(0).getx()+""); log.e("rawx",getleft() +""); 
     */ 
    switch (ev.getaction()) { 
 
    case motionevent.action_move: 
      int scrollx = getscrollx(); 
      log.e("scrollx", scrollx + ""); 
      if (scrollx >= mchildwidth) { 
        // 加载下一页,移除第一张 
        loadnextimg(); 
      } 
      if (scrollx == 0) { 
        // 加载上一页,移除最后一张 
        loadpreimg(); 
      } 
      break; 
    } 
 
    return super.ontouchevent(ev); 
  } 
 
  private void loadnextimg() {// 数组边界值计算 
    if (malllastindex == madapter.getcount() - 1) { 
      return; 
    } 
    // 移除第一张图片,且将水平滚动位置置0 
    scrollto(0, 0); 
    mviewpos.remove(mcontainer.getchildat(0)); 
    mcontainer.removeviewat(0); 
 
    // 获取下一张图片,并且设置onclick事件,且加入容器中 
    view view = madapter.getview(++malllastindex, null, mcontainer); 
    view.setonclicklistener(this); 
    mcontainer.addview(view); 
    mviewpos.put(view, malllastindex); 
 
    // 当前第一张图片小标 
    mallfirstindex++; 
    // 如果设置了滚动监听则触发 
    if (moncurrentimagechangelistener != null) { 
      notifycurrentimgchanged(); 
    } 
 
  } 
 
  private void loadpreimg() { 
    if (mallfirstindex == 0) { 
      return; 
    } 
    int index = malllastindex - mcountonescreen; 
    if (index >= 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); 
 
      malllastindex--; 
      mallfirstindex--; 
 
      if (null != moncurrentimagechangelistener) { 
        notifycurrentimgchanged(); 
      } 
    } 
  } 
 
  @override 
  public void onclick(view v) { 
    if(null!=monclickimagechangelistener){ 
      monclickimagechangelistener.onclickimagechangelistener(mviewpos.get(v), v); 
    } 
  } 
} 

下面是adapter的源码:

package com.ssa.horizontalscrollview.myview; 
 
import java.util.list; 
 
import com.ssa.horizontalscrollview.r; 
 
import android.content.context; 
import android.view.layoutinflater; 
import android.view.view; 
import android.view.viewgroup; 
import android.widget.imageview; 
import android.widget.textview; 
 
public class galleryhorizontalscrollviewadapter { 
  private layoutinflater minflater; 
  private list<integer> mdatas; 
 
  public galleryhorizontalscrollviewadapter(context context, list<integer> mdatas) { 
    minflater = layoutinflater.from(context); 
    this.mdatas = mdatas; 
  } 
 
  public object getitem(int position) { 
    return mdatas.get(position); 
  } 
 
  public long getitemid(int position) { 
    return position; 
  } 
 
  public int getcount() { 
    return mdatas.size(); 
  } 
   
  public view getview(int position, view contentview, viewgroup parent) { 
    viewholder myholder = null; 
    if (null == contentview) { 
      contentview = minflater.inflate(r.layout.activity_gallery_item, 
          parent, false); 
      myholder = new viewholder(contentview); 
      contentview.settag(myholder); 
    }else { 
      myholder = (viewholder)contentview.gettag(); 
    } 
    myholder.ivimg.setimageresource(mdatas.get(position)); 
    myholder.tvtext.settext("img_"+position); 
     
     
    return contentview; 
  } 
 
  private static class viewholder { 
    imageview ivimg; 
    textview tvtext; 
 
    public viewholder(view view) { 
      ivimg = (imageview)view.findviewbyid(r.id.iv_content); 
      tvtext =(textview)view.findviewbyid(r.id.tv_index); 
    } 
  } 
 
} 

下面是mainactivity的源码:

package com.ssa.horizontalscrollview; 
 
import java.util.arraylist; 
import java.util.arrays; 
import java.util.list; 
 
import android.app.activity; 
import android.graphics.color; 
import android.os.bundle; 
import android.view.view; 
import android.widget.imageview; 
 
import com.ssa.horizontalscrollview.myview.galleryhorizontalscrollview; 
import com.ssa.horizontalscrollview.myview.galleryhorizontalscrollview.onclickimagechangelistener; 
import com.ssa.horizontalscrollview.myview.galleryhorizontalscrollview.oncurrentimagechangelistener; 
import com.ssa.horizontalscrollview.myview.galleryhorizontalscrollviewadapter; 
 
public class mainactivity extends activity { 
  private galleryhorizontalscrollview mhorizontalscrollview; 
  private galleryhorizontalscrollviewadapter 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)); 
   
   
 
  @override 
  protected void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.activity_main); 
    mimg = (imageview)findviewbyid(r.id.iv_content); 
    mhorizontalscrollview = (galleryhorizontalscrollview)findviewbyid(r.id.mhsv_gallery_container); 
    madapter = new galleryhorizontalscrollviewadapter(this, mdatas); 
    mhorizontalscrollview.setmoncurrentimagechangelistener(new oncurrentimagechangelistener() { 
       
      @override 
      public void oncurrentimgchanged(int position, view view) { 
        mimg.setimageresource(mdatas.get(position)); 
        view.setbackgroundcolor(color.parsecolor("#6d9eeb")); 
      } 
    }); 
    mhorizontalscrollview.setmonclickimagelistener(new onclickimagechangelistener() { 
       
      @override 
      public void onclickimagechangelistener(int position, view view) { 
        mimg.setimageresource(mdatas.get(position)); 
      } 
    }); 
    mhorizontalscrollview.initdata(madapter); 
  } 
} 

至些,调试运行,读者会发现,整个相册会非常卡,

Android利用HorizontalScrollView仿ViewPager设计简单相册

甚至有的图片还没有显示出来如img_4,看一下logcat,相信大家会发现原因:

Android利用HorizontalScrollView仿ViewPager设计简单相册

信息已经提示的很清楚了,图片太大,

此时大家应该明白了,笔者故意选择了几张很大的图片加载,虽然没大到直接让应用崩掉,但是体验性已经变得非常差了,这是因为课堂上的老师讲课时用的图片都是几十k的小图片,加载当然不会有问题,所以要想使这个相册作为一个实用的相册,还要处理图片过大的问题,不然,依旧会造成oom。

此时就用到这个工具类了:

package com.ssa.horizontalscrollview.myutils; 
 
import android.content.res.resources; 
import android.graphics.bitmap; 
import android.graphics.bitmapfactory; 
 
public class bitmaputil { 
  public static bitmap decodesampledbitmapfromresources(resources res, 
      int resid, int reqwidth, int reqheight) { 
    final bitmapfactory.options options = new bitmapfactory.options(); 
    options.injustdecodebounds = true; 
    bitmapfactory.decoderesource(res, resid, options); 
    options.insamplesize = calculateinsamplesize(options, reqwidth, 
        reqheight); 
    options.injustdecodebounds = false; 
    return bitmapfactory.decoderesource(res, resid, options); 
 
  } 
 
  public static int calculateinsamplesize(bitmapfactory.options options, 
      int reqwidth, int reqheight) { 
    final int height = options.outheight; 
    final int width = options.outwidth; 
    int insamplesize = 1; 
    if (height > reqheight || width > reqwidth) { 
      final int halfheight = height / 2; 
      final int halfwidth = width / 2; 
      while ((halfheight / insamplesize) >= reqheight 
          && (halfwidth / insamplesize) >= reqwidth) { 
        insamplesize *= 2; 
      } 
    } 
 
    return insamplesize; 
  } 
} 

添加了这个工具类,上面几个类的代码也要略微修改一下,具体怎么改,大家可以下载下面我上传的源码:
至于效果如下动图所示(生成的gif图有点卡,大家可以运行看效果):

Android利用HorizontalScrollView仿ViewPager设计简单相册

源码下载:horizontalscrollview仿viewpager设计相册

以上就是本文的全部内容,希望对大家学习android软件编程有所帮助。