Android开发之利用Viewpager实现图片的预览
1.首先看下效果图
运行效果图
2.利用ViewPager图片的预览,类似Gallery的动画效果。下面给出讲解下实现的原理
<1>.原理介绍
它的作用就是让Viewpager展示多个条目,此PageTransformer是为了在Viewpager外面展示图片。
所以PageTransformer并没有改变viewpager的大小状态,我们在viewpager的父控件中拦截dispatchTouchEvent的方法来控制viewpager的滑动和相应点击事件,以此来实现示例中两侧的图片即可点击也可滑动的动画效果。
<2>.具体的代码实现
代码结构图
-
grally文件包下的类的实现
GalleryViewPager.java类的实现
public class GalleryViewPager extends ViewPager {
//默认距离
private final static float DISTANCE = 10;
private float downX;
private float downY;
public GalleryViewPager(Context context) {
super(context);
}
public GalleryViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if(ev.getAction() == MotionEvent.ACTION_DOWN){
downX = ev.getX();
downY = ev.getY();
}else if (ev.getAction() == MotionEvent.ACTION_UP) {
float upX = ev.getX();
float upY = ev.getY();
//如果 up的位置和down 的位置 距离 > 设置的距离,则事件继续传递,不执行下面的点击切换事件
if(Math.abs(upX - downX) > DISTANCE || Math.abs(upY - downY) > DISTANCE){
return super.dispatchTouchEvent(ev);
}
View view = viewOfClickOnScreen(ev);
if (view != null) {
int index = (Integer) view.getTag();
if (getCurrentItem() != index) {
setCurrentItem(index);
}
}
}
return super.dispatchTouchEvent(ev);
}
/**
* @param ev
* @return
*/
private View viewOfClickOnScreen(MotionEvent ev) {
int childCount = getChildCount();
int currentIndex = getCurrentItem();
int[] location = new int[2];
for (int i = 0; i < childCount; i++) {
View v = getChildAt(i);
int position = (Integer) v.getTag();
v.getLocationOnScreen(location);
int minX = location[0];
int minY = location[1];
int maxX = location[0] + v.getWidth();
int maxY = location[1] + v.getHeight();
if(position < currentIndex){
maxX -= v.getWidth() * (1 - ScalePageTransformer.MIN_SCALE) * 0.5 + v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE)) * 0.5;
minX -= v.getWidth() * (1 - ScalePageTransformer.MIN_SCALE) * 0.5 + v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE)) * 0.5;
}else if(position == currentIndex){
minX += v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE));
}else if(position > currentIndex){
maxX -= v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE)) * 0.5;
minX -= v.getWidth() * (Math.abs(1 - ScalePageTransformer.MAX_SCALE)) * 0.5;
}
float x = ev.getRawX();
float y = ev.getRawY();
if ((x > minX && x < maxX) && (y > minY && y < maxY)) {
return v;
}
}
return null;
}
}
ScalePageTransformer.java类的实现
public class ScalePageTransformer implements ViewPager.PageTransformer {
public static final float MAX_SCALE = 1.2f;
public static final float MIN_SCALE = 0.6f;
@Override
public void transformPage(View page, float position) {
if (position < -1) {
position = -1;
} else if (position > 1) {
position = 1;
}
float tempScale = position < 0 ? 1 + position : 1 - position;
float slope = (MAX_SCALE - MIN_SCALE) / 1;
//一个公式
float scaleValue = MIN_SCALE + tempScale * slope;
page.setScaleX(scaleValue);
page.setScaleY(scaleValue);
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
page.getParent().requestLayout();
}
}
}
-
adapter文件包下类的代码实现
AdapterBean.java类的代码实现
public class AdapterBean {
private View[] activeViews = new View[0];
private int[] activeViewTypes = new int[0];
private SparseArray<View>[] scrapViews;
private int viewTypeCount;
private SparseArray<View> currentScrapViews;
public void setViewTypeCount(int viewTypeCount) {
if (viewTypeCount < 1) {
return;
}
//noinspection unchecked
SparseArray<View>[] scrapViews = new SparseArray[viewTypeCount];
for (int i = 0; i < viewTypeCount; i++) {
scrapViews[i] = new SparseArray<View>();
}
this.viewTypeCount = viewTypeCount;
currentScrapViews = scrapViews[0];
this.scrapViews = scrapViews;
}
protected boolean shouldRecycleViewType(int viewType) {
return viewType >= 0;
}
View getScrapView(int position, int viewType) {
if (viewTypeCount == 1) {
return retrieveFromScrap(currentScrapViews, position);
} else if (viewType >= 0 && viewType < scrapViews.length) {
return retrieveFromScrap(scrapViews[viewType], position);
}
return null;
}
void addScrapView(View scrap, int position, int viewType) {
if (viewTypeCount == 1) {
currentScrapViews.put(position, scrap);
} else {
scrapViews[viewType].put(position, scrap);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
scrap.setAccessibilityDelegate(null);
}
}
void scrapActiveViews() {
final View[] activeViews = this.activeViews;
final int[] activeViewTypes = this.activeViewTypes;
final boolean multipleScraps = viewTypeCount > 1;
SparseArray<View> scrapViews = currentScrapViews;
final int count = activeViews.length;
for (int i = count - 1; i >= 0; i--) {
final View victim = activeViews[i];
if (victim != null) {
int whichScrap = activeViewTypes[i];
activeViews[i] = null;
activeViewTypes[i] = -1;
if (!shouldRecycleViewType(whichScrap)) {
continue;
}
if (multipleScraps) {
scrapViews = this.scrapViews[whichScrap];
}
scrapViews.put(i, victim);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
victim.setAccessibilityDelegate(null);
}
}
}
pruneScrapViews();
}
/**
* Makes sure that the size of scrapViews does not exceed the size of activeViews.
* (This can happen if an adapter does not recycle its views).
*/
private void pruneScrapViews() {
final int maxViews = activeViews.length;
final int viewTypeCount = this.viewTypeCount;
final SparseArray<View>[] scrapViews = this.scrapViews;
for (int i = 0; i < viewTypeCount; ++i) {
final SparseArray<View> scrapPile = scrapViews[i];
int size = scrapPile.size();
final int extras = size - maxViews;
size--;
for (int j = 0; j < extras; j++) {
scrapPile.remove(scrapPile.keyAt(size--));
}
}
}
static View retrieveFromScrap(SparseArray<View> scrapViews, int position) {
int size = scrapViews.size();
if (size > 0) {
// See if we still have a view for this position.
for (int i = 0; i < size; i++) {
int fromPosition = scrapViews.keyAt(i);
View view = scrapViews.get(fromPosition);
if (fromPosition == position) {
scrapViews.remove(fromPosition);
return view;
}
}
int index = size - 1;
View r = scrapViews.valueAt(index);
scrapViews.remove(scrapViews.keyAt(index));
return r;
} else {
return null;
}
}
}
MyPageradapter.java类的代码实现
public abstract class MyPageradapter extends PagerAdapter {
static final int IGNORE_ITEM_VIEW_TYPE = AdapterView.ITEM_VIEW_TYPE_IGNORE;
private final AdapterBean AdapterBean;
public MyPageradapter() {
this(new AdapterBean());
}
MyPageradapter(AdapterBean AdapterBean) {
this.AdapterBean = AdapterBean;
AdapterBean.setViewTypeCount(getViewTypeCount());
}
@Override
public void notifyDataSetChanged() {
AdapterBean.scrapActiveViews();
super.notifyDataSetChanged();
}
@Override
public final Object instantiateItem(final ViewGroup container, final int position) {
int viewType = getItemViewType(position);
View view = null;
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
view = AdapterBean.getScrapView(position, viewType);
}
view = getView(position, view, container);
container.addView(view);
return view;
}
@Override
public final void destroyItem(ViewGroup container, int position, Object object) {
View view = (View) object;
container.removeView(view);
int viewType = getItemViewType(position);
if (viewType != IGNORE_ITEM_VIEW_TYPE) {
AdapterBean.addScrapView(view, position, viewType);
}
}
@Override
public final boolean isViewFromObject(View view, Object object) {
return view == object;
}
public int getViewTypeCount() {
return 1;
}
@SuppressWarnings("UnusedParameters") // Argument potentially used by subclasses.
public int getItemViewType(int position) {
return 0;
}
public abstract View getView(int position, View convertView, ViewGroup container);
}
-
以上是实现图片滑动的一些必须文件,下面给出Activity类,在此类中可以实现页面视觉的效果
public class MainActivity extends AppCompatActivity {
private GalleryViewPager mViewPager;
private SimpleAdapter mPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewPager = (GalleryViewPager) findViewById(R.id.viewpager);
mViewPager.setPageTransformer(true, new ScalePageTransformer());
findViewById(R.id.root).setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return mViewPager.dispatchTouchEvent(event);
}
});
mPagerAdapter = new SimpleAdapter(this);
mViewPager.setAdapter(mPagerAdapter);
initData();
}
private void initData() {
List<Integer> list = new ArrayList<>();
list.add(R.drawable.wo);
list.add(R.drawable.wo1);
list.add(R.drawable.wo2);
list.add(R.drawable.wo3);
list.add(R.drawable.wo4);
list.add(R.drawable.wo5);
list.add(R.drawable.wo);
list.add(R.drawable.wo1);
//设置OffscreenPageLimit
mViewPager.setOffscreenPageLimit(Math.min(list.size(), 5));
mPagerAdapter.addAll(list);
}
public class SimpleAdapter extends MyPageradapter {
private final List<Integer> mList;
private final Context mContext;
public SimpleAdapter(Context context) {
mList = new ArrayList<>();
mContext = context;
}
public void addAll(List<Integer> list) {
mList.addAll(list);
notifyDataSetChanged();
}
@Override
public View getView(final int position, View convertView, ViewGroup container) {
ImageView imageView = null;
if (convertView == null) {
imageView = new ImageView(mContext);
} else {
imageView = (ImageView) convertView;
}
imageView.setTag(position);
imageView.setImageResource(mList.get(position));
imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if ((mViewPager.getCurrentItem() ) == position) {
Toast.makeText(mContext, "点击的位置是:::"+position, Toast.LENGTH_SHORT).show();
}
}
});
return imageView;
}
@Override
public int getCount() {
return mList.size();
}
}
}
-
下面给出实现的一些资源文件
首先是drawable文件夹下的资源文件,此文件夹下给出一些图片资源(备注:如果需要网络图片资源,可以通过网络地址下载图片,在此grally中显示图片滑动效果)。在此不在给出这些图片了。
其次,需要在layout文件夹下给出布局文件activity_main.xml布局文件。
activity_main.xml布局文件
最后,以上是viewpager中实现图片滑动的一个简单的demo,有不足的地方希望大家指正批评