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

最近学习了一个视频公开课,讲到了利用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图有点卡,大家可以运行看效果):

源码下载:HorizontalScrollView仿ViewPager设计相册

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

就是对虚怀若谷谦虚谨慎八个字真正理解的人,

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

相关文章:

你感兴趣的文章:

标签云: