本文主要实现的功能是制作一个照片查看器,点击照片能进入大图模式,支持左右滑动,支持手势缩放,

本文只演示1张图和3张图的效果,其他的效果分为为:

2张图的时候按照1行2列展示,

3张图的时候按照1行3列展示,

4张图的时候按照2行2列展示,

这样依次排列,

首先上能实现这个功能的控件,自定义的imglayout;

代码如下

package com.demo.swt.mystudyappshop.Wight;import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Build;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.ImageView;
import android.widget.RelativeLayout;import com.demo.swt.mystudyappshop.R;
import com.demo.swt.mystudyappshop.Util.DisplayUtils;
import com.squareup.picasso.Picasso;import java.util.ArrayList;
import java.util.List;/*** Created by pc on 2016/12/6.*/public class CstImage extends RelativeLayout {private final int MAX_NUM = 9;private final int SPECIFIC_NUM_1 = 1;private final int SPECIFIC_NUM_2 = 2;private final int SPECIFIC_NUM_4 = 4;private int rowNum;private int margin;private int leftMargin, rightMargin, topMargin, bottomMargin;private ScaleAnimation downAnim;private ScaleAnimation upAnim;private Handler handler = new Handler();private OnChlidItemClickListener onChlidItemClickListener;private boolean isFour; //是否4张private int imgMargin;private int imgMarginDp = 1;private float onRatio = 0.67f;private float twoRatio = 1f;private float fourRatio = 0.67f;private float otherRation = 1f;private int MAX_WIDTH;//图片下方是否需要添加介绍private boolean isNeedIntro;private int oneWidth, twoWidth, fourWidth, otherWidth;public CstImage(Context context) {super(context);init();}public CstImage(Context context, AttributeSet attrs) {super(context, attrs);TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CstImgLayout);margin = a.getDimensionPixelSize(R.styleable.CstImgLayout_all_margin, 0);final int dp2px15 = dip2px(15);if (margin > 0) {leftMargin = rightMargin = bottomMargin = topMargin = margin;} else {isNeedIntro = a.getBoolean(R.styleable.CstImgLayout_need_intro, false);leftMargin = a.getDimensionPixelSize(R.styleable.CstImgLayout_left_margin, dp2px15);rightMargin = a.getDimensionPixelSize(R.styleable.CstImgLayout_right_margin, dp2px15);bottomMargin = a.getDimensionPixelSize(R.styleable.CstImgLayout_bottom_margin, dp2px15);topMargin = a.getDimensionPixelSize(R.styleable.CstImgLayout_top_margin, dp2px15);}rowNum = a.getInteger(R.styleable.CstImgLayout_row_num, 3);realWidth = a.getDimensionPixelOffset(R.styleable.CstImgLayout_real_width, 0);a.recycle();init();}public CstImage(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}@TargetApi(Build.VERSION_CODES.LOLLIPOP)public CstImage(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);init();}private int dip2px(float dpValue) {final float scale = getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}private int realWidth;private void init() {int sw = realWidth <= 0 ? getResources().getDisplayMetrics().widthPixels : realWidth;imgMargin = DisplayUtils.dip2px(imgMarginDp);MAX_WIDTH = oneWidth = sw - leftMargin - rightMargin;twoWidth = (sw - leftMargin - rightMargin - imgMargin) / 2;fourWidth = twoWidth;otherWidth = (sw - leftMargin - rightMargin - 2 * imgMargin) / 3;initView();downAnim = new ScaleAnimation(1f, 0.98f, 1f, 0.98f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);upAnim = new ScaleAnimation(0.98f, 1f, 0.98f, 1f, Animation.RELATIVE_TO_SELF, 0.5f, Animation.RELATIVE_TO_SELF, 0.5f);downAnim.setFillAfter(true);downAnim.setDuration(100);downAnim.setInterpolator(new AccelerateInterpolator());upAnim.setFillAfter(true);upAnim.setDuration(100);upAnim.setInterpolator(new AccelerateInterpolator());}private void initView() {if (isNeedIntro) {for (int i = 0; i < MAX_NUM; i++) {RelativeLayout layout = new RelativeLayout(getContext());layout.setVisibility(GONE);ImageView imageView = new ImageView(getContext());imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);imageView.setId(i);RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);layout.addView(imageView, lp);addView(layout);}} else {for (int i = 0; i < MAX_NUM; i++) {ImageView imageView = new ImageView(getContext());imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);imageView.setVisibility(GONE);imageView.setId(i);addView(imageView);}}}public void setOnChildItemClickListener(OnChlidItemClickListener l) {onChlidItemClickListener = l;}public interface OnChlidItemClickListener {void onClick(int position, List<String> imgs);}public void setImgs(List<String> imgs) {if (null != imgs && !imgs.isEmpty()) {if (imgs.size() > MAX_NUM) {imgs = new ArrayList<>(imgs.subList(0, MAX_NUM));}setVisibility(View.VISIBLE);int size = imgs.size();if (size == SPECIFIC_NUM_1) {setOne(imgs);} else if (size == SPECIFIC_NUM_2) {setTwo(imgs);} else if (size == SPECIFIC_NUM_4) {setFour(imgs);} else {setOther(imgs);}} else {setVisibility(View.GONE);}requestLayout();}//一张图private void setOne(List<String> imgs) {for (int i = 0; i < MAX_NUM; i++) {if (0 == i) {getChildAt(i).setVisibility(VISIBLE);} else {getChildAt(i).setVisibility(GONE);}}RelativeLayout.LayoutParams lp = (LayoutParams) getChildAt(0).getLayoutParams();lp.width = oneWidth;lp.height = (int) (lp.width * onRatio);lp.rightMargin = 0;setImg(imgs, 0, getChildAt(0));}//两张图private void setTwo(List<String> imgs) {for (int i = 0; i < MAX_NUM; i++) {if (0 == i || 1 == i) {getChildAt(i).setVisibility(VISIBLE);RelativeLayout.LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();lp.width = twoWidth;lp.height = (int) (lp.width * twoRatio);if (1 == i) {lp.leftMargin = imgMargin + twoWidth;}setImg(imgs, i, getChildAt(i));} else {getChildAt(i).setVisibility(GONE);}}}//四张图private void setFour(List<String> imgs) {isFour = true;for (int i = 0; i < MAX_NUM; i++) {if (0 == i || 1 == i || 3 == i || 4 == i) {getChildAt(i).setVisibility(VISIBLE);RelativeLayout.LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();lp.width = fourWidth;lp.height = (int) (fourWidth * fourRatio);lp.rightMargin = imgMargin;int topId = i - rowNum;if (topId >= 0) {lp.leftMargin = 0;lp.topMargin = (imgMargin + lp.height) * (i / rowNum);lp.leftMargin = (fourWidth + imgMargin) * (i % rowNum);} else if (i > 0) {int leftId = getChildAt(i - 1).getId();lp.leftMargin = (fourWidth + imgMargin) * (i % rowNum);lp.addRule(RelativeLayout.ALIGN_TOP, leftId);lp.addRule(RelativeLayout.ALIGN_BOTTOM, leftId);}int position = i;if (position > 1) {position--;}setImg(imgs, position, getChildAt(i));} else {getChildAt(i).setVisibility(GONE);}}}//其他private void setOther(List<String> imgs) {int size = imgs.size();for (int i = 0; i < MAX_NUM; i++) {if (i < size) {getChildAt(i).setVisibility(VISIBLE);RelativeLayout.LayoutParams lp = (LayoutParams) getChildAt(i).getLayoutParams();lp.width = otherWidth;lp.height = (int) (otherWidth * otherRation);if (i % rowNum == (rowNum - 1)) {lp.rightMargin = 0;} else {lp.rightMargin = imgMargin;}int topId = i - rowNum;if (topId >= 0) {lp.leftMargin = 0;lp.topMargin = (imgMargin + lp.height) * (i / rowNum);lp.leftMargin = (otherWidth + imgMargin) * (i % rowNum);} else if (i > 0) {int leftId = getChildAt(i - 1).getId();lp.leftMargin = (otherWidth + imgMargin) * (i % rowNum);lp.addRule(RelativeLayout.ALIGN_TOP, leftId);lp.addRule(RelativeLayout.ALIGN_BOTTOM, leftId);}setImg(imgs, i, getChildAt(i));} else {getChildAt(i).setVisibility(GONE);}}}private void setImg(final List<String> imgs, final int position, final View view) {ImageView imageView = null;ViewGroup viewGroup;if (!isNeedIntro && view instanceof ImageView) {imageView = (ImageView) view;} else if (view instanceof ViewGroup && (viewGroup = (ViewGroup) view).getChildCount() > 0 && viewGroup.getChildAt(0) instanceof ImageView) {imageView = (ImageView) viewGroup.getChildAt(0);}final ImageView img = imageView;if (null != img) {Picasso.with(getContext()).load(imgs.get(position)).into(img);//add by zhangxutong end ,feature :按尺寸加载图片img.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {view.startAnimation(downAnim);handler.postDelayed(new Runnable() {@Overridepublic void run() {view.startAnimation(upAnim);}}, 100);view.postDelayed(new Runnable() {@Overridepublic void run() {view.clearAnimation();if (null != onChlidItemClickListener) {onChlidItemClickListener.onClick(position, imgs);}}}, 120);}});}}}

会调有几个地方报错,一个一个来,首先在valus目录下新建一个attrs文件,代码如下

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="CstImgLayout"><attr name="bottom_margin" format="dimension" /><attr name="left_margin" format="dimension" /><attr name="right_margin" format="dimension" /><attr name="top_margin" format="dimension" /><attr name="all_margin" format="dimension" /><attr name="child_margin" format="dimension" /><attr name="row_num" format="integer" /><attr name="need_intro" format="boolean" /><attr name="real_width" format="dimension" /></declare-styleable>
</resources>

然后加一个小工具类

代码如下

package com.demo.swt.mystudyappshop.Util;import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.content.ContextCompat;public class DisplayUtils {private static Context context;public static void setContext(Context Context) {DisplayUtils.context = Context;}/*** 根据手机的分辨率从 dp 的单位 转成为 px(像素)*/public static int dip2px(float dpValue) {if (null == context) return (int) dpValue;final float scale = context.getResources().getDisplayMetrics().density;return (int) (dpValue * scale + 0.5f);}/*** 根据手机的分辨率从 px(像素) 的单位 转成为 dp*/public static int px2dip(float pxValue) {if (null == context) return (int) pxValue;final float scale = context.getResources().getDisplayMetrics().density;return (int) (pxValue / scale + 0.5f);}/*** sp转px(正常字体下,1sp=1dp)** @param spValue* @return*/public static int sp2px(float spValue) {if (null == context) return (int) spValue;final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;return (int) (spValue * fontScale + 0.5f);}/*** sp转dp** @param spValue* @return*/public static int sp2dp(float spValue) {int sp2Px = sp2px(spValue);return px2dip(sp2Px);}public static int getQToPx(int rid) {return (int) context.getResources().getDimension(rid);}public static Drawable getDrawable(int drawable) {return ContextCompat.getDrawable(context, drawable);}
}

最后还有一个错误,因为这个控件用的是pisso加载图片的所以在gradle里面配置上pisso

compile 'com.squareup.picasso:picasso:2.3.2'

同步后,就没错误了,自定义的view就弄好了,我们这样使用

在xml文件中直接加上

 <com.anlaiye.swt.bigphoto.CstImageandroid:id="@+id/cstimage"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_marginLeft="30dp"android:layout_marginRight="20dp"android:layout_marginTop="10dp"></com.anlaiye.swt.bigphoto.CstImage>

这个控件,然后findviewbyid找到控件,

通过

 image= (CstImage) findViewById(R.id.cstimage);image.setImgs();

通过setimgs来设置图片,里面的参数是list<string>

一般这个list<string>后台会返回给我们,我们这里只是一个demo就不写网络请求框架了 ,我们直接自己放一个进去

我们初始化一个数组,和一个网络图片地址,把这个地址放入mlist中,然后在放置在setimgs(mlist)中,

 private String img = "http://pic.anlaiye.com.cn/16b840dfab614965acb07562801fceee_748x750.png";private List<String> mlist = new ArrayList<>();

这里只有一张图片 所以看到的是一张图片的效果

因为要方便左右滑动,想了一下还是放俩涨图片吧,

然后加上点击事件,所以完整代码如下

package com.anlaiye.swt.bigphoto;import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.widget.ImageView;
import android.widget.Toast;import java.util.ArrayList;
import java.util.List;public class MainActivity extends FragmentActivity {private String img = "http://pic.anlaiye.com.cn/6a143da5964749ab990343091ce3a0be_799x1066.png";private String img1 = "http://pic.anlaiye.com.cn/6a143da5964749ab990343091ce3a0be_799x1066.png";private List<String> mlist = new ArrayList<>();private CstImage image;private ImageView imageView;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);image = (CstImage) findViewById(R.id.cstimage);mlist.add(img);mlist.add(img1);image.setImgs(mlist);image.setOnChildItemClickListener(new CstImage.OnChlidItemClickListener() {@Overridepublic void onClick(int position, List<String> imgs) {Toast.makeText(getApplicationContext(), position + "被点击了",Toast.LENGTH_SHORT).show();}});}
}

