【Android UI】ViewPager

1、简介

一个继承了ViewGroup的容器,用户向ViewPager提供若干个带数据的界面。ViewPager允许这些界面进行翻转。

翻转:随着用户手指在屏幕滑动,而切换用户提供的界面。

数据界面:用户提供View或者fragment,通过适配器设置给ViewPager

2、常用api

1、setAdapter(PagerAdapter adapter) 设置适配器
2、setOffscreenPageLimit(int limit) 设置缓存的页面个数,默认是 1,代表默认缓存下一页。
3、setCurrentItem(int item) 跳转到指定页面
4、addOnPageChangeListener(OnPageChangeListener listener)页面滑动监听
5、setPageTransformer(boolean reverseDrawingOrder,PageTransformer transformer, int pageLayerType) 设置页面滑动的动画效果。
6、setPageMargin(int marginPixels) 设置两页面之间的间隔Margin
7、setPageMarginDrawable(…) 设置两页面间隔之间的divide photo ,要想显示设置的图片,需要同时设置 setPageMargin()方法

3、常见的Adapter介绍及PagerAdapter探讨
  • PagerAdapter:ViewPager的Adapter基类,此类为抽象类。
  • FragmentPagerAdapter
  • FragmentStatePagerAdapter

(1)PagerAdapter 🌰

public class PagerAdapterDemoActivity extends AppCompatActivity {
    // 准备一组图片
    private int[] imgs = new int[]{
            R.drawable.img_yao, R.drawable.img_zhuge,
            R.drawable.img_libai, R.drawable.img_mingren,
            R.drawable.img_haizei, R.drawable.img_douluo};
    private List<View> mList;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pager_adapter_demo);
        offerData();
        initViewPager();
    }
    private void offerData() {
        // adapter 提供数据
        mList = new ArrayList<>(); 
        // 为每个view视图内的图片填充数据
        for (int img : imgs) {
            @SuppressLint("InflateParams") View mView = LayoutInflater.from(this).inflate(R.layout.page_pageradapter, null, false);
            AppCompatImageView imageView = mView.findViewById(R.id.img);
            imageView.setImageResource(img);
            mList.add(mView);
        }
    }
    private void initViewPager() {
        ViewPager viewPager = findViewById(R.id.vp_pager);
        // 设置adapter
        viewPager.setAdapter(new MyPagerAdapter(mList));
    }
}
/**
 * Created by sunnyDay on 2019/12/14 10:39
 */
public class MyPagerAdapter extends PagerAdapter {
    private List<View> mList;

    public MyPagerAdapter(List<View> mList) {
        this.mList = mList;
    }

    @Override
    public int getCount() {
        return mList == null ? 0 : mList.size();
    }

     /* view:view 视图
     
        object:键对象
        
        返回值:标识页面视图是否与键对象相关联
        
              true:当前view界面与给定的键对应
              false:当前的view界面与给定的键不对应
              一般我们直接view==object,通过布尔返回值自动判断
      */
    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view == object;
    }

     /*
       container:ViewPager的setAdapter源码可以看出container就是父容器也即Viewpager 对象
       position:当前页面索引
       返回值:页面对象所关联的键
       
       此方法内部需要开发者手动add下view到容器。这时view就被添加到Viewpager了
      */
    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        container.addView(mList.get(position));
        return mList.get(position);
    }

    /*
     container:viewpager对象
     position:当前页面索引
     object:页面对象所关联的键
     */
    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView((View) object);
    }
}

实现思路非常简单,activity中通过for 创建了6个带数据的View,然后通过MyPagerAdapter的构造吧数据传递给ViewPager的adapter。ViewPager 为ViewGroup的子类,所以也是个容器,adapter获得数据后Viewpager就会知道,便从adapter中获得子view。填充展示。

ViewPager本身没有直接提供子视图(View或者fragment)回收机制,而是采用Adapter的回调来展示页面更新过程,PagerAdapter可以实现视图的回收、管理,例如FragmentPagerAdapter。Fragment事务来管理每个视图。

(2) 有关PagerAdapter清除View的写法

使用PagerAdapter时为啥这两种写法都行?

 @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
         container.removeView((View) object); // 方式1
        // container.removeView(mList.get(position)); // 方式2
    }

其实二者都是删除指定的界面因为,一个非常简单的PagerAdapter可以选择将页面view本身作为键对象

4、结合TabLayout+fragment

adapter结合fragment时就要用到FragmentPagerAdapter或者FragmentStatePagerAdapter了。

PagerAdapter为抽象类,FragmentPagerAdapter就是PagerAdapter实现类。它将每一个页面表示为一个 Fragment,并且每一个Fragment都将会保存到fragment manager当中。
这种pager十分适用于有一些静态fragment,例如一组tabs,的时候使用。每个页面对应的Fragment当用户可以访问的时候会一直存在内存中,但是,当这个页面不可见的时候,view hierarchy将会被销毁。这样子会导致应用程序占有太多资源。当页面数量比较大的时候,建议使用 FragmentStatePagerAdapter。

FragmentStatePagerAdapter也是PagerAdapter的直接子类。
当使用FragmentStatePagerAdapter 时,实现将只保留当前页面,当页面离开视线后,就会被消除,释放其资源;而在页面需要显示时,生成新的页面。这么实现的好处就是当拥有大量的页面时,不必在内存中占用大量的内存。

FragmentStatePageAdapter 🌰

