• 高级UI晋升之常用View(三)中篇


    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680
    本篇文章将从ViewPager来介绍常用View:
    文章目录

    一、简介

    二、基本使用

    1. xml引用
    2. page布局
    3. 创建适配器
    4. 设置适配器
    5. 标题栏
      5.1. xml引用
      5.2. 重写PagerAdapter的getTitle()方法
    6. 翻页动画
      6.1. DepthPageTransformer
      6.2. ZoomOutPageTransformer
      6.3. 自定义动画
      6.4. 开源框架ViewPagerTransforms
    7. 翻页监听
      7.1. 设置方法
      7.2. 翻页监听接口
      7.3. 重写方法
      7.4. 使用

    三、与Fragment结合使用

    1. 创建Fragment及相应的xml布局
    2. 给Viewpager设置数据和适配器

    四、实现轮播图效果

    1. 特点
    2. 使用介绍
      2.1. 导包 + 权限
      2.2. xml引用
      2.3. 创建图片加载器
      2.4. 设置数据

    五、实现画廊效果

    1. viewpager布局
    2. pager布局
    3. Adapter
    4. vp设置adapter
    5. 问题

    一、简介

    Viewpager,视图翻页工具,提供了多页面切换的效果。Android 3.0后引入的一个UI控件,位于v4包中。低版本使用需要导入v4包,但是现在我们开发的APP一般不再兼容3.0及以下的系统版本,另外现在大多数使用Android studio进行开发,默认导入v7包,v7包含了v4,所以不用导包,越来越方便了。

    Viewpager使用起来就是我们通过创建adapter给它填充多个view,左右滑动时,切换不同的view。Google官方是建议我们使用Fragment来填充ViewPager的,这样 可以更加方便的生成每个Page,以及管理每个Page的生命周期。

    Viewpager在Android开发中使用频率还是比较高的,下面开始一起学习吧!

    二、基本使用

    1. xml引用

    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v4.view.ViewPager>
    

    2. page布局

    <?xml version="1.0" encoding="utf-8"?>
    <TextView
        android:id="@+id/tv"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FAE8DA"
        android:gravity="center"
        android:text="Hello"
        android:textSize="22sp">
    </TextView>
    

    3. 创建适配器

    可直接new PagerAdapter,亦可创建它的子类

    public class MyPagerAdapter extends PagerAdapter {
        private Context mContext;
        private List<String> mData;
    
        public MyPagerAdapter(Context context ,List<String> list) {
            mContext = context;
            mData = list;
        }
    
        @Override
        public int getCount() {
            return mData.size();
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view = View.inflate(mContext, R.layout.item_base,null);
            TextView tv = (TextView) view.findViewById(R.id.tv);
            tv.setText(mData.get(position));
            container.addView(view);
            return view;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            // super.destroyItem(container,position,object); 这一句要删除,否则报错
            container.removeView((View)object);
        }
    
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    }
    

    4. 设置适配器

    private void setVp() {
        List<String> list = new ArrayList<>();
        for (int i = 0; i < 3; i++) {
           list.add("第"+i+"个View");
        }
    
        ViewPager vp = (ViewPager) findViewById(R.id.vp);
        vp.setAdapter(new MyPagerAdapter(this,list));
    }
    

    效果:

     
    19956127-6ca743fba39f74a2.gif
    20180228134517956.gif

    5. 标题栏

    给Viewpager设置标题栏有一下几种方式:

    PagerTabStrip: 带有下划线
    PagerTitleStrip: 不带下划线
    TabLayout:5.0后推出
    TabLayout的详细使用,可以看我的另一篇文章TabLayout。

    下面介绍另外两个的使用方法,没什么区别:

    1. xml引用

    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.v4.view.PagerTitleStrip
            android:id="@+id/pager_title"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:background="@android:color/white"
            android:layout_gravity="top"
            android:textColor="#ff0000"
            android:textSize="18sp">
    
        </android.support.v4.view.PagerTitleStrip>
    
    </android.support.v4.view.ViewPager>
    
     <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <android.support.v4.view.PagerTabStrip
            android:id="@+id/pager_tab"
            android:layout_width="match_parent"
            android:layout_height="45dp"
            android:layout_gravity="top"
            android:background="@android:color/white"
            android:textColor="#ff0000">
        </android.support.v4.view.PagerTabStrip>
        
    </android.support.v4.view.ViewPager>
    
    1. 重写PagerAdapter的getTitle()方法
     @Override
    public CharSequence getPageTitle(int position) {
        return mTitles[position];
    }
    

    这两种方法作为了解,不常用,项目中还没用到过

    效果:

     
    19956127-4f77f47bef964456.gif
     
     
    19956127-f0bd1e3f4e0a3e73.gif
     

    6. 翻页动画

    ViewPager有个方法叫做:

    setPageTransformer(boolean reverseDrawingOrder, PageTransformer transformer) 用于设置ViewPager切换时的动画效果,并且google官方还给出了两个示例(因为使用的是属性动画,所以不兼容3.0以下)。

    1. DepthPageTransformer

    public class DepthPageTransformer implements ViewPager.PageTransformer {
        private static final float MIN_SCALE = 0.75f;
        public void transformPage(View view, float position) {
            int pageWidth = view.getWidth();
            if (position < -1) { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setAlpha(0);
            } else if (position <= 0) { // [-1,0]
                // Use the default slide transition when moving to the left page
                view.setAlpha(1);
                view.setTranslationX(0);
                view.setScaleX(1);
                view.setScaleY(1);
            } else if (position <= 1) { // (0,1]
                // Fade the page out.
                view.setAlpha(1 - position);
                // Counteract the default slide transition
                view.setTranslationX(pageWidth * -position);
                // Scale the page down (between MIN_SCALE and 1)
                float scaleFactor = MIN_SCALE
                        + (1 - MIN_SCALE) * (1 - Math.abs(position));
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);
            } else { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setAlpha(0);
            }
        }
    }
    

    调用:

    vp.setPageTransformer(false,new DepthPageTransformer());
    

    效果:

     
    19956127-a14cce3249e1b894.gif
     

    2. ZoomOutPageTransformer

    public class ZoomOutPageTransformer implements ViewPager.PageTransformer
    {
        private static final float MIN_SCALE = 0.85f;
        private static final float MIN_ALPHA = 0.5f;
        @SuppressLint("NewApi")
        public void transformPage(View view, float position)
        {
            int pageWidth = view.getWidth();
            int pageHeight = view.getHeight();
            Log.e("TAG", view + " , " + position + "");
            if (position < -1)
            { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setAlpha(0);
            } else if (position <= 1) //a页滑动至b页 ; a页从 0.0 -1 ;b页从1 ~ 0.0
            { // [-1,1]
                // Modify the default slide transition to shrink the page as well
                float scaleFactor = Math.max(MIN_SCALE, 1 - Math.abs(position));
                float vertMargin = pageHeight * (1 - scaleFactor) / 2;
                float horzMargin = pageWidth * (1 - scaleFactor) / 2;
                if (position < 0)
                {
                    view.setTranslationX(horzMargin - vertMargin / 2);
                } else
                {
                    view.setTranslationX(-horzMargin + vertMargin / 2);
                }
                // Scale the page down (between MIN_SCALE and 1)
                view.setScaleX(scaleFactor);
                view.setScaleY(scaleFactor);
                // Fade the page relative to its size.
                view.setAlpha(MIN_ALPHA + (scaleFactor - MIN_SCALE)
                        / (1 - MIN_SCALE) * (1 - MIN_ALPHA));
            } else
            { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setAlpha(0);
            }
        }
    }
    

    调用:

    vp.setPageTransformer(false,new ZoomOutPageTransformer());
    

    效果:

     
    19956127-dd778f9e31a1d70a.gif
     

    3. 自定义动画
    网上看到鸿洋大神写的

    public class RotateDownPageTransformer implements ViewPager.PageTransformer {
        private static final float ROT_MAX = 20.0f;
        private float mRot;
    
    
        public void transformPage(View view, float position)
        {
            Log.e("TAG", view + " , " + position + "");
            if (position < -1)
            { // [-Infinity,-1)
                // This page is way off-screen to the left.
                view.setRotation(0);
            } else if (position <= 1) // a页滑动至b页 ; a页从 0.0 ~ -1 ;b页从1 ~ 0.0
            { // [-1,1]
                // Modify the default slide transition to shrink the page as well
                if (position < 0)
                {
                    mRot = (ROT_MAX * position);
                    view.setPivotX(view.getMeasuredWidth() * 0.5f);
                    view.setPivotY(view.getMeasuredHeight());
                    view.setRotation( mRot);
                } else
                {
                    mRot = (ROT_MAX * position);
                    view.setPivotX(view.getMeasuredWidth() * 0.5f);
                    view.setPivotY(view.getMeasuredHeight());
                    view.setRotation( mRot);
                }
                // Scale the page down (between MIN_SCALE and 1)
                // Fade the page relative to its size.
            } else
            { // (1,+Infinity]
                // This page is way off-screen to the right.
                view.setRotation( 0);
            }
        }
    }
    

    效果:

     
    19956127-e72543d3e1e3141b.gif
     

    position说明:
    当前显示页为0,前一页为-1,后一页为1,滑动过程中数值不断变大或变小,所以为float类型

    4. 开源框架ViewPagerTransforms
    里面有十几种翻页动画,基本够用了
    Github地址:ViewPagerTransforms

    7. 翻页监听

    1. 设置方法

    addOnPageChangeListener()
    

    2. 翻页监听接口

    ViewPager.OnPageChangeListener
    

    3. 重写方法
    onPageScrolled(int position, float positionOffset, int positionOffsetPixels)

    页面滑动状态停止前一直调用

    position:当前点击滑动页面的位置
    positionOffset:当前页面偏移的百分比
    positionOffsetPixels:当前页面偏移的像素位置

    onPageSelected(int position)

    滑动后显示的页面和滑动前不同,调用

    position:选中显示页面的位置

    onPageScrollStateChanged(int state)

    页面状态改变时调用

    state:当前页面的状态

    SCROLL_STATE_IDLE:空闲状态
    SCROLL_STATE_DRAGGING:滑动状态
    SCROLL_STATE_SETTLING:滑动后滑翔的状态

    1. 使用
    vp.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            Log.e("vp","滑动中=====position:"+ position + "   positionOffset:"+ positionOffset + "   positionOffsetPixels:"+positionOffsetPixels);
        }
    
        @Override
        public void onPageSelected(int position) {
            Log.e("vp","显示页改变=====postion:"+ position);
        }
    
        @Override
        public void onPageScrollStateChanged(int state) {
            switch (state) {
                case ViewPager.SCROLL_STATE_IDLE:
                    Log.e("vp","状态改变=====SCROLL_STATE_IDLE====静止状态");
                    break;
                case ViewPager.SCROLL_STATE_DRAGGING:
                    Log.e("vp","状态改变=====SCROLL_STATE_DRAGGING==滑动状态");
                    break;
                case ViewPager.SCROLL_STATE_SETTLING:
                    Log.e("vp","状态改变=====SCROLL_STATE_SETTLING==滑翔状态");
                    break;
            }
        }
    });
    

    Log:

     
    19956127-bbdf47afddc70337.png
     

    三、与Fragment结合使用

    与Fragment结合使用其实也一样,只是用Fragment代替原先的View,填充Viewpager;然后就是Adapter不一样,配合Fragment使用的有两个Adapter:FragmentPagerAdapter和FragmentStatePagerAdapter。

    相同点:
    FragmentPagerAdapter和FragmentStatePagerAdapter都继承自PagerAdapter

    不同点:
    卸载不再需fragment时,各自采用的处理方法有所不同

    FragmentStatePagerAdapter会销毁不需要的fragment。事务提交后, activity的FragmentManager中的fragment会被彻底移除。 FragmentStatePagerAdapter类名中的“state”表明:在销毁fragment时,可在onSaveInstanceState(Bundle)方法中保存fragment的Bundle信息。用户切换回来时,保存的实例状态可用来恢复生成新的fragment

    FragmentPagerAdapter有不同的做法。对于不再需要的fragment, FragmentPagerAdapter会选择调用事务的detach(Fragment)方法来处理它,而非remove(Fragment)方法。也就是说, FragmentPagerAdapter只是销毁了fragment的视图, fragment实例还保留在FragmentManager中。因此,FragmentPagerAdapter创建的fragment永远不会被销毁

    也就是:在destroyItem()方法中,FragmentStatePagerAdapter调用的是remove()方法,适用于页面较多的情况;FragmentPagerAdapter调用的是detach()方法,适用于页面较少的情况。但是有页面数据需要刷新的情况,不管是页面少还是多,还是要用FragmentStatePagerAdapter,否则页面会因为没有重建得不到刷新

    使用如下:

    1. 创建Fragment及相应的xml布局

    public class PagerFragment extends Fragment {
        String mContent;
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            mContent = (String) getArguments().get("content");
            View view = inflater.inflate(R.layout.fragment_pager, container, false) ;
            TextView textView = (TextView) view.findViewById(R.id.tv);
            textView.setText(mContent);
            return view;
        }
    
    }
    
    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context="com.strivestay.viewpagerdemo.PagerFragment">
    
        <TextView
            android:id="@+id/tv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:gravity="center"
            android:textSize="18sp"
            android:text="@string/hello_blank_fragment"/>
    
    </FrameLayout>
    

    2. 给Viewpager设置数据和适配器

    private void setVp() {
        final List<PagerFragment> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            PagerFragment fragment = new PagerFragment();
            Bundle bundle = new Bundle();
            bundle.putString("content","第"+i+"个Fragment");
            fragment.setArguments(bundle);
    
            list.add(fragment);
        }
    
        ViewPager vp = (ViewPager) findViewById(R.id.vp);
    //        vp.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) {
    //            @Override
    //            public Fragment getItem(int position) {
    //                return list.get(position);
    //            }
    //
    //            @Override
    //            public int getCount() {
    //                return list.size();
    //            }
    //        });
    
        vp.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) {
            @Override
            public Fragment getItem(int position) {
                return list.get(position);
            }
    
            @Override
            public int getCount() {
                return list.size();
            }
        });
    
    }
    

    效果:

     
    19956127-381f4b0c65a17b04.gif
     

    四、实现轮播图效果

    这里我就不自己实现了,介绍一个轮播图开源控件:banner

    1. 特点

    支持无限循环和多种主题
    可以灵活设置轮播样式、动画、轮播和切换时间、位置、图片加载框架

    2. 使用介绍

    1. 导包 + 权限

    implementation 'com.youth.banner:banner:1.4.10'
    
    <!-- 加载网络图片需要权限 -->
    <uses-permission android:name="android.permission.INTERNET" /> 
    
    <!-- 加载本地图片需要权限-->
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    

    2. xml引用

    <com.youth.banner.Banner
        android:id="@+id/banner"
        android:layout_width="match_parent"
        android:layout_height="自己设定"/>
    

    3. 创建图片加载器

    /**
     * 图片轮播加载
     */
    public class GlideImageLoader extends ImageLoader {
    
        @Override  // path随便传,我这里最终传的是个对象,拿到图片Url
        public void displayImage(Context context, Object path, ImageView imageView) {
    
            //Glide 加载图片,Fresco也好、加载本地图片也好,这个类功能就是加载图片
            Glide.with(context).load(((AdList.DataBean)path).image).into(imageView);
    
        }
    }
    
    

    4. 设置数据

    /**
     * 设置banner
     * @param data
     */
    private void setBanner(List<AdList.DataBean> data) {
        // banner样式
        banner.setBannerStyle(BannerConfig.NOT_INDICATOR);
        // 设置图片加载器
        banner.setImageLoader(new GlideImageLoader());
        // 设置图片集合
        banner.setImages(data);
        // 翻页特效
        banner.setBannerAnimation(Transformer.Default);
        // 设置轮播时间
        banner.setDelayTime(4000);
        // banner设置方法全部调用完毕时最后调用
        banner.start();
    }
    
    @Override
    public void onResume() {
        super.onResume();
        //开始自动翻页
        banner.startAutoPlay();
    }
    
    @Override
    public void onPause() {
        super.onPause();
        //停止翻页
        banner.stopAutoPlay();
    }
    
    

    以上是简单使用,更详细的用法可以直接到GitHub上去看,文档是中文的,很方便,API也很简单,上面已经给出链接

    效果:(项目里截图不方便,直接拿的示例图)

     
    19956127-44ef007038f5f763.png
     

    五、实现画廊效果

    效果如下:

     
    19956127-e76a947cefd188db.gif
     
     
    19956127-634b82ce42e7d94b.gif
     

    实现步骤:
    1. viewpager布局

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:clipChildren="false"
        tools:context="com.strivestay.viewpagerdemo.FourthActivity">
    
        <include layout="@layout/layout_toolbar"/>
    
        <android.support.v4.view.ViewPager
            android:id="@+id/vp"
            android:layout_width="match_parent"
            android:layout_height="180dp"
            android:layout_marginTop="100dp"
            android:layout_marginLeft="30dp"
            android:layout_marginRight="30dp"
            android:clipChildren="false">
        </android.support.v4.view.ViewPager>
    
    </LinearLayout>
    

    要点:
    给viewpager和它的父布局都设置属性android:clipChildren=“false”

    . pager布局

    item_banner_samll.xml

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <ImageView
            android:id="@+id/iv_banner"
            android:layout_width="200dp"
            android:layout_height="180dp"
            android:background="#009999"
            android:scaleType="centerCrop"/>
    </LinearLayout>
    

    item_banner.xml

    <?xml version="1.0" encoding="utf-8"?>
    <ImageView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/iv_banner"
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:background="#009999"
        android:scaleType="centerCrop"/>
    

    区别:
    宽度,一个占满viewpager的宽度,一个小于viewpager的宽度

    3. Adapter

    /**
     * 画廊效果,page宽度占满vp
     * @author StriveStay
     * @date 2018/2/24
     */
    public class FourthPageAdapter extends PagerAdapter {
        private Context mContext;
    
        public FourthPageAdapter(Context context) {
            mContext = context;
        }
    
        @Override
        public int getCount() {
            return Integer.MAX_VALUE;
        }
    
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            // 就4张图片
            position %= 4;
    
            View view = View.inflate(mContext,R.layout.item_banner,null);
            ImageView iv = (ImageView) view.findViewById(R.id.iv_banner);
            int resourceId = mContext.getResources().getIdentifier("img" + (position + 1), "drawable", mContext.getPackageName());
            Glide.with(mContext).load(resourceId).into(iv);
            //            iv.setImageResource(resourceId);
            container.addView(view);
            return view;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View)object);
        }
    
    }
    

    如果是page宽度 < vp宽度,需要重写getPageWidth()方法,用于计算page占据vp的百分比

    @Override
    public float getPageWidth(int position) {
        float itemWidth =  (mContext.getResources().getDisplayMetrics().density * 200);
        float vpWidth = (mContext.getResources().getDisplayMetrics().widthPixels - mContext.getResources().getDisplayMetrics().density * 60);
        return  itemWidth / vpWidth;
    }
    

    3. vp设置adapter

    private void setVp() {
        ViewPager vp = (ViewPager) findViewById(R.id.vp);
    
        // 设置适配器
    //        vp.setAdapter(new FourthPageAdapter(this));
        vp.setAdapter(new FourthSmallPageAdapter(this));
    
        // page 边距
        vp.setPageMargin((int)(getResources().getDisplayMetrics().density * 15));
    
        // 为了左右无限滑动,显示在中间,且显示第一张
        int i = Integer.MAX_VALUE/2%4;
        vp.setCurrentItem(Integer.MAX_VALUE/2 + (4-i));
    
    }
    

    4. 问题
    当page宽度 < vp宽度,且page的数量较少,没有占满vp,这时滑动vp,会出现闪屏,如下:

     
    19956127-1e7de8f0148ccd54.gif
     

    解决办法:

    当明确知道vp放不下2个page时,可以如下处理

    @Override
    public float getPageWidth(int position) {
        // 加上这句
        if(getCount() < 2){
            return super.getPageWidth(position);
        }
    
        float itemWidth =  (mContext.getResources().getDisplayMetrics().density * 200);
        float vpWidth = (mContext.getResources().getDisplayMetrics().widthPixels - mContext.getResources().getDisplayMetrics().density * 60);
    
        Log.e("比例",vpWidth+"==="+itemWidth+"==="+(int)(vpWidth/itemWidth));
    
        return  itemWidth / vpWidth;
    
    }
    

    当vp可以放置两个以上的page时,也是个通用的方法

    vp.setOnTouchListener(new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            // vp中最多能放下2个page,则让vp中page个数 < 3时,让vp不能滑动
            if(vp.getChildCount() < 3 && event.getAction() == MotionEvent.ACTION_MOVE){
                return true;
            }
            return false;
        }
    });
    

    更多Android高级架构进阶视频学习请点击:https://space.bilibili.com/474380680
    原文链接:https://blog.csdn.net/weixin_39251617/article/details/79399592

  • 相关阅读:
    关于Faster-RCNN训练细节
    tensorflow 使用tfrecords创建自己数据集
    深度学习(tensorflow) —— 自己数据集读取opencv
    Tensorflow常见函数case argmax equal
    pandas dataframe类型操作
    celery 启动命令
    pipenv 虚拟环境新玩法
    拉取远程分支
    odoo 开发 context 上下文的用法
    odoo 二次开发的方法
  • 原文地址:https://www.cnblogs.com/Android-Alvin/p/11953009.html
Copyright © 2020-2023  润新知