运行看下效果 (不要忘记加网络权限,因为是加载网络图片)

<uses-permission android:name="android.permission.INTERNET" />

现在的这个是俩张图的效果,其他的效果,小伙伴可以自行尝试,

好了,接下来制作大图模式,首先分析思路是点击进入另外一个activity或者(fragment)都可以,然后使用一个viewpager铺满屏幕,里面放置一个imageview,好了大概的思路有了开始写:

首先写一个新的activity:

接下来我直接把注释写代码里面,直接贴代码了,(突然发现写的在详细,也没人看,还是简单点,直接拿代码好了)

package com.anlaiye.swt.bigphoto;import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.FragmentActivity;
import android.support.v4.view.PagerAdapter;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;import java.util.ArrayList;
import java.util.List;import uk.co.senab.photoview.PhotoViewAttacher;/*** Created by pc on 2016/12/5.*/public class BigImageActivity extends FragmentActivity {private ArrayList<String> tulist = new ArrayList<>();private NoPreloadViewPager noPreloadViewPager;private int pos;private List<MyPhotoView> mSimplelist = new ArrayList<>();@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);requestWindowFeature(Window.FEATURE_NO_TITLE);setContentView(R.layout.bigimagee);noPreloadViewPager = (NoPreloadViewPager) findViewById(R.id.nopreload);Intent intent = getIntent();Bundle bundle = intent.getExtras();tulist = bundle.getStringArrayList("tulist");pos = bundle.getInt("pos");getSimpleList();noPreloadViewPager.setAdapter(new PagerAdapter() {@Overridepublic int getCount() {return tulist.size();}@Overridepublic boolean isViewFromObject(View view, Object object) {return view == object;}@Overridepublic void destroyItem(ViewGroup container, int position, Object object) {container.removeView(mSimplelist.get(position));}@Overridepublic Object instantiateItem(ViewGroup container, final int position) {container.addView(mSimplelist.get(position));return mSimplelist.get(position);}});noPreloadViewPager.setCurrentItem(pos);}protected List<MyPhotoView> getSimpleList() {for (int i = 0; i < tulist.size(); i++) {MyPhotoView photoDraweeView = new MyPhotoView(getApplicationContext());photoDraweeView.setImageUri(tulist.get(i));mSimplelist.add(photoDraweeView);photoDraweeView.setOnPhotoTapListener(new PhotoViewAttacher.OnPhotoTapListener() {@Overridepublic void onPhotoTap(View view, float x, float y) {finish();}});}return mSimplelist;}}

可以看到我们在这个界面接收了上一个界面传过来的list<string> ,所以我们的MainActivity的最终代码是,把list<string>传过去

package com.anlaiye.swt.bigphoto;import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;import java.util.ArrayList;
import java.util.List;public class MainActivity extends FragmentActivity {private String img = "http://pic.anlaiye.com.cn/6a143da5964749ab990343091ce3a0be_799x1066.png";private String img1 = "http://pic.anlaiye.com.cn/7e6f898d8ed14d00a7b2b844c7dccfe0_799x1422.png";private List<String> mlist = new ArrayList<>();private CstImage image;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);image = (CstImage) findViewById(R.id.cstimage);mlist.add(img);mlist.add(img1);image.setImgs(mlist);image.setOnChildItemClickListener(new CstImage.OnChlidItemClickListener() {@Overridepublic void onClick(int position, List<String> imgs) {Intent intent = new Intent(MainActivity.this, BigImageActivity.class);Bundle bundle = new Bundle();bundle.putStringArrayList("tulist", (ArrayList<String>) mlist);bundle.putInt("pos", position);intent.putExtras(bundle);startActivity(intent);}});}
}

同时BigImageActivity还会有几个报错,

几个报错,一个一个的修补一下

NoPreloadViewPager 这是一个viewpager不过他不会预加载,为什么要这样呢,因为我们viewpager里面是根据position设置图片的,如果预加载,翻到最后一张就会出现空指针,闪退

下面是不会预加载的viewpager的源码,只修改了

private static final int DEFAULT_OFFSCREEN_PAGES = 0;//默认是1

其他的没动,直接copy就好了

