Android仿微信图片详情页面,可下拉关闭页面
效果图:
1、要实现效果图的方案,需要操作重写ViewPager的事件处理。
2、下滑 透明的方案可以使用设置背景来实现
下拉缩放的效果需要重写onInterceptTouchEvent和onTouchEvent, 在onInterceptTouchEvent中不拦截down事件,并且在move事件中判断是否满足下滑缩放的条件;
在onTouchEvent中实现具体的缩放以及透明度变化的效果;
同时在ViewPager的滑动要与下滑缩放区分开,因此需要监听OnPageChangeListener,并在onTouchEvent判断是否ViewPager在滑动中
public class DragViewPager extends ViewPager implements View.OnClickListener {
public static final int STATUS_NORMAL = 0;//正常浏览状态
public static final int STATUS_MOVING = 1;//滑动状态
public static final int STATUS_RESETTING = 2;//返回中状态
public static final String TAG = "DragViewPager";
public static final float MIN_SCALE_SIZE = 0.3f;//最小缩放比例
public static final int BACK_DURATION = 300;//ms
public static final int DRAG_GAP_PX = 50;
private int currentStatus = STATUS_NORMAL;
private int currentPageStatus;
private float mDownX;
private float mDownY;
private float screenHeight;
/**
* 要缩放的View
*/
private View currentShowView;
/**
* 滑动速度检测类
*/
private VelocityTracker mVelocityTracker;
private IAnimClose iAnimClose;
public void setIAnimClose(IAnimClose iAnimClose) {
this.iAnimClose = iAnimClose;
}
public DragViewPager(Context context) {
super(context);
init(context);
}
public DragViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public void init(Context context) {
screenHeight = ScreenUtils.getScreenHeight(context);
setBackgroundColor(Color.BLACK);
addOnPageChangeListener(new OnPageChangeListener() {
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
@Override
public void onPageSelected(int position) {
}
@Override
public void onPageScrollStateChanged(int state) {
currentPageStatus = state;
}
});
}
public void setCurrentShowView(View currentShowView) {
this.currentShowView = currentShowView;
if (this.currentShowView != null) {
this.currentShowView.setOnClickListener(this);
}
}
//配合SubsamplingScaleImageView使用,根据需要拦截ACTION_MOVE
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (getAdapter() instanceof ImagePagerAdapter) {
ImagePagerAdapter adapter = ((ImagePagerAdapter) getAdapter());
SubsamplingScaleImageView mImage = (SubsamplingScaleImageView) adapter.getItem(getCurrentItem()).getView().findViewById(R.id.image);
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
Log.e("jc","onInterceptTouchEvent:ACTION_DOWN");
mDownX = ev.getRawX();
mDownY = ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
Log.e("jc","onInterceptTouchEvent:ACTION_MOVE");
if (mImage.getCenter() != null && mImage.getCenter().y <= mImage.getHeight() / mImage.getScale() / 2) {
Log.e("jc","onInterceptTouchEvent:ACTION_MOVE");
int deltaX = Math.abs((int) (ev.getRawX() - mDownX));
int deltaY = (int) (ev.getRawY() - mDownY);
if (deltaY > DRAG_GAP_PX && deltaX <= DRAG_GAP_PX) {//往下移动超过临界,左右移动不超过临界时,拦截滑动事件
return true;
}
}
break;
case MotionEvent.ACTION_UP:
Log.e("jc","onInterceptTouchEvent:ACTION_UP");
break;
}
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (currentStatus == STATUS_RESETTING)
return false;
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_DOWN:
mDownX = ev.getRawX();
mDownY = ev.getRawY();
addIntoVelocity(ev);
break;
case MotionEvent.ACTION_MOVE:
addIntoVelocity(ev);
int deltaY = (int) (ev.getRawY() - mDownY);
//手指往上滑动
if (deltaY <= DRAG_GAP_PX && currentStatus != STATUS_MOVING)
return super.onTouchEvent(ev);
//viewpager不在切换中,并且手指往下滑动,开始缩放
if (currentPageStatus != SCROLL_STATE_DRAGGING && (deltaY > DRAG_GAP_PX || currentStatus == STATUS_MOVING)) {
moveView(ev.getRawX(), ev.getRawY());
return true;
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (currentStatus != STATUS_MOVING)
return super.onTouchEvent(ev);
final float mUpX = ev.getRawX();
final float mUpY = ev.getRawY();
float vY = computeYVelocity();//松开时必须释放VelocityTracker资源
if (vY >= 1200 || Math.abs(mUpY - mDownY) > screenHeight / 4) {
//下滑速度快,或者下滑距离超过屏幕高度的一半,就关闭
if (iAnimClose != null) {
iAnimClose.onPictureRelease(currentShowView);
}
} else {
resetReviewState(mUpX, mUpY);
}
break;
}
return super.onTouchEvent(ev);
}
//返回浏览状态
private void resetReviewState(final float mUpX, final float mUpY) {
currentStatus = STATUS_RESETTING;
if (mUpY != mDownY) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(mUpY, mDownY);
valueAnimator.setDuration(BACK_DURATION);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float mY = (float) animation.getAnimatedValue();
float percent = (mY - mDownY) / (mUpY - mDownY);
float mX = percent * (mUpX - mDownX) + mDownX;
moveView(mX, mY);
if (mY == mDownY) {
mDownY = 0;
mDownX = 0;
currentStatus = STATUS_NORMAL;
}
}
});
valueAnimator.start();
} else if (mUpX != mDownX) {
ValueAnimator valueAnimator = ValueAnimator.ofFloat(mUpX, mDownX);
valueAnimator.setDuration(BACK_DURATION);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float mX = (float) animation.getAnimatedValue();
float percent = (mX - mDownX) / (mUpX - mDownX);
float mY = percent * (mUpY - mDownY) + mDownY;
moveView(mX, mY);
if (mX == mDownX) {
mDownY = 0;
mDownX = 0;
currentStatus = STATUS_NORMAL;
}
}
});
valueAnimator.start();
} else if (iAnimClose != null)
iAnimClose.onPictureClick();
}
//移动View
private void moveView(float movingX, float movingY) {
if (currentShowView == null)
return;
currentStatus = STATUS_MOVING;
float deltaX = movingX - mDownX;
float deltaY = movingY - mDownY;
float scale = 1f;
float alphaPercent = 1f;
if (deltaY > 0) {
scale = 1 - Math.abs(deltaY) / screenHeight;
alphaPercent = 1 - Math.abs(deltaY) / (screenHeight / 2);
}
ViewHelper.setTranslationX(currentShowView, deltaX);
ViewHelper.setTranslationY(currentShowView, deltaY);
scaleView(scale);
setBackgroundColor(getBlackAlpha(alphaPercent));
}
//缩放View
private void scaleView(float scale) {
scale = Math.min(Math.max(scale, MIN_SCALE_SIZE), 1);
ViewHelper.setScaleX(currentShowView, scale);
ViewHelper.setScaleY(currentShowView, scale);
}
private int getBlackAlpha(float percent) {
percent = Math.min(1, Math.max(0, percent));
int intAlpha = (int) (percent * 255);
return Color.argb(intAlpha,0,0,0);
}
private void addIntoVelocity(MotionEvent event) {
if (mVelocityTracker == null)
mVelocityTracker = VelocityTracker.obtain();
mVelocityTracker.addMovement(event);
}
private float computeYVelocity() {
float result = 0;
if (mVelocityTracker != null) {
mVelocityTracker.computeCurrentVelocity(1000);
result = mVelocityTracker.getYVelocity();
releaseVelocity();
}
return result;
}
private void releaseVelocity() {
if (mVelocityTracker != null) {
mVelocityTracker.clear();
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
@Override
public void onClick(View v) {
if (iAnimClose != null) {
iAnimClose.onPictureClick();
}
}
public interface IAnimClose {
void onPictureClick();
void onPictureRelease(View view);
}
}
需要进行缩放的View我这里在Adapter中添加回调设置,并且adapter可以实现更新的效果
public class ImagePagerAdapter extends FragmentStatePagerAdapter {
private DragViewPager mPager;
private ArrayList<Fragment> mFragmentList;
public ImagePagerAdapter(FragmentManager fm, List<String> datas,DragViewPager pager) {
super(fm);
mPager=pager;
mPager.setAdapter(this);
updateData(datas);
}
public void updateData(List<String> dataList) {
ArrayList<Fragment> fragments = new ArrayList<>();
for (int i = 0, size = dataList.size(); i < size; i++) {
final ImageDetailFragment fragment = ImageDetailFragment.newInstance(dataList.get(i));
fragment.setOnImageListener(new ImageDetailFragment.OnImageListener() {
@Override
public void onInit() {
View view = fragment.getView();
mPager.setCurrentShowView(view);
}
});
fragments.add(fragment);
}
setViewList(fragments);
}
private void setViewList(ArrayList<Fragment> fragmentList) {
if (mFragmentList != null) {
mFragmentList.clear();
}
mFragmentList = fragmentList;
notifyDataSetChanged();
}
@Override
public int getCount() {
return mFragmentList==null?0:mFragmentList.size();
}
public int getItemPosition(Object object) {
return POSITION_NONE;
}
@Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
}
要实现透明度变化的效果还需要对activity设置theme
<style name="translucent" parent="AppTheme">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<item name="android:windowIsTranslucent">true</item>
<item name="android:windowBackground">@android:color/transparent</item>
</style>
<activity android:name=".ui.ImagePagerActivity"
android:theme="@style/translucent"/>
在需要使用的页面只需要调用即可
ImagePagerActivity.startImagePage(MainActivity.this,
urls,pos,recyclerView.getLayoutManager().findViewByPosition(pos));
在整个demo中采用的转场动画,需要设置共享元素,因此在需要使用的页面需要设置如下:
首先设置转场动画的共享元素,因为跳转和返回时都会调用onMapSharedElements,需要判断bundle是否为空,bundle会在返回的时候在onActivityReenter获取到
//设置转场动画的共享元素,因为跳转和返回时都会调用onMapSharedElements,需要判断bundle是否为空
setExitSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
if (bundle!=null){
int index = bundle.getInt(ImagePagerActivity.STATE_POSITION,0);
sharedElements.clear();
sharedElements.put("img", recyclerView.getLayoutManager().findViewByPosition(index));
bundle=null;
}
}
});
其次在返回的时候获取数据bundle
@Override
public void onActivityReenter(int resultCode, Intent data) {
super.onActivityReenter(resultCode, data);
bundle = data.getExtras();
int currentPosition = bundle.getInt(ImagePagerActivity.STATE_POSITION,0);
//做相应的滚动
recyclerView.scrollToPosition(currentPosition);
//暂时延迟 Transition 的使用,直到我们确定了共享元素的确切大小和位置才使用
//postponeEnterTransition后不要忘记调用startPostponedEnterTransition
ActivityCompat.postponeEnterTransition(this);
recyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
// TODO: figure out why it is necessary to request layout here in order to get a smooth transition.
recyclerView.requestLayout();
//共享元素准备好后调用startPostponedEnterTransition来恢复过渡效果
ActivityCompat.startPostponedEnterTransition(MainActivity.this);
return true;
}
});
}
完整的调用页面的代码如下:
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private MainAdapter mainAdapter;
//图片集合
private ArrayList<String> urls;
//存放返回时当前页码
private Bundle bundle;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
urls=new ArrayList<>();
//为了显示效果,重复添加了三次
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524477979306&di=3eb07e9302606048abe13d7b6a2bc601&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fitem%2F201406%2F12%2F20140612211118_YYXAC.jpeg");
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524133463580&di=1315bc4db30999f00b89ef79c3bb06e5&imgtype=0&src=http%3A%2F%2Fpic36.photophoto.cn%2F20150710%2F0005018721870517_b.jpg");
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524133463575&di=6221f21bcb761675c5d161ebc53d5948&imgtype=0&src=http%3A%2F%2Fimg5.duitang.com%2Fuploads%2Fitem%2F201410%2F03%2F20141003112442_AkkuH.thumb.700_0.jpeg");
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524477979306&di=3eb07e9302606048abe13d7b6a2bc601&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fitem%2F201406%2F12%2F20140612211118_YYXAC.jpeg");
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524133463580&di=1315bc4db30999f00b89ef79c3bb06e5&imgtype=0&src=http%3A%2F%2Fpic36.photophoto.cn%2F20150710%2F0005018721870517_b.jpg");
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524133463575&di=6221f21bcb761675c5d161ebc53d5948&imgtype=0&src=http%3A%2F%2Fimg5.duitang.com%2Fuploads%2Fitem%2F201410%2F03%2F20141003112442_AkkuH.thumb.700_0.jpeg");
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524477979306&di=3eb07e9302606048abe13d7b6a2bc601&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fitem%2F201406%2F12%2F20140612211118_YYXAC.jpeg");
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524133463580&di=1315bc4db30999f00b89ef79c3bb06e5&imgtype=0&src=http%3A%2F%2Fpic36.photophoto.cn%2F20150710%2F0005018721870517_b.jpg");
urls.add("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1524133463575&di=6221f21bcb761675c5d161ebc53d5948&imgtype=0&src=http%3A%2F%2Fimg5.duitang.com%2Fuploads%2Fitem%2F201410%2F03%2F20141003112442_AkkuH.thumb.700_0.jpeg");
initView();
}
private void initView() {
recyclerView = (RecyclerView) findViewById(R.id.recycler);
recyclerView.setLayoutManager(new GridLayoutManager(this, 2));
mainAdapter = new MainAdapter(urls);
mainAdapter.setOnItemClickListener(new MainAdapter.OnItemClickListener() {
@Override
public void onItemClick(int pos) {
ImagePagerActivity.startImagePage(MainActivity.this,
urls,pos,recyclerView.getLayoutManager().findViewByPosition(pos));
}
});
recyclerView.setAdapter(mainAdapter);
//设置转场动画的共享元素,因为跳转和返回都会调用,需要判断bundle是否为空
setExitSharedElementCallback(new SharedElementCallback() {
@Override
public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {
if (bundle!=null){
int index = bundle.getInt(ImagePagerActivity.STATE_POSITION,0);
sharedElements.clear();
sharedElements.put("img", recyclerView.getLayoutManager().findViewByPosition(index));
bundle=null;
}
}
});
}
//返回的时候获取数据
@Override
public void onActivityReenter(int resultCode, Intent data) {
super.onActivityReenter(resultCode, data);
bundle = data.getExtras();
int currentPosition = bundle.getInt(ImagePagerActivity.STATE_POSITION,0);
//做相应的滚动
recyclerView.scrollToPosition(currentPosition);
//暂时延迟 Transition 的使用,直到我们确定了共享元素的确切大小和位置才使用
//postponeEnterTransition后不要忘记调用startPostponedEnterTransition
ActivityCompat.postponeEnterTransition(this);
recyclerView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
// TODO: figure out why it is necessary to request layout here in order to get a smooth transition.
recyclerView.requestLayout();
//共享元素准备好后调用startPostponedEnterTransition来恢复过渡效果
ActivityCompat.startPostponedEnterTransition(MainActivity.this);
return true;
}
});
}
}
源码地址:
https://github.com/JohnsonHou/ImageReview
到这里就结束啦。
评论