前面写过一篇Android 画廊效果,具体参考:http://blog.csdn.net/shineflowers/article/details/40543137

自从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
  1. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. >
  6. <HorizontalScrollView
  7. android:layout_width="wrap_content"
  8. android:layout_height="150dp"
  9. android:layout_gravity="center_vertical"
  10. android:background="#AA444444"
  11. android:scrollbars="none">
  12. <LinearLayout
  13. android:id="@+id/id_gallery"
  14. android:layout_width="wrap_content"
  15. android:layout_height="wrap_content"
  16. android:layout_gravity="center_vertical"
  17. android:orientation="horizontal">
  18. </LinearLayout>
  19. </HorizontalScrollView>
  20. </LinearLayout>

很简单,就一个HorizontalScrollView内部有个水平方向的LinearLayout

MainActivity:

[java]view plaincopy
  1. packagecom.example.zhy_horizontalscrollview;
  2. importandroid.app.Activity;
  3. importandroid.os.Bundle;
  4. importandroid.view.LayoutInflater;
  5. importandroid.view.View;
  6. importandroid.view.Window;
  7. importandroid.widget.ImageView;
  8. importandroid.widget.LinearLayout;
  9. importandroid.widget.TextView;
  10. publicclassMainActivityextendsActivity
  11. {
  12. privateLinearLayoutmGallery;
  13. privateint[]mImgIds;
  14. privateLayoutInflatermInflater;
  15. @Override
  16. protectedvoidonCreate(BundlesavedInstanceState)
  17. {
  18. super.onCreate(savedInstanceState);
  19. requestWindowFeature(Window.FEATURE_NO_TITLE);
  20. setContentView(R.layout.activity_main);
  21. mInflater=LayoutInflater.from(this);
  22. initData();
  23. initView();
  24. }
  25. privatevoidinitData()
  26. {
  27. mImgIds=newint[]{R.drawable.a,R.drawable.b,R.drawable.c,
  28. R.drawable.d,R.drawable.e,R.drawable.f,R.drawable.g,
  29. R.drawable.h,R.drawable.l};
  30. }
  31. privatevoidinitView()
  32. {
  33. mGallery=(LinearLayout)findViewById(R.id.id_gallery);
  34. for(inti=0;i<mImgIds.length;i++)
  35. {
  36. Viewview=mInflater.inflate(R.layout.activity_index_gallery_item,
  37. mGallery,false);
  38. ImageViewimg=(ImageView)view
  39. .findViewById(R.id.id_index_gallery_item_image);
  40. img.setImageResource(mImgIds[i]);
  41. TextViewtxt=(TextView)view
  42. .findViewById(R.id.id_index_gallery_item_text);
  43. txt.setText("someinfo");
  44. mGallery.addView(view);
  45. }
  46. }
  47. }
很简单,我预先准备了一些图片直接放在了Drawble下,然后循环加入HorizontalScrollView的LinearLayout中即可,Item的布局就省了,后面会贴源码。

效果图:

效果还是不错的~如果只需要简单展示几张图片,直接用就可以了。

下面准备进入正题,HorizontalScrollView不管里面多少View都是不会回收的,当达到一定量的时候会发生OOM,下面介绍如何改写HorizontalScollView实现文章开始所说的效果。

2、自定义HorizontalScrollView

思想:

1、首先根据屏幕的大小和Item的大小,计算可以一个屏幕最多可以加载多少个Item,然后加载该数量Item。

2、当用户右滑(从右向左),滑动到一定距离时,加载下一张,删除第一张

3、当用户左滑(从左向右),滑动到一定距离时,加载上一张,删除最后一张

看下最后的效果图:


为了增加一定的趣味,做了一个类似上面的相册效果,支持拖动时自动变化,和点击变化~~是不是很赞~

1、首先看布局文件:

