WebView+Fragment+ViewPager构建浏览器多标签页


效果图


源码地址:点击打开链接,觉得不错的请star一下,如果有bug还请指出。


自定义ViewPager用于解决滑动冲突

public class MyViewPager extends ViewPager implements OnGestureListener{
    private boolean isFullScreen=true;    //用于标识viewpager是否拦截事件,防止影响标签的左右滑动
    private OnLayoutClickListener lc;
    private GestureDetector gestureDetector;
    private boolean canDel=true;

    public MyViewPager(Context context) {
        this(context,null);
    }

    public MyViewPager(Context context, AttributeSet attrs) {
        super(context, attrs);
            gestureDetector=new GestureDetector(context,this);
        this.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) {
                //防止viewpager在滚动中item仍可以上下滑动
                canDel = state == SCROLL_STATE_IDLE;
            }
        });
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return !isFullScreen;
    }

    private FrameLayout frameLayout;
    protected float point_x, point_y; //手指按下的位置
    private int left, right, bottom;
    private int measureWidth,measureHeight;
    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            frameLayout=WebPage.webpagelist.get(getCurrentItem()).getInnerContainer();
            measureWidth=frameLayout.getMeasuredWidth();
            measureHeight=frameLayout.getMeasuredHeight();

            point_x = ev.getRawX();
            point_y = ev.getRawY();
            left = frameLayout.getLeft();
            right = frameLayout.getRight();
            bottom = frameLayout.getBottom();
        }
        if (ev.getAction() == MotionEvent.ACTION_MOVE){

            float mov_x = ev.getRawX() - point_x;
            float mov_y = ev.getRawY() - point_y;
            Log.d("trr","mov_y"+mov_y);
            if(Math.abs(mov_x) < Math.abs(mov_y)&&canDel){
                frameLayout.measure(MeasureSpec.makeMeasureSpec(measureWidth,MeasureSpec.AT_MOST),MeasureSpec.makeMeasureSpec(measureHeight,MeasureSpec.AT_MOST));
                frameLayout.layout(left, (int) mov_y, right, bottom + (int) mov_y);
            }

        }
        if (ev.getAction() == MotionEvent.ACTION_UP){
            Log.d("trr","frameLayout:"+frameLayout
            .getTop());
            if(Math.abs(frameLayout.getTop())>frameLayout.getWidth()/2){
                EventBus.getDefault().post(new MessageEvent(frameLayout.getTop()));

            }else {
                ObjectAnimator objectAnimator=ObjectAnimator.ofFloat(frameLayout,"translationY",frameLayout.getTop(),0);
                objectAnimator.setDuration(400).start();
                frameLayout.measure(MeasureSpec.makeMeasureSpec(measureWidth,MeasureSpec.AT_MOST),MeasureSpec.makeMeasureSpec(measureHeight,MeasureSpec.AT_MOST));
                frameLayout.layout(left,0,right,bottom);
            }
                
        }
        gestureDetector.onTouchEvent(ev);
        return super.onTouchEvent(ev);
    }


    public void setFullScreen(boolean fullScreen) {
        isFullScreen = fullScreen;
    }

    public void setOnLayoutClickListener(OnLayoutClickListener lc){
        this.lc=lc;
    }

    @Override
    public boolean onDown(MotionEvent e) {
        return true;
    }

    @Override
    public void onShowPress(MotionEvent e) {

    }

    @Override
    public boolean onSingleTapUp(MotionEvent e) {
        lc.onLayoutClick();   //手指点击屏幕不滑动时调用
        return true;
    }

    @Override
    public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

        return false;
    }

    @Override
    public void onLongPress(MotionEvent e) {

    }

    @Override
    public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {

        if(Math.abs(velocityY)>7000){
            EventBus.getDefault().post(new MessageEvent(frameLayout.getTop()));
            return true;
        }
        if(Math.abs(frameLayout.getTop())>frameLayout.getWidth()/2){
            EventBus.getDefault().post(new MessageEvent(frameLayout.getTop()));
        }else {
            frameLayout.layout(left,0,right,bottom);
        }
        return true;
    }
    public interface OnLayoutClickListener{
        void onLayoutClick();
    }

}

