美文网首页Android DemoAndroid自定义Viewandroid
Android仿微信图片详情页面,可下拉关闭页面

Android仿微信图片详情页面,可下拉关闭页面

作者: JC_Hou | 来源:发表于2018-04-28 17:33 被阅读428次

    话不多说,直接上效果图

    imagereview.gif

    ImageReview
    在这个库里面用到了第三方的缩放图片查看库

    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;
                }
            });
        }
    }
    

    需要查看完整代码点击这里,喜欢的请给个star,谢谢

    相关文章

      网友评论

      • FlyClound:效果不太好,查看图片的界面状态栏被隐藏了,导致返回界面抖动.图片缩放不是太好
      • 你需要一台永动机:挺不错,使用了。感谢博主~
        JC_Hou:@你需要一台永动机 多谢多谢
        你需要一台永动机:@JC_Hou 已经给了,哈哈
        JC_Hou:@你需要一台永动机 喜欢能不能给github点个 star,谢谢

      本文标题:Android仿微信图片详情页面,可下拉关闭页面

      本文链接:https://www.haomeiwen.com/subject/sgoglftx.html