当前位置: 移动技术网 > IT编程>移动开发>Android > Android利用HorizontalScrollView仿ViewPager设计简单相册

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

2019年07月24日  | 移动技术网IT编程  | 我要评论

双六,新疆地税网,风月书阁marketiva

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

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

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

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

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

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

话不多说,上代码:

代码结构如下图:

一个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); 
  } 
} 

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

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

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

此时大家应该明白了,笔者故意选择了几张很大的图片加载,虽然没大到直接让应用崩掉,但是体验性已经变得非常差了,这是因为课堂上的老师讲课时用的图片都是几十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软件编程有所帮助。

如对本文有疑问,请在下面进行留言讨论,广大热心网友会与你互动!! 点击进行留言回复

相关文章:

验证码:
移动技术网