package com.anlaiye.swt.bigphoto;import android.content.Context;
import android.database.DataSetObserver;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.support.v4.os.ParcelableCompat;
import android.support.v4.os.ParcelableCompatCreatorCallbacks;
import android.support.v4.view.KeyEventCompat;
import android.support.v4.view.MotionEventCompat;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.VelocityTrackerCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewConfigurationCompat;
import android.support.v4.widget.EdgeEffectCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.FocusFinder;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.SoundEffectConstants;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Interpolator;
import android.widget.Scroller;import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;/*** Created by pc on 2016/12/6.*/public class NoPreloadViewPager extends ViewGroup {private static final String TAG = "NoPreloadViewPager";private static final boolean DEBUG = false;private static final boolean USE_CACHE = false;private static final int DEFAULT_OFFSCREEN_PAGES = 0;//默认是1private static final int MAX_SETTLE_DURATION = 600; // msstatic class ItemInfo {Object object;int position;boolean scrolling;}private static final Comparator<ItemInfo> COMPARATOR = new Comparator<ItemInfo>() {@Overridepublic int compare(ItemInfo lhs, ItemInfo rhs) {return lhs.position - rhs.position;}};private static final Interpolator sInterpolator = new Interpolator() {public float getInterpolation(float t) {// _o(t) = t * t * ((tension + 1) * t + tension)// o(t) = _o(t - 1) + 1t -= 1.0f;return t * t * t + 1.0f;}};private final ArrayList<ItemInfo> mItems = new ArrayList<ItemInfo>();private PagerAdapter mAdapter;private int mCurItem;   // Index of currently displayed page.private int mRestoredCurItem = -1;private Parcelable mRestoredAdapterState = null;private ClassLoader mRestoredClassLoader = null;private Scroller mScroller;private PagerObserver mObserver;private int mPageMargin;private Drawable mMarginDrawable;private int mChildWidthMeasureSpec;private int mChildHeightMeasureSpec;private boolean mInLayout;private boolean mScrollingCacheEnabled;private boolean mPopulatePending;private boolean mScrolling;private int mOffscreenPageLimit = DEFAULT_OFFSCREEN_PAGES;private boolean mIsBeingDragged;private boolean mIsUnableToDrag;private int mTouchSlop;private float mInitialMotionX;/*** Position of the last motion event.*/private float mLastMotionX;private float mLastMotionY;/*** ID of the active pointer. This is used to retain consistency during* drags/flings if multiple pointers are used.*/private int mActivePointerId = INVALID_POINTER;/*** Sentinel value for no current active pointer.* Used by {@link #mActivePointerId}.*/private static final int INVALID_POINTER = -1;/*** Determines speed during touch scrolling*/private VelocityTracker mVelocityTracker;private int mMinimumVelocity;private int mMaximumVelocity;private float mBaseLineFlingVelocity;private float mFlingVelocityInfluence;private boolean mFakeDragging;private long mFakeDragBeginTime;private EdgeEffectCompat mLeftEdge;private EdgeEffectCompat mRightEdge;private boolean mFirstLayout = true;private OnPageChangeListener mOnPageChangeListener;/*** Indicates that the pager is in an idle, settled state. The current page* is fully in view and no animation is in progress.*/public static final int SCROLL_STATE_IDLE = 0;/*** Indicates that the pager is currently being dragged by the user.*/public static final int SCROLL_STATE_DRAGGING = 1;/*** Indicates that the pager is in the process of settling to a final position.*/public static final int SCROLL_STATE_SETTLING = 2;private int mScrollState = SCROLL_STATE_IDLE;/*** Callback interface for responding to changing state of the selected page.*/public interface OnPageChangeListener {/*** This method will be invoked when the current page is scrolled, either as part* of a programmatically initiated smooth scroll or a user initiated touch scroll.** @param position             Position index of the first page currently being displayed.*                             Page position+1 will be visible if positionOffset is nonzero.* @param positionOffset       Value from [0, 1) indicating the offset from the page at position.* @param positionOffsetPixels Value in pixels indicating the offset from position.*/public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels);/*** This method will be invoked when a new page becomes selected. Animation is not* necessarily complete.** @param position Position index of the new selected page.*/public void onPageSelected(int position);/*** Called when the scroll state changes. Useful for discovering when the user* begins dragging, when the pager is automatically settling to the current page,* or when it is fully stopped/idle.** @param state The new scroll state.* @see android.support.v4.view.ViewPager#SCROLL_STATE_IDLE* @see android.support.v4.view.ViewPager#SCROLL_STATE_DRAGGING* @see android.support.v4.view.ViewPager#SCROLL_STATE_SETTLING*/public void onPageScrollStateChanged(int state);}/*** Simple implementation of the {@link android.support.v4.view.LazyViewPager.OnPageChangeListener} interface with stub* implementations of each method. Extend this if you do not intend to override* every method of {@link android.support.v4.view.LazyViewPager.OnPageChangeListener}.*/public static class SimpleOnPageChangeListener implements OnPageChangeListener {@Overridepublic void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {// This space for rent}@Overridepublic void onPageSelected(int position) {// This space for rent}@Overridepublic void onPageScrollStateChanged(int state) {// This space for rent}}public NoPreloadViewPager(Context context) {super(context);initViewPager();}public NoPreloadViewPager(Context context, AttributeSet attrs) {super(context, attrs);initViewPager();}void initViewPager() {setWillNotDraw(false);setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);setFocusable(true);final Context context = getContext();mScroller = new Scroller(context, sInterpolator);final ViewConfiguration configuration = ViewConfiguration.get(context);mTouchSlop = ViewConfigurationCompat.getScaledPagingTouchSlop(configuration);mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();mLeftEdge = new EdgeEffectCompat(context);mRightEdge = new EdgeEffectCompat(context);float density = context.getResources().getDisplayMetrics().density;mBaseLineFlingVelocity = 2500.0f * density;mFlingVelocityInfluence = 0.4f;}private void setScrollState(int newState) {if (mScrollState == newState) {return;}mScrollState = newState;if (mOnPageChangeListener != null) {mOnPageChangeListener.onPageScrollStateChanged(newState);}}public void setAdapter(PagerAdapter adapter) {if (mAdapter != null) {
//            mAdapter.unregisterDataSetObserver(mObserver);mAdapter.startUpdate(this);for (int i = 0; i < mItems.size(); i++) {final ItemInfo ii = mItems.get(i);mAdapter.destroyItem(this, ii.position, ii.object);}mAdapter.finishUpdate(this);mItems.clear();removeAllViews();mCurItem = 0;scrollTo(0, 0);}mAdapter = adapter;if (mAdapter != null) {if (mObserver == null) {mObserver = new PagerObserver();}
//            mAdapter.registerDataSetObserver(mObserver);mPopulatePending = false;if (mRestoredCurItem >= 0) {mAdapter.restoreState(mRestoredAdapterState, mRestoredClassLoader);setCurrentItemInternal(mRestoredCurItem, false, true);mRestoredCurItem = -1;mRestoredAdapterState = null;mRestoredClassLoader = null;} else {populate();}}}public PagerAdapter getAdapter() {return mAdapter;}/*** Set the currently selected page. If the ViewPager has already been through its first* layout there will be a smooth animated transition between the current item and the* specified item.** @param item Item index to select*/public void setCurrentItem(int item) {mPopulatePending = false;setCurrentItemInternal(item, !mFirstLayout, false);}/*** Set the currently selected page.** @param item         Item index to select* @param smoothScroll True to smoothly scroll to the new item, false to transition immediately*/public void setCurrentItem(int item, boolean smoothScroll) {mPopulatePending = false;setCurrentItemInternal(item, smoothScroll, false);}public int getCurrentItem() {return mCurItem;}void setCurrentItemInternal(int item, boolean smoothScroll, boolean always) {setCurrentItemInternal(item, smoothScroll, always, 0);}void setCurrentItemInternal(int item, boolean smoothScroll, boolean always, int velocity) {if (mAdapter == null || mAdapter.getCount() <= 0) {setScrollingCacheEnabled(false);return;}if (!always && mCurItem == item && mItems.size() != 0) {setScrollingCacheEnabled(false);return;}if (item < 0) {item = 0;} else if (item >= mAdapter.getCount()) {item = mAdapter.getCount() - 1;}final int pageLimit = mOffscreenPageLimit;if (item > (mCurItem + pageLimit) || item < (mCurItem - pageLimit)) {// We are doing a jump by more than one page.  To avoid// glitches, we want to keep all current pages in the view// until the scroll ends.for (int i = 0; i < mItems.size(); i++) {mItems.get(i).scrolling = true;}}final boolean dispatchSelected = mCurItem != item;mCurItem = item;populate();final int destX = (getWidth() + mPageMargin) * item;if (smoothScroll) {smoothScrollTo(destX, 0, velocity);if (dispatchSelected && mOnPageChangeListener != null) {mOnPageChangeListener.onPageSelected(item);}} else {if (dispatchSelected && mOnPageChangeListener != null) {mOnPageChangeListener.onPageSelected(item);}completeScroll();scrollTo(destX, 0);}}public void setOnPageChangeListener(OnPageChangeListener listener) {mOnPageChangeListener = listener;}/*** Returns the number of pages that will be retained to either side of the* current page in the view hierarchy in an idle state. Defaults to 1.** @return How many pages will be kept offscreen on either side* @see #setOffscreenPageLimit(int)*/public int getOffscreenPageLimit() {return mOffscreenPageLimit;}/*** Set the number of pages that should be retained to either side of the* current page in the view hierarchy in an idle state. Pages beyond this* limit will be recreated from the adapter when needed.* <p>* <p>This is offered as an optimization. If you know in advance the number* of pages you will need to support or have lazy-loading mechanisms in place* on your pages, tweaking this setting can have benefits in perceived smoothness* of paging animations and interaction. If you have a small number of pages (3-4)* that you can keep active all at once, less time will be spent in layout for* newly created view subtrees as the user pages back and forth.</p>* <p>* <p>You should keep this limit low, especially if your pages have complex layouts.* This setting defaults to 1.</p>** @param limit How many pages will be kept offscreen in an idle state.*/public void setOffscreenPageLimit(int limit) {if (limit < DEFAULT_OFFSCREEN_PAGES) {Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +DEFAULT_OFFSCREEN_PAGES);limit = DEFAULT_OFFSCREEN_PAGES;}if (limit != mOffscreenPageLimit) {mOffscreenPageLimit = limit;populate();}}/*** Set the margin between pages.** @param marginPixels Distance between adjacent pages in pixels* @see #getPageMargin()* @see #setPageMarginDrawable(android.graphics.drawable.Drawable)* @see #setPageMarginDrawable(int)*/public void setPageMargin(int marginPixels) {final int oldMargin = mPageMargin;mPageMargin = marginPixels;final int width = getWidth();recomputeScrollPosition(width, width, marginPixels, oldMargin);requestLayout();}/*** Return the margin between pages.** @return The size of the margin in pixels*/public int getPageMargin() {return mPageMargin;}/*** Set a drawable that will be used to fill the margin between pages.** @param d Drawable to display between pages*/public void setPageMarginDrawable(Drawable d) {mMarginDrawable = d;if (d != null) refreshDrawableState();setWillNotDraw(d == null);invalidate();}/*** Set a drawable that will be used to fill the margin between pages.** @param resId Resource ID of a drawable to display between pages*/public void setPageMarginDrawable(int resId) {setPageMarginDrawable(getContext().getResources().getDrawable(resId));}@Overrideprotected boolean verifyDrawable(Drawable who) {return super.verifyDrawable(who) || who == mMarginDrawable;}@Overrideprotected void drawableStateChanged() {super.drawableStateChanged();final Drawable d = mMarginDrawable;if (d != null && d.isStateful()) {d.setState(getDrawableState());}}// We want the duration of the page snap animation to be influenced by the distance that// the screen has to travel, however, we don't want this duration to be effected in a// purely linear fashion. Instead, we use this method to moderate the effect that the distance// of travel has on the overall snap duration.float distanceInfluenceForSnapDuration(float f) {f -= 0.5f; // center the values about 0.f *= 0.3f * Math.PI / 2.0f;return (float) Math.sin(f);}/*** Like {@link android.view.View#scrollBy}, but scroll smoothly instead of immediately.** @param x the number of pixels to scroll by on the X axis* @param y the number of pixels to scroll by on the Y axis*/void smoothScrollTo(int x, int y) {smoothScrollTo(x, y, 0);}/*** Like {@link android.view.View#scrollBy}, but scroll smoothly instead of immediately.** @param x        the number of pixels to scroll by on the X axis* @param y        the number of pixels to scroll by on the Y axis* @param velocity the velocity associated with a fling, if applicable. (0 otherwise)*/void smoothScrollTo(int x, int y, int velocity) {if (getChildCount() == 0) {// Nothing to do.setScrollingCacheEnabled(false);return;}int sx = getScrollX();int sy = getScrollY();int dx = x - sx;int dy = y - sy;if (dx == 0 && dy == 0) {completeScroll();setScrollState(SCROLL_STATE_IDLE);return;}setScrollingCacheEnabled(true);mScrolling = true;setScrollState(SCROLL_STATE_SETTLING);final float pageDelta = (float) Math.abs(dx) / (getWidth() + mPageMargin);int duration = (int) (pageDelta * 100);velocity = Math.abs(velocity);if (velocity > 0) {duration += (duration / (velocity / mBaseLineFlingVelocity)) * mFlingVelocityInfluence;} else {duration += 100;}duration = Math.min(duration, MAX_SETTLE_DURATION);mScroller.startScroll(sx, sy, dx, dy, duration);invalidate();}void addNewItem(int position, int index) {ItemInfo ii = new ItemInfo();ii.position = position;ii.object = mAdapter.instantiateItem(this, position);if (index < 0) {mItems.add(ii);} else {mItems.add(index, ii);}}void dataSetChanged() {// This method only gets called if our observer is attached, so mAdapter is non-null.boolean needPopulate = mItems.size() < 3 && mItems.size() < mAdapter.getCount();int newCurrItem = -1;for (int i = 0; i < mItems.size(); i++) {final ItemInfo ii = mItems.get(i);final int newPos = mAdapter.getItemPosition(ii.object);if (newPos == PagerAdapter.POSITION_UNCHANGED) {continue;}if (newPos == PagerAdapter.POSITION_NONE) {mItems.remove(i);i--;mAdapter.destroyItem(this, ii.position, ii.object);needPopulate = true;if (mCurItem == ii.position) {// Keep the current item in the valid rangenewCurrItem = Math.max(0, Math.min(mCurItem, mAdapter.getCount() - 1));}continue;}if (ii.position != newPos) {if (ii.position == mCurItem) {// Our current item changed position. Follow it.newCurrItem = newPos;}ii.position = newPos;needPopulate = true;}}Collections.sort(mItems, COMPARATOR);if (newCurrItem >= 0) {// TODO This currently causes a jump.setCurrentItemInternal(newCurrItem, false, true);needPopulate = true;}if (needPopulate) {populate();requestLayout();}}void populate() {if (mAdapter == null) {return;}// Bail now if we are waiting to populate.  This is to hold off// on creating views from the time the user releases their finger to// fling to a new position until we have finished the scroll to// that position, avoiding glitches from happening at that point.if (mPopulatePending) {if (DEBUG) Log.i(TAG, "populate is pending, skipping for now...");return;}// Also, don't populate until we are attached to a window.  This is to// avoid trying to populate before we have restored our view hierarchy// state and conflicting with what is restored.if (getWindowToken() == null) {return;}mAdapter.startUpdate(this);final int pageLimit = mOffscreenPageLimit;final int startPos = Math.max(0, mCurItem - pageLimit);final int N = mAdapter.getCount();final int endPos = Math.min(N - 1, mCurItem + pageLimit);if (DEBUG) Log.v(TAG, "populating: startPos=" + startPos + " endPos=" + endPos);// Add and remove pages in the existing list.int lastPos = -1;for (int i = 0; i < mItems.size(); i++) {ItemInfo ii = mItems.get(i);if ((ii.position < startPos || ii.position > endPos) && !ii.scrolling) {if (DEBUG) Log.i(TAG, "removing: " + ii.position + " @ " + i);mItems.remove(i);i--;mAdapter.destroyItem(this, ii.position, ii.object);} else if (lastPos < endPos && ii.position > startPos) {// The next item is outside of our range, but we have a gap// between it and the last item where we want to have a page// shown.  Fill in the gap.lastPos++;if (lastPos < startPos) {lastPos = startPos;}while (lastPos <= endPos && lastPos < ii.position) {if (DEBUG) Log.i(TAG, "inserting: " + lastPos + " @ " + i);addNewItem(lastPos, i);lastPos++;i++;}}lastPos = ii.position;}// Add any new pages we need at the end.lastPos = mItems.size() > 0 ? mItems.get(mItems.size() - 1).position : -1;if (lastPos < endPos) {lastPos++;lastPos = lastPos > startPos ? lastPos : startPos;while (lastPos <= endPos) {if (DEBUG) Log.i(TAG, "appending: " + lastPos);addNewItem(lastPos, -1);lastPos++;}}if (DEBUG) {Log.i(TAG, "Current page list:");for (int i = 0; i < mItems.size(); i++) {Log.i(TAG, "#" + i + ": page " + mItems.get(i).position);}}ItemInfo curItem = null;for (int i = 0; i < mItems.size(); i++) {if (mItems.get(i).position == mCurItem) {curItem = mItems.get(i);break;}}mAdapter.setPrimaryItem(this, mCurItem, curItem != null ? curItem.object : null);mAdapter.finishUpdate(this);if (hasFocus()) {View currentFocused = findFocus();ItemInfo ii = currentFocused != null ? infoForAnyChild(currentFocused) : null;if (ii == null || ii.position != mCurItem) {for (int i = 0; i < getChildCount(); i++) {View child = getChildAt(i);ii = infoForChild(child);if (ii != null && ii.position == mCurItem) {if (child.requestFocus(FOCUS_FORWARD)) {break;}}}}}}public static class SavedState extends BaseSavedState {int position;Parcelable adapterState;ClassLoader loader;public SavedState(Parcelable superState) {super(superState);}@Overridepublic void writeToParcel(Parcel out, int flags) {super.writeToParcel(out, flags);out.writeInt(position);out.writeParcelable(adapterState, flags);}@Overridepublic String toString() {return "FragmentPager.SavedState{"+ Integer.toHexString(System.identityHashCode(this))+ " position=" + position + "}";}public static final Creator<SavedState> CREATOR= ParcelableCompat.newCreator(new ParcelableCompatCreatorCallbacks<SavedState>() {@Overridepublic SavedState createFromParcel(Parcel in, ClassLoader loader) {return new SavedState(in, loader);}@Overridepublic SavedState[] newArray(int size) {return new SavedState[size];}});SavedState(Parcel in, ClassLoader loader) {super(in);if (loader == null) {loader = getClass().getClassLoader();}position = in.readInt();adapterState = in.readParcelable(loader);this.loader = loader;}}@Overridepublic Parcelable onSaveInstanceState() {Parcelable superState = super.onSaveInstanceState();SavedState ss = new SavedState(superState);ss.position = mCurItem;if (mAdapter != null) {ss.adapterState = mAdapter.saveState();}return ss;}@Overridepublic void onRestoreInstanceState(Parcelable state) {if (!(state instanceof SavedState)) {super.onRestoreInstanceState(state);return;}SavedState ss = (SavedState) state;super.onRestoreInstanceState(ss.getSuperState());if (mAdapter != null) {mAdapter.restoreState(ss.adapterState, ss.loader);setCurrentItemInternal(ss.position, false, true);} else {mRestoredCurItem = ss.position;mRestoredAdapterState = ss.adapterState;mRestoredClassLoader = ss.loader;}}@Overridepublic void addView(View child, int index, LayoutParams params) {if (mInLayout) {addViewInLayout(child, index, params);child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec);} else {super.addView(child, index, params);}if (USE_CACHE) {if (child.getVisibility() != GONE) {child.setDrawingCacheEnabled(mScrollingCacheEnabled);} else {child.setDrawingCacheEnabled(false);}}}ItemInfo infoForChild(View child) {for (int i = 0; i < mItems.size(); i++) {ItemInfo ii = mItems.get(i);if (mAdapter.isViewFromObject(child, ii.object)) {return ii;}}return null;}ItemInfo infoForAnyChild(View child) {ViewParent parent;while ((parent = child.getParent()) != this) {if (parent == null || !(parent instanceof View)) {return null;}child = (View) parent;}return infoForChild(child);}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();mFirstLayout = true;}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {// For simple implementation, or internal size is always 0.// We depend on the container to specify the layout size of// our view.  We can't really know what it is since we will be// adding and removing different arbitrary views and do not// want the layout to change as this happens.setMeasuredDimension(getDefaultSize(0, widthMeasureSpec),getDefaultSize(0, heightMeasureSpec));// Children are just made to fill our space.mChildWidthMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredWidth() -getPaddingLeft() - getPaddingRight(), MeasureSpec.EXACTLY);mChildHeightMeasureSpec = MeasureSpec.makeMeasureSpec(getMeasuredHeight() -getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY);// Make sure we have created all fragments that we need to have shown.mInLayout = true;populate();mInLayout = false;// Make sure all children have been properly measured.final int size = getChildCount();for (int i = 0; i < size; ++i) {final View child = getChildAt(i);if (child.getVisibility() != GONE) {if (DEBUG) Log.v(TAG, "Measuring #" + i + " " + child+ ": " + mChildWidthMeasureSpec);child.measure(mChildWidthMeasureSpec, mChildHeightMeasureSpec);}}}@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {super.onSizeChanged(w, h, oldw, oldh);// Make sure scroll position is set correctly.if (w != oldw) {recomputeScrollPosition(w, oldw, mPageMargin, mPageMargin);}}private void recomputeScrollPosition(int width, int oldWidth, int margin, int oldMargin) {final int widthWithMargin = width + margin;if (oldWidth > 0) {final int oldScrollPos = getScrollX();final int oldwwm = oldWidth + oldMargin;final int oldScrollItem = oldScrollPos / oldwwm;final float scrollOffset = (float) (oldScrollPos % oldwwm) / oldwwm;final int scrollPos = (int) ((oldScrollItem + scrollOffset) * widthWithMargin);scrollTo(scrollPos, getScrollY());if (!mScroller.isFinished()) {// We now return to your regularly scheduled scroll, already in progress.final int newDuration = mScroller.getDuration() - mScroller.timePassed();mScroller.startScroll(scrollPos, 0, mCurItem * widthWithMargin, 0, newDuration);}} else {int scrollPos = mCurItem * widthWithMargin;if (scrollPos != getScrollX()) {completeScroll();scrollTo(scrollPos, getScrollY());}}}@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {mInLayout = true;populate();mInLayout = false;final int count = getChildCount();final int width = r - l;for (int i = 0; i < count; i++) {View child = getChildAt(i);ItemInfo ii;if (child.getVisibility() != GONE && (ii = infoForChild(child)) != null) {int loff = (width + mPageMargin) * ii.position;int childLeft = getPaddingLeft() + loff;int childTop = getPaddingTop();if (DEBUG) Log.v(TAG, "Positioning #" + i + " " + child + " f=" + ii.object+ ":" + childLeft + "," + childTop + " " + child.getMeasuredWidth()+ "x" + child.getMeasuredHeight());child.layout(childLeft, childTop,childLeft + child.getMeasuredWidth(),childTop + child.getMeasuredHeight());}}mFirstLayout = false;}@Overridepublic void computeScroll() {if (DEBUG) Log.i(TAG, "computeScroll: finished=" + mScroller.isFinished());if (!mScroller.isFinished()) {if (mScroller.computeScrollOffset()) {if (DEBUG) Log.i(TAG, "computeScroll: still scrolling");int oldX = getScrollX();int oldY = getScrollY();int x = mScroller.getCurrX();int y = mScroller.getCurrY();if (oldX != x || oldY != y) {scrollTo(x, y);}if (mOnPageChangeListener != null) {final int widthWithMargin = getWidth() + mPageMargin;final int position = x / widthWithMargin;final int offsetPixels = x % widthWithMargin;final float offset = (float) offsetPixels / widthWithMargin;mOnPageChangeListener.onPageScrolled(position, offset, offsetPixels);}// Keep on drawing until the animation has finished.invalidate();return;}}// Done with scroll, clean up state.completeScroll();}private void completeScroll() {boolean needPopulate = mScrolling;if (needPopulate) {// Done with scroll, no longer want to cache view drawing.setScrollingCacheEnabled(false);mScroller.abortAnimation();int oldX = getScrollX();int oldY = getScrollY();int x = mScroller.getCurrX();int y = mScroller.getCurrY();if (oldX != x || oldY != y) {scrollTo(x, y);}setScrollState(SCROLL_STATE_IDLE);}mPopulatePending = false;mScrolling = false;for (int i = 0; i < mItems.size(); i++) {ItemInfo ii = mItems.get(i);if (ii.scrolling) {needPopulate = true;ii.scrolling = false;}}if (needPopulate) {populate();}}@Overridepublic boolean onInterceptTouchEvent(MotionEvent ev) {/** This method JUST determines whether we want to intercept the motion.* If we return true, onMotionEvent will be called and we do the actual* scrolling there.*/final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;// Always take care of the touch gesture being complete.if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {// Release the drag.if (DEBUG) Log.v(TAG, "Intercept done!");mIsBeingDragged = false;mIsUnableToDrag = false;mActivePointerId = INVALID_POINTER;return false;}// Nothing more to do here if we have decided whether or not we// are dragging.if (action != MotionEvent.ACTION_DOWN) {if (mIsBeingDragged) {if (DEBUG) Log.v(TAG, "Intercept returning true!");return true;}if (mIsUnableToDrag) {if (DEBUG) Log.v(TAG, "Intercept returning false!");return false;}}switch (action) {case MotionEvent.ACTION_MOVE: {/** mIsBeingDragged == false, otherwise the shortcut would have caught it. Check* whether the user has moved far enough from his original down touch.*//** Locally do absolute value. mLastMotionY is set to the y value* of the down event.*/final int activePointerId = mActivePointerId;if (activePointerId == INVALID_POINTER) {// If we don't have a valid id, the touch down wasn't on content.break;}final int pointerIndex = MotionEventCompat.findPointerIndex(ev, activePointerId);final float x = MotionEventCompat.getX(ev, pointerIndex);final float dx = x - mLastMotionX;final float xDiff = Math.abs(dx);final float y = MotionEventCompat.getY(ev, pointerIndex);final float yDiff = Math.abs(y - mLastMotionY);final int scrollX = getScrollX();final boolean atEdge = (dx > 0 && scrollX == 0) || (dx < 0 && mAdapter != null &&scrollX >= (mAdapter.getCount() - 1) * getWidth() - 1);if (DEBUG) Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);if (canScroll(this, false, (int) dx, (int) x, (int) y)) {// Nested view has scrollable area under this point. Let it be handled there.mInitialMotionX = mLastMotionX = x;mLastMotionY = y;return false;}if (xDiff > mTouchSlop && xDiff > yDiff) {if (DEBUG) Log.v(TAG, "Starting drag!");mIsBeingDragged = true;setScrollState(SCROLL_STATE_DRAGGING);mLastMotionX = x;setScrollingCacheEnabled(true);} else {if (yDiff > mTouchSlop) {// The finger has moved enough in the vertical// direction to be counted as a drag...  abort// any attempt to drag horizontally, to work correctly// with children that have scrolling containers.if (DEBUG) Log.v(TAG, "Starting unable to drag!");mIsUnableToDrag = true;}}break;}case MotionEvent.ACTION_DOWN: {/** Remember location of down touch.* ACTION_DOWN always refers to pointer index 0.*/mLastMotionX = mInitialMotionX = ev.getX();mLastMotionY = ev.getY();mActivePointerId = MotionEventCompat.getPointerId(ev, 0);if (mScrollState == SCROLL_STATE_SETTLING) {// Let the user 'catch' the pager as it animates.mIsBeingDragged = true;mIsUnableToDrag = false;setScrollState(SCROLL_STATE_DRAGGING);} else {completeScroll();mIsBeingDragged = false;mIsUnableToDrag = false;}if (DEBUG) Log.v(TAG, "Down at " + mLastMotionX + "," + mLastMotionY+ " mIsBeingDragged=" + mIsBeingDragged+ "mIsUnableToDrag=" + mIsUnableToDrag);break;}case MotionEventCompat.ACTION_POINTER_UP:onSecondaryPointerUp(ev);break;}/** The only time we want to intercept motion events is if we are in the* drag mode.*/return mIsBeingDragged;}@Overridepublic boolean onTouchEvent(MotionEvent ev) {if (mFakeDragging) {// A fake drag is in progress already, ignore this real one// but still eat the touch events.// (It is likely that the user is multi-touching the screen.)return true;}if (ev.getAction() == MotionEvent.ACTION_DOWN && ev.getEdgeFlags() != 0) {// Don't handle edge touches immediately -- they may actually belong to one of our// descendants.return false;}if (mAdapter == null || mAdapter.getCount() == 0) {// Nothing to present or scroll; nothing to touch.return false;}if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();}mVelocityTracker.addMovement(ev);final int action = ev.getAction();boolean needsInvalidate = false;switch (action & MotionEventCompat.ACTION_MASK) {case MotionEvent.ACTION_DOWN: {/** If being flinged and user touches, stop the fling. isFinished* will be false if being flinged.*/completeScroll();// Remember where the motion event startedmLastMotionX = mInitialMotionX = ev.getX();mActivePointerId = MotionEventCompat.getPointerId(ev, 0);break;}case MotionEvent.ACTION_MOVE:if (!mIsBeingDragged) {final int pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);final float x = MotionEventCompat.getX(ev, pointerIndex);final float xDiff = Math.abs(x - mLastMotionX);final float y = MotionEventCompat.getY(ev, pointerIndex);final float yDiff = Math.abs(y - mLastMotionY);if (DEBUG)Log.v(TAG, "Moved x to " + x + "," + y + " diff=" + xDiff + "," + yDiff);if (xDiff > mTouchSlop && xDiff > yDiff) {if (DEBUG) Log.v(TAG, "Starting drag!");mIsBeingDragged = true;mLastMotionX = x;setScrollState(SCROLL_STATE_DRAGGING);setScrollingCacheEnabled(true);}}if (mIsBeingDragged) {// Scroll to follow the motion eventfinal int activePointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId);final float x = MotionEventCompat.getX(ev, activePointerIndex);final float deltaX = mLastMotionX - x;mLastMotionX = x;float oldScrollX = getScrollX();float scrollX = oldScrollX + deltaX;final int width = getWidth();final int widthWithMargin = width + mPageMargin;final int lastItemIndex = mAdapter.getCount() - 1;final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin);final float rightBound =Math.min(mCurItem + 1, lastItemIndex) * widthWithMargin;if (scrollX < leftBound) {if (leftBound == 0) {float over = -scrollX;needsInvalidate = mLeftEdge.onPull(over / width);}scrollX = leftBound;} else if (scrollX > rightBound) {if (rightBound == lastItemIndex * widthWithMargin) {float over = scrollX - rightBound;needsInvalidate = mRightEdge.onPull(over / width);}scrollX = rightBound;}// Don't lose the rounded componentmLastMotionX += scrollX - (int) scrollX;scrollTo((int) scrollX, getScrollY());if (mOnPageChangeListener != null) {final int position = (int) scrollX / widthWithMargin;final int positionOffsetPixels = (int) scrollX % widthWithMargin;final float positionOffset = (float) positionOffsetPixels / widthWithMargin;mOnPageChangeListener.onPageScrolled(position, positionOffset,positionOffsetPixels);}}break;case MotionEvent.ACTION_UP:if (mIsBeingDragged) {final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(velocityTracker, mActivePointerId);mPopulatePending = true;final int widthWithMargin = getWidth() + mPageMargin;final int scrollX = getScrollX();final int currentPage = scrollX / widthWithMargin;int nextPage = initialVelocity > 0 ? currentPage : currentPage + 1;setCurrentItemInternal(nextPage, true, true, initialVelocity);mActivePointerId = INVALID_POINTER;endDrag();needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();}break;case MotionEvent.ACTION_CANCEL:if (mIsBeingDragged) {setCurrentItemInternal(mCurItem, true, true);mActivePointerId = INVALID_POINTER;endDrag();needsInvalidate = mLeftEdge.onRelease() | mRightEdge.onRelease();}break;case MotionEventCompat.ACTION_POINTER_DOWN: {final int index = MotionEventCompat.getActionIndex(ev);final float x = MotionEventCompat.getX(ev, index);mLastMotionX = x;mActivePointerId = MotionEventCompat.getPointerId(ev, index);break;}case MotionEventCompat.ACTION_POINTER_UP:onSecondaryPointerUp(ev);mLastMotionX = MotionEventCompat.getX(ev,MotionEventCompat.findPointerIndex(ev, mActivePointerId));break;}if (needsInvalidate) {invalidate();}return true;}@Overridepublic void draw(Canvas canvas) {super.draw(canvas);boolean needsInvalidate = false;final int overScrollMode = ViewCompat.getOverScrollMode(this);if (overScrollMode == ViewCompat.OVER_SCROLL_ALWAYS ||(overScrollMode == ViewCompat.OVER_SCROLL_IF_CONTENT_SCROLLS &&mAdapter != null && mAdapter.getCount() > 1)) {if (!mLeftEdge.isFinished()) {final int restoreCount = canvas.save();final int height = getHeight() - getPaddingTop() - getPaddingBottom();canvas.rotate(270);canvas.translate(-height + getPaddingTop(), 0);mLeftEdge.setSize(height, getWidth());needsInvalidate |= mLeftEdge.draw(canvas);canvas.restoreToCount(restoreCount);}if (!mRightEdge.isFinished()) {final int restoreCount = canvas.save();final int width = getWidth();final int height = getHeight() - getPaddingTop() - getPaddingBottom();final int itemCount = mAdapter != null ? mAdapter.getCount() : 1;canvas.rotate(90);canvas.translate(-getPaddingTop(),-itemCount * (width + mPageMargin) + mPageMargin);mRightEdge.setSize(height, width);needsInvalidate |= mRightEdge.draw(canvas);canvas.restoreToCount(restoreCount);}} else {mLeftEdge.finish();mRightEdge.finish();}if (needsInvalidate) {// Keep animatinginvalidate();}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);// Draw the margin drawable if needed.if (mPageMargin > 0 && mMarginDrawable != null) {final int scrollX = getScrollX();final int width = getWidth();final int offset = scrollX % (width + mPageMargin);if (offset != 0) {// Pages fit completely when settled; we only need to draw when in betweenfinal int left = scrollX - offset + width;mMarginDrawable.setBounds(left, 0, left + mPageMargin, getHeight());mMarginDrawable.draw(canvas);}}}/*** Start a fake drag of the pager.* <p>* <p>A fake drag can be useful if you want to synchronize the motion of the ViewPager* with the touch scrolling of another view, while still letting the ViewPager* control the snapping motion and fling behavior. (e.g. parallax-scrolling tabs.)* Call {@link #fakeDragBy(float)} to simulate the actual drag motion. Call* {@link #endFakeDrag()} to complete the fake drag and fling as necessary.* <p>* <p>During a fake drag the ViewPager will ignore all touch events. If a real drag* is already in progress, this method will return false.** @return true if the fake drag began successfully, false if it could not be started.* @see #fakeDragBy(float)* @see #endFakeDrag()*/public boolean beginFakeDrag() {if (mIsBeingDragged) {return false;}mFakeDragging = true;setScrollState(SCROLL_STATE_DRAGGING);mInitialMotionX = mLastMotionX = 0;if (mVelocityTracker == null) {mVelocityTracker = VelocityTracker.obtain();} else {mVelocityTracker.clear();}final long time = SystemClock.uptimeMillis();final MotionEvent ev = MotionEvent.obtain(time, time, MotionEvent.ACTION_DOWN, 0, 0, 0);mVelocityTracker.addMovement(ev);ev.recycle();mFakeDragBeginTime = time;return true;}/*** End a fake drag of the pager.** @see #beginFakeDrag()* @see #fakeDragBy(float)*/public void endFakeDrag() {if (!mFakeDragging) {throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");}final VelocityTracker velocityTracker = mVelocityTracker;velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker, mActivePointerId);mPopulatePending = true;if ((Math.abs(initialVelocity) > mMinimumVelocity)|| Math.abs(mInitialMotionX - mLastMotionX) >= (getWidth() / 3)) {if (mLastMotionX > mInitialMotionX) {setCurrentItemInternal(mCurItem - 1, true, true);} else {setCurrentItemInternal(mCurItem + 1, true, true);}} else {setCurrentItemInternal(mCurItem, true, true);}endDrag();mFakeDragging = false;}/*** Fake drag by an offset in pixels. You must have called {@link #beginFakeDrag()} first.** @param xOffset Offset in pixels to drag by.* @see #beginFakeDrag()* @see #endFakeDrag()*/public void fakeDragBy(float xOffset) {if (!mFakeDragging) {throw new IllegalStateException("No fake drag in progress. Call beginFakeDrag first.");}mLastMotionX += xOffset;float scrollX = getScrollX() - xOffset;final int width = getWidth();final int widthWithMargin = width + mPageMargin;final float leftBound = Math.max(0, (mCurItem - 1) * widthWithMargin);final float rightBound =Math.min(mCurItem + 1, mAdapter.getCount() - 1) * widthWithMargin;if (scrollX < leftBound) {scrollX = leftBound;} else if (scrollX > rightBound) {scrollX = rightBound;}// Don't lose the rounded componentmLastMotionX += scrollX - (int) scrollX;scrollTo((int) scrollX, getScrollY());if (mOnPageChangeListener != null) {final int position = (int) scrollX / widthWithMargin;final int positionOffsetPixels = (int) scrollX % widthWithMargin;final float positionOffset = (float) positionOffsetPixels / widthWithMargin;mOnPageChangeListener.onPageScrolled(position, positionOffset,positionOffsetPixels);}// Synthesize an event for the VelocityTracker.final long time = SystemClock.uptimeMillis();final MotionEvent ev = MotionEvent.obtain(mFakeDragBeginTime, time, MotionEvent.ACTION_MOVE,mLastMotionX, 0, 0);mVelocityTracker.addMovement(ev);ev.recycle();}/*** Returns true if a fake drag is in progress.** @return true if currently in a fake drag, false otherwise.* @see #beginFakeDrag()* @see #fakeDragBy(float)* @see #endFakeDrag()*/public boolean isFakeDragging() {return mFakeDragging;}private void onSecondaryPointerUp(MotionEvent ev) {final int pointerIndex = MotionEventCompat.getActionIndex(ev);final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex);if (pointerId == mActivePointerId) {// This was our active pointer going up. Choose a new// active pointer and adjust accordingly.final int newPointerIndex = pointerIndex == 0 ? 1 : 0;mLastMotionX = MotionEventCompat.getX(ev, newPointerIndex);mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex);if (mVelocityTracker != null) {mVelocityTracker.clear();}}}private void endDrag() {mIsBeingDragged = false;mIsUnableToDrag = false;if (mVelocityTracker != null) {mVelocityTracker.recycle();mVelocityTracker = null;}}private void setScrollingCacheEnabled(boolean enabled) {if (mScrollingCacheEnabled != enabled) {mScrollingCacheEnabled = enabled;if (USE_CACHE) {final int size = getChildCount();for (int i = 0; i < size; ++i) {final View child = getChildAt(i);if (child.getVisibility() != GONE) {child.setDrawingCacheEnabled(enabled);}}}}}/*** Tests scrollability within child views of v given a delta of dx.** @param v      View to test for horizontal scrollability* @param checkV Whether the view v passed should itself be checked for scrollability (true),*               or just its children (false).* @param dx     Delta scrolled in pixels* @param x      X coordinate of the active touch point* @param y      Y coordinate of the active touch point* @return true if child views of v can be scrolled by delta of dx.*/protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) {if (v instanceof ViewGroup) {final ViewGroup group = (ViewGroup) v;final int scrollX = v.getScrollX();final int scrollY = v.getScrollY();final int count = group.getChildCount();// Count backwards - let topmost views consume scroll distance first.for (int i = count - 1; i >= 0; i--) {// TODO: Add versioned support here for transformed views.// This will not work for transformed views in Honeycomb+final View child = group.getChildAt(i);if (x + scrollX >= child.getLeft() && x + scrollX < child.getRight() &&y + scrollY >= child.getTop() && y + scrollY < child.getBottom() &&canScroll(child, true, dx, x + scrollX - child.getLeft(),y + scrollY - child.getTop())) {return true;}}}return checkV && ViewCompat.canScrollHorizontally(v, -dx);}@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {// Let the focused view and/or our descendants get the key firstreturn super.dispatchKeyEvent(event) || executeKeyEvent(event);}/*** You can call this function yourself to have the scroll view perform* scrolling from a key event, just as if the event had been dispatched to* it by the view hierarchy.** @param event The key event to execute.* @return Return true if the event was handled, else false.*/public boolean executeKeyEvent(KeyEvent event) {boolean handled = false;if (event.getAction() == KeyEvent.ACTION_DOWN) {switch (event.getKeyCode()) {case KeyEvent.KEYCODE_DPAD_LEFT:handled = arrowScroll(FOCUS_LEFT);break;case KeyEvent.KEYCODE_DPAD_RIGHT:handled = arrowScroll(FOCUS_RIGHT);break;case KeyEvent.KEYCODE_TAB:if (KeyEventCompat.hasNoModifiers(event)) {handled = arrowScroll(FOCUS_FORWARD);} else if (KeyEventCompat.hasModifiers(event, KeyEvent.META_SHIFT_ON)) {handled = arrowScroll(FOCUS_BACKWARD);}break;}}return handled;}public boolean arrowScroll(int direction) {View currentFocused = findFocus();if (currentFocused == this) currentFocused = null;boolean handled = false;View nextFocused = FocusFinder.getInstance().findNextFocus(this, currentFocused,direction);if (nextFocused != null && nextFocused != currentFocused) {if (direction == View.FOCUS_LEFT) {// If there is nothing to the left, or this is causing us to// jump to the right, then what we really want to do is page left.if (currentFocused != null && nextFocused.getLeft() >= currentFocused.getLeft()) {handled = pageLeft();} else {handled = nextFocused.requestFocus();}} else if (direction == View.FOCUS_RIGHT) {// If there is nothing to the right, or this is causing us to// jump to the left, then what we really want to do is page right.if (currentFocused != null && nextFocused.getLeft() <= currentFocused.getLeft()) {handled = pageRight();} else {handled = nextFocused.requestFocus();}}} else if (direction == FOCUS_LEFT || direction == FOCUS_BACKWARD) {// Trying to move left and nothing there; try to page.handled = pageLeft();} else if (direction == FOCUS_RIGHT || direction == FOCUS_FORWARD) {// Trying to move right and nothing there; try to page.handled = pageRight();}if (handled) {playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));}return handled;}boolean pageLeft() {if (mCurItem > 0) {setCurrentItem(mCurItem - 1, true);return true;}return false;}boolean pageRight() {if (mAdapter != null && mCurItem < (mAdapter.getCount() - 1)) {setCurrentItem(mCurItem + 1, true);return true;}return false;}/*** We only want the current page that is being shown to be focusable.*/@Overridepublic void addFocusables(ArrayList<View> views, int direction, int focusableMode) {final int focusableCount = views.size();final int descendantFocusability = getDescendantFocusability();if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {for (int i = 0; i < getChildCount(); i++) {final View child = getChildAt(i);if (child.getVisibility() == VISIBLE) {ItemInfo ii = infoForChild(child);if (ii != null && ii.position == mCurItem) {child.addFocusables(views, direction, focusableMode);}}}}// we add ourselves (if focusable) in all cases except for when we are// FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is// to avoid the focus search finding layouts when a more precise search// among the focusable children would be more interesting.if (descendantFocusability != FOCUS_AFTER_DESCENDANTS ||// No focusable descendants(focusableCount == views.size())) {// Note that we can't call the superclass here, because it will// add all views in.  So we need to do the same thing View does.if (!isFocusable()) {return;}if ((focusableMode & FOCUSABLES_TOUCH_MODE) == FOCUSABLES_TOUCH_MODE &&isInTouchMode() && !isFocusableInTouchMode()) {return;}if (views != null) {views.add(this);}}}/*** We only want the current page that is being shown to be touchable.*/@Overridepublic void addTouchables(ArrayList<View> views) {// Note that we don't call super.addTouchables(), which means that// we don't call View.addTouchables().  This is okay because a ViewPager// is itself not touchable.for (int i = 0; i < getChildCount(); i++) {final View child = getChildAt(i);if (child.getVisibility() == VISIBLE) {ItemInfo ii = infoForChild(child);if (ii != null && ii.position == mCurItem) {child.addTouchables(views);}}}}/*** We only want the current page that is being shown to be focusable.*/@Overrideprotected boolean onRequestFocusInDescendants(int direction,Rect previouslyFocusedRect) {int index;int increment;int end;int count = getChildCount();if ((direction & FOCUS_FORWARD) != 0) {index = 0;increment = 1;end = count;} else {index = count - 1;increment = -1;end = -1;}for (int i = index; i != end; i += increment) {View child = getChildAt(i);if (child.getVisibility() == VISIBLE) {ItemInfo ii = infoForChild(child);if (ii != null && ii.position == mCurItem) {if (child.requestFocus(direction, previouslyFocusedRect)) {return true;}}}}return false;}@Overridepublic boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {// ViewPagers should only report accessibility info for the current page,// otherwise things get very confusing.// TODO: Should this note something about the paging container?final int childCount = getChildCount();for (int i = 0; i < childCount; i++) {final View child = getChildAt(i);if (child.getVisibility() == VISIBLE) {final ItemInfo ii = infoForChild(child);if (ii != null && ii.position == mCurItem &&child.dispatchPopulateAccessibilityEvent(event)) {return true;}}}return false;}private class PagerObserver extends DataSetObserver {@Overridepublic void onChanged() {dataSetChanged();}@Overridepublic void onInvalidated() {dataSetChanged();}}
}