[html]view plaincopy
  1. <LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent"
  5. android:background="@android:color/white"
  6. android:orientation="vertical">
  7. <FrameLayout
  8. android:layout_width="fill_parent"
  9. android:layout_height="0dp"
  10. android:layout_weight="1">
  11. <ImageView
  12. android:id="@+id/id_content"
  13. android:layout_width="fill_parent"
  14. android:layout_height="fill_parent"
  15. android:layout_gravity="center"
  16. android:layout_margin="10dp"
  17. android:scaleType="centerCrop"
  18. android:src="@drawable/ic_launcher"/>
  19. </FrameLayout>
  20. <com.example.zhy_horizontalscrollview.MyHorizontalScrollView
  21. android:id="@+id/id_horizontalScrollView"
  22. android:layout_width="wrap_content"
  23. android:layout_height="150dp"
  24. android:layout_gravity="bottom"
  25. android:background="@android:color/white"
  26. android:scrollbars="none">
  27. <LinearLayout
  28. android:id="@+id/id_gallery"
  29. android:layout_width="wrap_content"
  30. android:layout_height="wrap_content"
  31. android:layout_gravity="center_vertical"
  32. android:orientation="horizontal">
  33. </LinearLayout>
  34. </com.example.zhy_horizontalscrollview.MyHorizontalScrollView>
  35. </LinearLayout>
没任何变化,除了把类名改成了我们自定义的类~

2、为了和国际接轨,我们也搞个Adapter,类似BaseAdapter

[java]view plaincopy
  1. packagecom.example.zhy_horizontalscrollview;
  2. importjava.util.List;
  3. importandroid.content.Context;
  4. importandroid.view.LayoutInflater;
  5. importandroid.view.View;
  6. importandroid.view.ViewGroup;
  7. importandroid.widget.BaseAdapter;
  8. importandroid.widget.ImageView;
  9. importandroid.widget.TextView;
  10. publicclassHorizontalScrollViewAdapter
  11. {
  12. privateContextmContext;
  13. privateLayoutInflatermInflater;
  14. privateList<Integer>mDatas;
  15. publicHorizontalScrollViewAdapter(Contextcontext,List<Integer>mDatas)
  16. {
  17. this.mContext=context;
  18. mInflater=LayoutInflater.from(context);
  19. this.mDatas=mDatas;
  20. }
  21. publicintgetCount()
  22. {
  23. returnmDatas.size();
  24. }
  25. publicObjectgetItem(intposition)
  26. {
  27. returnmDatas.get(position);
  28. }
  29. publiclonggetItemId(intposition)
  30. {
  31. returnposition;
  32. }
  33. publicViewgetView(intposition,ViewconvertView,ViewGroupparent)
  34. {
  35. ViewHolderviewHolder=null;
  36. if(convertView==null)
  37. {
  38. viewHolder=newViewHolder();
  39. convertView=mInflater.inflate(
  40. R.layout.activity_index_gallery_item,parent,false);
  41. viewHolder.mImg=(ImageView)convertView
  42. .findViewById(R.id.id_index_gallery_item_image);
  43. viewHolder.mText=(TextView)convertView
  44. .findViewById(R.id.id_index_gallery_item_text);
  45. convertView.setTag(viewHolder);
  46. }else
  47. {
  48. viewHolder=(ViewHolder)convertView.getTag();
  49. }
  50. viewHolder.mImg.setImageResource(mDatas.get(position));
  51. viewHolder.mText.setText("someinfo");
  52. returnconvertView;
  53. }
  54. privateclassViewHolder
  55. {
  56. ImageViewmImg;
  57. TextViewmText;
  58. }
  59. }

3、下面先看用法:

[java]view plaincopy
  1. packagecom.example.zhy_horizontalscrollview;
  2. importjava.util.ArrayList;
  3. importjava.util.Arrays;
  4. importjava.util.List;
  5. importandroid.app.Activity;
  6. importandroid.graphics.Color;
  7. importandroid.os.Bundle;
  8. importandroid.view.View;
  9. importandroid.view.Window;
  10. importandroid.widget.ImageView;
  11. importcom.example.zhy_horizontalscrollview.MyHorizontalScrollView.CurrentImageChangeListener;
  12. importcom.example.zhy_horizontalscrollview.MyHorizontalScrollView.OnItemClickListener;
  13. publicclassMainActivityextendsActivity
  14. {
  15. privateMyHorizontalScrollViewmHorizontalScrollView;
  16. privateHorizontalScrollViewAdaptermAdapter;
  17. privateImageViewmImg;
  18. privateList<Integer>mDatas=newArrayList<Integer>(Arrays.asList(
  19. R.drawable.a,R.drawable.b,R.drawable.c,R.drawable.d,
  20. R.drawable.e,R.drawable.f,R.drawable.g,R.drawable.h,
  21. R.drawable.l));
  22. @Override
  23. protectedvoidonCreate(BundlesavedInstanceState)
  24. {
  25. super.onCreate(savedInstanceState);
  26. requestWindowFeature(Window.FEATURE_NO_TITLE);
  27. setContentView(R.layout.activity_main);
  28. mImg=(ImageView)findViewById(R.id.id_content);
  29. mHorizontalScrollView=(MyHorizontalScrollView)findViewById(R.id.id_horizontalScrollView);
  30. mAdapter=newHorizontalScrollViewAdapter(this,mDatas);
  31. //添加滚动回调
  32. mHorizontalScrollView
  33. .setCurrentImageChangeListener(newCurrentImageChangeListener()
  34. {
  35. @Override
  36. publicvoidonCurrentImgChanged(intposition,
  37. ViewviewIndicator)
  38. {
  39. mImg.setImageResource(mDatas.get(position));
  40. viewIndicator.setBackgroundColor(Color
  41. .parseColor("#AA024DA4"));
  42. }
  43. });
  44. //添加点击回调
  45. mHorizontalScrollView.setOnItemClickListener(newOnItemClickListener()
  46. {
  47. @Override
  48. publicvoidonClick(Viewview,intposition)
  49. {
  50. mImg.setImageResource(mDatas.get(position));
  51. view.setBackgroundColor(Color.parseColor("#AA024DA4"));
  52. }
  53. });
  54. //设置适配器
  55. mHorizontalScrollView.initDatas(mAdapter);
  56. }
  57. }

