</pre>      <p></p><p>       近期看了一堂某在线IT学习的视频公开课,这里就不说名字了,省的有打广告的嫌疑。讲到了利用HorizontalScrollView仿ViewPager设计的一个简单相册。其实主要用了ViewPager缓存的思想。此篇博客参考<a target=_blank target="_blank" href="http://blog.csdn.net/lmj623565791/article/details/38140505" style="text-decoration:none; color:rgb(12,137,207); font-family:'microsoft yahei'; font-size:15px; line-height:35px">http://blog.csdn.net/lmj623565791/article/details/38140505</a>(这篇博客与公开课的讲的大致一样)</p><p>       这里简单说一下ViewPager的缓存机制</p><p>       1.进入ViewPager时,加载当前页和后一页;</p><p>       2.当滑动ViewPager至下一页时,加载后一页,此时第一页是不会销毁的,同时加载当前页的下一页。</p><p>其实就是默认加载3页,当前页,前一页和后一页。</p><p>       而此HorizontalScrollView是默认加载两页的,这个要注意,不然调度代码会让人晕。</p><p>       话不多说,上代码:</p><p>       代码结构如下图:<img src="https://img-blog.csdn.net/20160521161649394?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" /></p><p>       一个View,一个Adapter,一个MainActivity,相信不用解释,大家也相当清楚了,典型的MVC模式~</p><p></p><pre code_snippet_id="1691658" snippet_file_name="blog_20160521_2_7131836" name="code" class="java">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 implementsOnClickListener {private LinearLayout mContainer;// MyHorizontalScrollView中的LinearLayoutprivate int mChildWidth;// 子元素的宽度private int mChildHeight;// 子元素的高度private int mAllLastIndex;// 当前的最后一张的indexprivate int mdisplayLastIndex;// 当前显示的最后一张的indexprivate int mAllFirstIndex;// 当前的第一张indexprivate 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();// 计算每次加载多少个itemmdisplayLastIndex = 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));}@Overridepublic 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;}// 移除第一张图片,且将水平滚动位置置0scrollTo(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();}}}@Overridepublic 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));@Overrideprotected 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() {@Overridepublic void onCurrentImgChanged(int position, View view) {mImg.setImageResource(mDatas.get(position));view.setBackgroundColor(Color.parseColor("#6d9eeb"));}});mHorizontalScrollView.setmOnClickImageListener(new OnClickImageChangeListener() {@Overridepublic 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图有点卡,大家可以运行看效果):

下载地址:

点击打开链接

gitHub 地址:

点击打开github链接

仿ViewPager相册(使用HorizontalScrollView)相关推荐

  1. uniapp框架如何实现仿微信相册 | 图视频过滤、相册选择功能

    今天我们分享基于uniapp + vue实现仿微信相册实例,该插件完全还原了微信相册的功能 1: 相册选择 2: 图片,视频类型过滤 3: 自定义相册界面UI 技术实现 开发环境:HbuilderX ...

  2. uniapp框架如何实现仿微信相册插件 | 图视频编辑 + 压缩

    在上上篇文章中(),我们基于uniapp框架实现了仿微信相册中的拍照+录像功能.今天,就继续在uni-app中实现: 1: 图片编辑 2: 视频编辑 3: 文件压缩 技术实现 开发环境:Hbuilde ...

  3. 纯CSS的实现仿图片相册浏览模式代码

    演示下载 查看演示 立刻下载 本文标题: 纯CSS的实现仿图片相册浏览模式代码 固定链接: http://www.tcode.me/article/1215.html 来自淘代码转载请注明

  4. HTML精仿ios相册,高仿ios相册地图功能

    本篇文章已授权微信公众号 guolin_blog (郭霖) 独家发布 老规矩先上图, 高仿 ios 相册, 地图算法分析. 下载.gif 百度地图 SDK 新增点聚合功能.通过该功能,可通过缩小地图层 ...

  5. 一个带时间的相册页面(仿小米相册)

    一个带时间的相册页面,真小米手机仿小米相册 这个我研究了两天,真要命,昨晚搞出来的.在这里我们用了以下gradle: compile 'com.android.support:cardview-v7: ...

  6. 仿微信相册旋转箭头 - 不恢复原位 - androi view旋转180属性动画

    仿微信相册箭头旋转 - 不恢复原位 - androi view旋转180 思路:利用属性动画,动态设置view的旋转角度 1 .创建旋转动画 res/anim/rotate_view.xml < ...

  7. HTML精仿ios相册,iOS开发-仿微信相册选择Demo

    前言 这是一篇基于苹果PhotoKit框架,仿微信聊天内相册选择的Demo. 附上GIF: 使用方法: //先pod文件,目前最新版本0.3.0,作者持续更新,建议后期都pod最新版本 pod 'TJ ...

  8. 仿QQ相册RecyclerView滑动选中

    重要的事情在前面说 本文介绍的方法是基于坐标动态计算该Item在RecyclerView中的行列值实现的,是我最初的设计思路,相对有局限性,后面借鉴RecyclerView的相关API进行了重新设计新 ...

  9. android 横向相册,Android ViewPager相册横向移动的实现方法

    当我们第一次下载QQ并且打开的时候,会有一个新手引导,引导是几张图片,再加上一些文字说明,向右滑动,直到结束,今天一大早起来研究了一下关于此种效果的实现之ViewPager控件. 下面这个例子将用Vi ...

最新文章

  1. emmmmmm(官宣?)
  2. linux 线程与CPU绑定
  3. spoj453 Sums in a Triangle (tutorial) 动态规划
  4. sql基础教程mysql_SQL基础教程(第2版)笔记整理
  5. python列表添加元组_【Python爬虫】列表、元组、集合练习
  6. Java中list.forEach方法的使用示例-根据key获取对应的value
  7. linux查域名对应的ip 系统调用,DDNS 的工作原理及其在 Linux 上的实现
  8. 【链表】链表变化时其中的节点变化情况
  9. mysql用户创建,及授权
  10. Java学习笔记_字符串/静态static
  11. IBM软件OEM概览
  12. 牛客xiao白月赛32-- 拼三角(暴力却有坑)
  13. 5月16日亮相!华硕ZenFone 6新旗舰曝光:无刘海全面屏加持
  14. [ C语言 ] 用C语言实现小游戏 ---- 三子棋 代码 + 解析
  15. LQR控制算法的浅析
  16. 如何做专利挖掘,关键是寻找专利点,其实并不太难
  17. JavaScript高级程序设计[美]Nicholas C.Zakas著 读书笔记(二)
  18. Python爬虫下载视频(梨视频)
  19. Unity Shader 麻将平面阴影高光
  20. 论文浅尝 | ERNIE-ViL:从场景图中获取结构化知识来学习视觉语言联合表示

热门文章

  1. 第8组 团队展示(组长)
  2. 批量下载论文代码(对一篇论文的所有参考文献进行自动下载或者)
  3. java 分部类_C#中分部类和分部方法的应用
  4. 【Linux逻辑卷管理】之pvcreate、pvdisplay和pvremove
  5. ×××系统×××组网应用分析
  6. 计算机配置常用日语单词,日语词汇学习:计算机相关词汇(5)
  7. 6、【斯纳克图书馆管理系统】系统设置
  8. 16、【斯纳克图书馆管理系统】列印索书号
  9. iOS14.5beta4推送升级,安全性更新来了
  10. 自制Influxdb可视化管理工具