仿ViewPager相册(使用HorizontalScrollView)

12 篇文章 0 订阅

</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 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图有点卡,大家可以运行看效果):



下载地址:

点击打开链接

gitHub 地址:

点击打开github链接




     

       

      

       

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值