public class FragmentStatePageAdapterActivity extends AppCompatActivity {
    private com.google.android.material.tabs.TabLayout tabLayout;
    private ViewPager viewPager;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_fragment_state_page_adapter);
        getSupportActionBar().hide();
        initView();
        initData();

    }

    private void initData() {
        populateTabsData();
        populateViewPagerData();
    }

    private void populateViewPagerData() {
        List<Fragment> mList = new ArrayList<>();
        mList.add(new AnimeFragment());
        mList.add(new TvFragment());
        mList.add(new LoLFragment());
        mList.add(new KingGloryFragment());
        mList.add(new PeaceFragment());
        viewPager.setAdapter(new MyFragmentStateAdapter(getSupportFragmentManager(), FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT, mList));
        tabLayout.setupWithViewPager(viewPager, false);//自动绑定viewpager,实现联动效果
    }

    private void populateTabsData() {
        String[] arr = new String[]{
                "动漫", "电视剧", "LOL", "王者荣耀", "和平精英",
        };
        for (String s : arr) {
           //注意这里的对象使用(不要直接new TableLayout,使用的对象要为上文findviewbyid那个)否则回报空指针
            tabLayout.addTab(tabLayout.newTab().setText(s));
        }

    }

    private void initView() {
        tabLayout = findViewById(R.id.my_tab_layout);
        viewPager = findViewById(R.id.vp_pager);
    }
}
/**
 * Created by sunnyDay on 2019/12/16 19:46
 */
public class MyFragmentStateAdapter extends FragmentStatePagerAdapter {
    private List<Fragment> mList;

    public MyFragmentStateAdapter(@NonNull FragmentManager fm, int behavior, List<Fragment> mlist) {
        super(fm, behavior);
        this.mList = mlist;
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        return mList.get(position);
    }

    @Override
    public int getCount() {
        return mList == null ? 0 : mList.size();
    }

    /**
     * view pager 和 tab layout 联动时这个方法必须重写 否则 tab 标题会被全部置空
     * */
 //    @Nullable
//    @Override
//    public CharSequence getPageTitle(int position) {
//        String[] arr = new String[]{
//                "动漫", "电视剧", "LOL", "王者荣耀", "和平精英",
//        };
//        return arr[position];
//    }
}

behavior的值:

  • BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT:指示仅当前片段处于该Lifecycle.State.RESUMED 状态。

  • BEHAVIOR_SET_USER_VISIBLE_HINT:已经不建议使用,建议使用BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT

ViewPager与TabLayout联动的坑

 tabLayout.setupWithViewPager(viewPager, false);

如上这句代码后我们惊奇的发现tablayout的文字全部都是空了。这时我们分析下源码如下:

  private void setupWithViewPager( @Nullable final ViewPager viewPager, boolean autoRefresh, boolean implicitSetup) {
...
...
... 忽略n行

if (viewPager != null) {
      this.viewPager = viewPager;
      if (pageChangeListener == null) {
        pageChangeListener = new TabLayoutOnPageChangeListener(this);
      }
      pageChangeListener.reset();
      viewPager.addOnPageChangeListener(pageChangeListener);
      currentVpSelectedListener = new ViewPagerOnTabSelectedListener(viewPager);
      addOnTabSelectedListener(currentVpSelectedListener);
      final PagerAdapter adapter = viewPager.getAdapter();
      if (adapter != null) {
        // 重要方法
        setPagerAdapter(adapter, autoRefresh);
      }
      if (adapterChangeListener == null) {
        adapterChangeListener = new AdapterChangeListener();
      }
      adapterChangeListener.setAutoRefresh(autoRefresh);
      viewPager.addOnAdapterChangeListener(adapterChangeListener);
      setScrollPosition(viewPager.getCurrentItem(), 0f, true);
    } else {
      this.viewPager = null;
      // 重要方法
      setPagerAdapter(null, false);
    }  

这个方法内部有个重要方法setPagerAdapter,这时我们进入setPagerAdapter方法看看。

  void setPagerAdapter(@Nullable final PagerAdapter adapter, final boolean addObserver) {
     ... 省略n行
    populateFromPagerAdapter(); // 重要方法
  }

这里有个populateFromPagerAdapter方法:

在这里插入图片描述

看到这里就明白为啥联动时不重写adapter的getPageTitle时,tabs消失的坑。

The end

参考

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中,Button 是一种常用的用户界面元素,用于触发操作或执行特定的功能。下面是使用 Button 的基本步骤: 1. 在 XML 布局文件中定义 Button: ```xml <Button android:id="@+id/myButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Click Me" /> ``` 在这个示例中,我们定义了一个 id 为 "myButton" 的 Button,设置宽高为包裹内容,并设置显示文本为 "Click Me"。 2. 在代码中找到 Button 并设置点击事件的监听器: ```java Button myButton = findViewById(R.id.myButton); myButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 在这里编写按钮点击时要执行的代码 } }); ``` 这段代码通过 findViewById() 方法找到 XML 布局文件中定义的 id 为 "myButton" 的 Button,并为其设置了一个点击事件监听器。当按钮被点击时,onClick() 方法中的代码将会执行。 3. 在 onClick() 方法中编写按钮点击时要执行的代码。例如,可以弹出一个 Toast 消息: ```java Toast.makeText(getApplicationContext(), "Button Clicked", Toast.LENGTH_SHORT).show(); ``` 这段代码会在按钮被点击时显示一个短暂的 Toast 消息。 这就是使用 Button 的基本步骤。您可以根据实际需求自定义按钮的样式、添加图标等。同时,还可以使用其他属性和方法来控制按钮的行为和外观。希望这能帮助您开始使用 Button 在 Android 中实现交互功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值