注意FragmentPagerAdapter和FragmentStatePagerAdapter的区别,由于要保持webview的状态,使用FragmentPagerAdapter比较好,但在删除标签页后要手动删除fragment,否则会导致内存泄露。调用notifyDataSetChanged后系统会调用destroyItem,在方法内执行fragment缓存删除。

public class WebPageAdapter extends FragmentPagerAdapter {
    private FragmentManager fm;
    public static final int ADD_PAGE=0;
    public static final int DELETE_PAGE=1;
    private int deleteItem=-1,notifyType=1;   //deleteItem记录要删除的标签
    public WebPageAdapter(FragmentManager fm) {
        super(fm);
        this.fm=fm;
    }

    @Override
    public WebViewFragment getItem(int position) {
        return WebPage.webpagelist.get(position);
    }

    @Override
    public long getItemId(int position) {
        return WebPage.webpagelist.get(position).hashCode();  //默认是直接返回position,会导致删除时标签页顺序错乱,所以要返回hash码
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;    //必须返回该值调用notifyDataSetChanged才有效
    }

    @Override
    public int getCount() {
        return WebPage.webpagelist.size();
    }
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        return super.instantiateItem(container,position);
    }
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        Log.d("WebView","调用了destroyItem");
        if(notifyType==1&&position==deleteItem){
            fm.beginTransaction().remove((Fragment) object).commit();   //将fragment缓存移除
            deleteItem=-1;
            return;
        }
        super.destroyItem(container, position, object);
    }

    public int getDeleteItem() {
        return deleteItem;
    }

    public void setDeleteItem(int deleteItem) {
        this.deleteItem = deleteItem;
    }

    public void notifyDataSetChanged(int type) {
        notifyType=type;
        super.notifyDataSetChanged();
    }

}


public class WebPage {
    public static List<WebViewFragment> webpagelist=new ArrayList<>(); //标签页集合
}

ViewPager配置

android:clipChildren="false"   //在布局文件中viewpager的节点加上该属性设置可以在缩小viewpager时绘制两边的页面

mViewPager.setPageMargin(40);    //页面间隔

mViewPager.animate().scaleX(0.65f).scaleY(0.65f).setDuration(400).start();   //缩小viewpager动画

webView.setLayerType(View.LAYER_TYPE_SOFTWARE,null);   //防止webview侧滑闪屏,真机不会,模拟器仍然会

以下方法可以防止在快速滑动页面时出现错位

private void fixWebPage(int position) {
        try {
            Field field = mViewPager.getClass().getField("mCurItem");
            field.setAccessible(true);
            field.setInt(mViewPager, position);
        } catch (Exception e) {
            e.printStackTrace();
        }
        webpageAdapter.notifyDataSetChanged();
        mViewPager.setCurrentItem(position);
    }

删除页面

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {
        //删除动画
        int viewTop = event.getViewTop();   //当手指上滑到可以删除页面时页面顶端的纵坐标
        int value;
        if (viewTop > 0) {
            value = 2500;
        } else {
            value = -2500;
        }
        View selectedView = WebPage.webpagelist.get(mViewPager.getCurrentItem()).getInnerContainer();
        Animation animation = new TranslateAnimation(0, 0, viewTop, value);
        animation.setDuration(400);
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {

            }

            @Override
            public void onAnimationEnd(Animation animation) {
                WebPage.webpagelist.remove(mViewPager.getCurrentItem());
                webpageAdapter.setDeleteItem(mViewPager.getCurrentItem());
                webpageAdapter.notifyDataSetChanged(WebPageAdapter.DELETE_PAGE);
                if (WebPage.webpagelist.size() == 0) {
                    first = true;
                    WebViewFragment fragment = new WebViewFragment(initWebView());
                    WebPage.webpagelist.add(fragment);
                    webpageAdapter.notifyDataSetChanged(WebPageAdapter.ADD_PAGE);
                    ZoomChange(1);
                }
            }

            @Override
            public void onAnimationRepeat(Animation animation) {

            }
        });
        selectedView.startAnimation(animation);

    }

注:标签页的删除用到了依赖库EventBus。

文章只列出部分代码,完整源码请点击上文链接

猜你喜欢

转载自blog.csdn.net/weixin_40855673/article/details/78591620