myphotoview是支持手势的一个自定义的view,

想要研究这个源码的可以参考这个链接https://github.com/biezhihua/MySimpleDraweeView

代码

package com.anlaiye.swt.bigphoto;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.view.MotionEvent;import com.facebook.common.references.CloseableReference;
import com.facebook.datasource.DataSource;
import com.facebook.drawee.backends.pipeline.Fresco;
import com.facebook.drawee.controller.AbstractDraweeController;
import com.facebook.drawee.controller.BaseControllerListener;
import com.facebook.drawee.generic.GenericDraweeHierarchy;
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
import com.facebook.drawee.view.DraweeHolder;
import com.facebook.imagepipeline.core.ImagePipeline;
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.image.CloseableStaticBitmap;
import com.facebook.imagepipeline.image.ImageInfo;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;import uk.co.senab.photoview.PhotoView;/*** Created by pc on 2016/12/13.*/public class MyPhotoView extends PhotoView {protected DraweeHolder<GenericDraweeHierarchy> mDraweeHolder;public MyPhotoView(Context context) {this(context, null);}public MyPhotoView(Context context, AttributeSet attr) {this(context, attr, 0);}public MyPhotoView(Context context, AttributeSet attr, int defStyle) {super(context, attr, defStyle);selfInit();}private void selfInit() {if (mDraweeHolder == null) {final GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources()).setProgressBarImage(new LoadingProgressDrawable(getContext())).build();mDraweeHolder = DraweeHolder.create(hierarchy, getContext());}}@Overrideprotected void onDetachedFromWindow() {super.onDetachedFromWindow();mDraweeHolder.onDetach();}@Overrideprotected void onAttachedToWindow() {super.onAttachedToWindow();mDraweeHolder.onAttach();}@Overrideprotected boolean verifyDrawable(Drawable dr) {super.verifyDrawable(dr);return dr == mDraweeHolder.getHierarchy().getTopLevelDrawable();}@Overridepublic void onStartTemporaryDetach() {super.onStartTemporaryDetach();mDraweeHolder.onDetach();}@Overridepublic void onFinishTemporaryDetach() {super.onFinishTemporaryDetach();mDraweeHolder.onAttach();}@Overridepublic boolean onTouchEvent(MotionEvent event) {return mDraweeHolder.onTouchEvent(event) || super.onTouchEvent(event);}public void setImageUri(String uri) {final ImageRequest imageRequest = ImageRequestBuilder.newBuilderWithSource(Uri.parse(uri)).setAutoRotateEnabled(true).build();final ImagePipeline imagePipeline = Fresco.getImagePipeline();final DataSource<CloseableReference<CloseableImage>> dataSource = imagePipeline.fetchDecodedImage(imageRequest, this);final AbstractDraweeController controller = Fresco.newDraweeControllerBuilder().setOldController(mDraweeHolder.getController()).setImageRequest(imageRequest).setControllerListener(new BaseControllerListener<ImageInfo>() {@Overridepublic void onFinalImageSet(String id, ImageInfo imageInfo, Animatable animatable) {super.onFinalImageSet(id, imageInfo, animatable);CloseableReference<CloseableImage> imageCloseableReference = null;try {imageCloseableReference = dataSource.getResult();if (imageCloseableReference != null) {final CloseableImage image = imageCloseableReference.get();if (image != null && image instanceof CloseableStaticBitmap) {CloseableStaticBitmap closeableStaticBitmap = (CloseableStaticBitmap) image;final Bitmap bitmap = closeableStaticBitmap.getUnderlyingBitmap();if (bitmap != null) {setImageBitmap(bitmap);// 如果是长图,让其宽度放大至与屏幕等宽//setScaleType(ScaleType.CENTER_INSIDE);}}}} finally {dataSource.close();CloseableReference.closeSafely(imageCloseableReference);}}}).build();mDraweeHolder.setController(controller);setImageDrawable(mDraweeHolder.getTopLevelDrawable());}}

下面这个是一个loding的界面,在图片还没加载完成的时候会出现,

LoadingProgressDrawable

代码

package com.anlaiye.swt.bigphoto;import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.drawable.Drawable;public class LoadingProgressDrawable extends Drawable {private static final String TAG = "LoadingProgressDrawable";private static int[] Loadings = {R.mipmap.load_progress_1,R.mipmap.load_progress_3,R.mipmap.load_progress_4,R.mipmap.load_progress_6,R.mipmap.load_progress_7,R.mipmap.load_progress_8,R.mipmap.load_progress_9,R.mipmap.load_progress_10,R.mipmap.load_progress_11,R.mipmap.load_progress_12};private Paint mPaint;private int mLevel;private Context context;public LoadingProgressDrawable(Context context) {this.context = context;mPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);}BitmapFactory.Options options = new BitmapFactory.Options();@Overridepublic void draw(Canvas canvas) {Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), Loadings[getIndex()], options);int left = getBounds().right / 2 - options.outWidth / 2;int top = getBounds().bottom / 2 - options.outHeight / 2;canvas.drawBitmap(bitmap, left, top, mPaint);}private int getIndex() {int index = mLevel / 1000;if (index < 0) {index = 0;} else if (index >= Loadings.length) {index = Loadings.length - 1;}return index;}@Overridepublic void setAlpha(int alpha) {mPaint.setAlpha(alpha);}@Overridepublic void setColorFilter(ColorFilter cf) {mPaint.setColorFilter(cf);}@Overridepublic int getOpacity() {return Color.TRANSPARENT;}@Overrideprotected boolean onLevelChange(int level) {this.mLevel = level;this.invalidateSelf();return true;}
}

然后看下我们的最终效果图,缩放类型可以自己在MyPhotot的源码里面去修改,不过我觉得inside类型看起来是最舒服的,

下图就是最终的效果图,有其他的功能需要扩展的可以在上面继续扩展,文末有完整的源代码的链接,

直接用android studio打开就好了,下载相应的api 和tool就可以运行了,

完整代码在这里http://download.csdn.net/detail/qq_15527709/9711091

(我是小白,不要喷我)

制作一个简单的照片查看器(支持缩放手势)相关推荐

  1. 【QT学习】制作一个简单的图片查看器(完整源码)

  2. 使用Python制作一个简单的刷博器

    呵呵,不得不佩服Python的强大,寥寥几句代码就能做一个简单的刷博器. import webbrowser as web import time import oscount=0 while cou ...

  3. openGL ES进阶教程(五)制作一个简单的VR播放器,播放全景视频

    之前写过全景(VR)图片,和用openGL ES+MediaPlayer 渲染播放视频+滤镜效果 这一篇就在之前的基础上实现一个最简单的VR播放器,播放全景视频. 概述: 全景视频是一种用3D摄像机进 ...

  4. 微信小程序 用wx.getRecorderManager()和wx.createInnerAudioContext()制作一个简单的录音播放器(录制以及播放)

    在开发的时候公司提出了一个如题的要求,发现微信官方文档里好像没有相关的组件,就自己做了一个简单的,可以实现用户录制音频,播放和暂停的需求.放上来供大家参考 预览 wxml <view>&l ...

  5. Python搭建UDP网络通信模型,制作一个简单的私人聊天器~

    互联网的本质是什么? 其实就是信息的交换. 就比如我们常用的QQ.微信等. 那么如何将自己的信息发送到其他人的电脑上呢? 那就需要借助网络模型来完成这样的事情了. 今天就带领大家使用UDP网络模型来完 ...

  6. 制作一个简单的FLV播放器 【转】

    我们将制作的这个FLV播放器由这样几部分组成: 一个用于显示视频图像的视频对象, 三个分别用于播放.暂停和停止视频的按钮, 以及一个用于显示缓冲区装载进度的动态文本. 1. 创建三个按钮元件,分别命名 ...

  7. python制作一个简单的udp聊天器

    UDP协议 英文名:User Datagram Protocol 中文名:数据报协议 协议说明:UDP是一种面向无连接的传输层通信协议. 举例:发短信,不需要双方建立连接,数据报的大小应限制在64k以 ...

  8. 制作一个简单的音乐播放器

    舞台布置及所加的元件如下图 然后直接在放代码即可. //声明 var jzdz:URLRequest=new URLRequest("http://59.52.188.151/s2.mp3& ...

  9. 【J2SE】java实现简单照片查看器

    程序执行结果: project结构图: 程序代码: import java.awt.BorderLayout; import java.awt.FileDialog; import java.awt. ...

最新文章

  1. List和ObservableCollection的相互转化
  2. Exercise Physiology (运动生理学)
  3. 在linux下安装JDK
  4. 泡泡玛特,走出“盲盒”?
  5. .Net高级技术——对象序列化
  6. ArcGIS改变数据集或要素类的的坐标系(投影)
  7. 轻松弄懂var、let、const之间的区别(一看就懂)
  8. android涂鸦板保存功能,android实现涂鸦,保存涂鸦后的图片,清屏
  9. html怎么定位到不同的页面,html页面定位到指定位置的4种实现方式
  10. php object 对象不存在。增加对象_《相亲者女》:找一个匹配的对象,但永远不存在...
  11. c语言程序求点坐标在哪个象限,C课后习题
  12. Debian下配置iSCSI Target。
  13. 产品经理的书籍笔记(一)--------《神一样的产品经理》
  14. mysql commit用法_sql commit用法
  15. 搜索引擎(二)网页排名算法(1)PageRank
  16. 鸿蒙使用体验 2.0,鸿蒙的到来与华为的破局
  17. 我用python代码表白,泡到了大学小师妹
  18. opengl生成图片php,(转)使用OpenGL显示图像(七)Android OpenGLES2.0——纹理贴图之显示图片...
  19. Java HashSet和Java HashMap
  20. 千峰教育——网络管理

热门文章

  1. 【解释】Word2vec 词嵌入
  2. 职业规划之方法论【转自知乎】
  3. 君正全平台linux源码同步教程(除X1830人脸识别板)
  4. ShareSDK集成新浪微博
  5. jsp常见面试题及其知识点、EL、JSTL技术
  6. java字符串首字母变大写
  7. 强烈建议一个重新分区工具
  8. cookies之PHPSESSID
  9. TP-link在命令行模式下的一些基本设置
  10. matlab产生伪随机序列,伪随机序列发生器PRBS7的matlab实现