用起来是不是有点像ListView,初始化数据适配器,然后设置数据适配器,然后就是设置各种回调~~

如果仅仅是一堆图片展示,类似商品切换,更见简单,就不需要设置滚动监听和点击监听了~

4、最后看自定义的MyHorizontalScrollView类

[java]view plaincopy
  1. packagecom.example.zhy_horizontalscrollview;
  2. importjava.util.HashMap;
  3. importjava.util.Map;
  4. importandroid.content.Context;
  5. importandroid.graphics.Color;
  6. importandroid.util.AttributeSet;
  7. importandroid.util.DisplayMetrics;
  8. importandroid.util.Log;
  9. importandroid.view.MotionEvent;
  10. importandroid.view.View;
  11. importandroid.view.View.OnClickListener;
  12. importandroid.view.WindowManager;
  13. importandroid.widget.HorizontalScrollView;
  14. importandroid.widget.LinearLayout;
  15. publicclassMyHorizontalScrollViewextendsHorizontalScrollViewimplements
  16. OnClickListener
  17. {
  18. /**
  19. *图片滚动时的回调接口
  20. *
  21. *@authorzhy
  22. *
  23. */
  24. publicinterfaceCurrentImageChangeListener
  25. {
  26. voidonCurrentImgChanged(intposition,ViewviewIndicator);
  27. }
  28. /**
  29. *条目点击时的回调
  30. *
  31. *@authorzhy
  32. *
  33. */
  34. publicinterfaceOnItemClickListener
  35. {
  36. voidonClick(Viewview,intpos);
  37. }
  38. privateCurrentImageChangeListenermListener;
  39. privateOnItemClickListenermOnClickListener;
  40. privatestaticfinalStringTAG="MyHorizontalScrollView";
  41. /**
  42. *HorizontalListView中的LinearLayout
  43. */
  44. privateLinearLayoutmContainer;
  45. /**
  46. *子元素的宽度
  47. */
  48. privateintmChildWidth;
  49. /**
  50. *子元素的高度
  51. */
  52. privateintmChildHeight;
  53. /**
  54. *当前最后一张图片的index
  55. */
  56. privateintmCurrentIndex;
  57. /**
  58. *当前第一张图片的下标
  59. */
  60. privateintmFristIndex;
  61. /**
  62. *当前第一个View
  63. */
  64. privateViewmFirstView;
  65. /**
  66. *数据适配器
  67. */
  68. privateHorizontalScrollViewAdaptermAdapter;
  69. /**
  70. *每屏幕最多显示的个数
  71. */
  72. privateintmCountOneScreen;
  73. /**
  74. *屏幕的宽度
  75. */
  76. privateintmScreenWitdh;
  77. /**
  78. *保存View与位置的键值对
  79. */
  80. privateMap<View,Integer>mViewPos=newHashMap<View,Integer>();
  81. publicMyHorizontalScrollView(Contextcontext,AttributeSetattrs)
  82. {
  83. super(context,attrs);
  84. //获得屏幕宽度
  85. WindowManagerwm=(WindowManager)context
  86. .getSystemService(Context.WINDOW_SERVICE);
  87. DisplayMetricsoutMetrics=newDisplayMetrics();
  88. wm.getDefaultDisplay().getMetrics(outMetrics);
  89. mScreenWitdh=outMetrics.widthPixels;
  90. }
  91. @Override
  92. protectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec)
  93. {
  94. super.onMeasure(widthMeasureSpec,heightMeasureSpec);
  95. mContainer=(LinearLayout)getChildAt(0);
  96. }
  97. /**
  98. *加载下一张图片
  99. */
  100. protectedvoidloadNextImg()
  101. {
  102. //数组边界值计算
  103. if(mCurrentIndex==mAdapter.getCount()-1)
  104. {
  105. return;
  106. }
  107. //移除第一张图片,且将水平滚动位置置0
  108. scrollTo(0,0);
  109. mViewPos.remove(mContainer.getChildAt(0));
  110. mContainer.removeViewAt(0);
  111. //获取下一张图片,并且设置onclick事件,且加入容器中
  112. Viewview=mAdapter.getView(++mCurrentIndex,null,mContainer);
  113. view.setOnClickListener(this);
  114. mContainer.addView(view);
  115. mViewPos.put(view,mCurrentIndex);
  116. //当前第一张图片小标
  117. mFristIndex++;
  118. //如果设置了滚动监听则触发
  119. if(mListener!=null)
  120. {
  121. notifyCurrentImgChanged();
  122. }
  123. }
  124. /**
  125. *加载前一张图片
  126. */
  127. protectedvoidloadPreImg()
  128. {
  129. //如果当前已经是第一张,则返回
  130. if(mFristIndex==0)
  131. return;
  132. //获得当前应该显示为第一张图片的下标
  133. intindex=mCurrentIndex-mCountOneScreen;
  134. if(index>=0)
  135. {
  136. //mContainer=(LinearLayout)getChildAt(0);
  137. //移除最后一张
  138. intoldViewPos=mContainer.getChildCount()-1;
  139. mViewPos.remove(mContainer.getChildAt(oldViewPos));
  140. mContainer.removeViewAt(oldViewPos);
  141. //将此View放入第一个位置
  142. Viewview=mAdapter.getView(index,null,mContainer);
  143. mViewPos.put(view,index);
  144. mContainer.addView(view,0);
  145. view.setOnClickListener(this);
  146. //水平滚动位置向左移动view的宽度个像素
  147. scrollTo(mChildWidth,0);
  148. //当前位置--,当前第一个显示的下标--
  149. mCurrentIndex--;
  150. mFristIndex--;
  151. //回调
  152. if(mListener!=null)
  153. {
  154. notifyCurrentImgChanged();
  155. }
  156. }
  157. }
  158. /**
  159. *滑动时的回调
  160. */
  161. publicvoidnotifyCurrentImgChanged()
  162. {
  163. //先清除所有的背景色,点击时会设置为蓝色
  164. for(inti=0;i<mContainer.getChildCount();i++)
  165. {
  166. mContainer.getChildAt(i).setBackgroundColor(Color.WHITE);
  167. }
  168. mListener.onCurrentImgChanged(mFristIndex,mContainer.getChildAt(0));
  169. }
  170. /**
  171. *初始化数据,设置数据适配器
  172. *
  173. *@parammAdapter
  174. */
  175. publicvoidinitDatas(HorizontalScrollViewAdaptermAdapter)
  176. {
  177. this.mAdapter=mAdapter;
  178. mContainer=(LinearLayout)getChildAt(0);
  179. //获得适配器中第一个View
  180. finalViewview=mAdapter.getView(0,null,mContainer);
  181. mContainer.addView(view);
  182. //强制计算当前View的宽和高
  183. if(mChildWidth==0&&mChildHeight==0)
  184. {
  185. intw=View.MeasureSpec.makeMeasureSpec(0,
  186. View.MeasureSpec.UNSPECIFIED);
  187. inth=View.MeasureSpec.makeMeasureSpec(0,
  188. View.MeasureSpec.UNSPECIFIED);
  189. view.measure(w,h);
  190. mChildHeight=view.getMeasuredHeight();
  191. mChildWidth=view.getMeasuredWidth();
  192. Log.e(TAG,view.getMeasuredWidth()+","+view.getMeasuredHeight());
  193. mChildHeight=view.getMeasuredHeight();
  194. //计算每次加载多少个View
  195. mCountOneScreen=mScreenWitdh/mChildWidth+2;
  196. Log.e(TAG,"mCountOneScreen="+mCountOneScreen
  197. +",mChildWidth="+mChildWidth);
  198. }
  199. //初始化第一屏幕的元素
  200. initFirstScreenChildren(mCountOneScreen);
  201. }
  202. /**
  203. *加载第一屏的View
  204. *
  205. *@parammCountOneScreen
  206. */
  207. publicvoidinitFirstScreenChildren(intmCountOneScreen)
  208. {
  209. mContainer=(LinearLayout)getChildAt(0);
  210. mContainer.removeAllViews();
  211. mViewPos.clear();
  212. for(inti=0;i<mCountOneScreen;i++)
  213. {
  214. Viewview=mAdapter.getView(i,null,mContainer);
  215. view.setOnClickListener(this);
  216. mContainer.addView(view);
  217. mViewPos.put(view,i);
  218. mCurrentIndex=i;
  219. }
  220. if(mListener!=null)
  221. {
  222. notifyCurrentImgChanged();
  223. }
  224. }
  225. @Override
  226. publicbooleanonTouchEvent(MotionEventev)
  227. {
  228. switch(ev.getAction())
  229. {
  230. caseMotionEvent.ACTION_MOVE:
  231. //Log.e(TAG,getScrollX()+"");
  232. intscrollX=getScrollX();
  233. //如果当前scrollX为view的宽度,加载下一张,移除第一张
  234. if(scrollX>=mChildWidth)
  235. {
  236. loadNextImg();
  237. }
  238. //如果当前scrollX=0,往前设置一张,移除最后一张
  239. if(scrollX==0)
  240. {
  241. loadPreImg();
  242. }
  243. break;
  244. }
  245. returnsuper.onTouchEvent(ev);
  246. }
  247. @Override
  248. publicvoidonClick(Viewv)
  249. {
  250. if(mOnClickListener!=null)
  251. {
  252. for(inti=0;i<mContainer.getChildCount();i++)
  253. {
  254. mContainer.getChildAt(i).setBackgroundColor(Color.WHITE);
  255. }
  256. mOnClickListener.onClick(v,mViewPos.get(v));
  257. }
  258. }
  259. publicvoidsetOnItemClickListener(OnItemClickListenermOnClickListener)
  260. {
  261. this.mOnClickListener=mOnClickListener;
  262. }
  263. publicvoidsetCurrentImageChangeListener(
  264. CurrentImageChangeListenermListener)
  265. {
  266. this.mListener=mListener;
  267. }
  268. }

首先,加载第一个Item,根据item的宽计算当前屏幕可以加载多少张图片,然后初始化第一屏的图片,接下来就是从写onTouchEvent,在其中监听用户的ACTION_MOVE,然后根据移动的距离加载前一张或者后一张,同时动态移除不可见的View,回收内存~~~~

代码中有个Map专门存储View和posion的,主要是为了给点击回调提供当前的View的位置,有点类似:Android 自定义 ViewPager 打造千变万化的图片切换效果里面的Map的巧妙用法~~

是不是完全实现了ViewPager和HorizontalScrollView的合体~~~HorizontalScrollView的效果,ViewPager的特性~~~~

最后贴一下旋转屏幕后的效果图:


可以看出,不仅是做相册,还是图片轮播想过都是刚刚的!

转载:http://blog.csdn.net/lmj623565791/article/details/38140505

更多相关文章

  1. Android(安卓)-- 超全的 File,Bitmap,Drawable,Uri, FilePath ,byte
  2. android红米等关于读取本地文件夹图片获取路径的问题的解决
  3. Android使用webview,触发网页中链接的事件 以及webview加载本地ht
  4. android RadioGroup实现单选以及默认选中
  5. android studio使用jni调用opencv库实现图片转换【详细实例】(二)
  6. android 音频开发之混响效果
  7. android上的一个网络接口和图片缓存框架enif
  8. Android(安卓)指南针
  9. android webview 加载本地html并且解决多图卡顿问题

随机推荐

  1. android类
  2. Android 属性文件build.prop,获取属性以及
  3. android 抽屉的一些小问题
  4. LinearLayout中gravity和layout_gravity
  5. Android应用程序基础
  6. Android(安卓)二级列表
  7. Android CTS 测试总结【转】
  8. Android之 UI主线程
  9. Android 在界面中显示以及输入文本信息 T
  10. android ListView控件 去上下滑动阴影 选