美文网首页
ViewPager的坑坑

ViewPager的坑坑

作者: 波澜步惊 | 来源:发表于2019-07-12 16:34 被阅读0次

前言

做程序开发,基础很重要。同样是拧螺丝人家拧出来的可以经久不坏,你拧出来的遇到点风浪就开始颤抖,可见基本功的重要性。此系列,专门收录一些看似基础,但是没那么简单的小细节,同时提供权威解决方案。喜欢的同志们点个赞就是对我最大的鼓励!先行谢过!

网上可能有一些其他文章,提供了解决方案,但是要么就是没有提供可运行demo,要么就是demo不够纯粹,让人探索起来受到其他代码因素的影响,无法专注于当前这个知识点(比如,我只是想了解Activity的生命周期,你把生命周期探究的过程混入到一个很复杂的大杂烩Demo中,让人一眼就没有了阅读Demo代码的欲望),所以我觉得有必要做一个专题,用最纯粹的方式展示一个的解决方案.

正文

ViewPager。老牌系统控件了,坑当然也有不少,今天先说第一个。

Github地址:https://github.com/18598925736/ViewPagerKeng
建议下载源码之后对照阅读

错误示范

很简单,activity_main.xml里面 一个ViewPager

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:padding="20dp"
    tools:context=".MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/vp"
        android:background="#ddccdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="为了对比ViewPager的高度,特意放个按钮"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

</android.support.constraint.ConstraintLayout>

然后:MainActivity.java 很简单的一个ViewPageradapter使用。

package study.hank.com.viewpager;

import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewPager vp = findViewById(R.id.vp);

        List<Data> list = new ArrayList<>();
        list.add(new Data("1"));
        list.add(new Data("2"));
        list.add(new Data("3"));
        vp.setAdapter(new MyPagerAdapter(this, list));
    }

    class MyPagerAdapter extends PagerAdapter {

        private Context context;
        private List<Data> dataList;

        public MyPagerAdapter(Context context, List<Data> dataList) {
            this.context = context;
            this.dataList = dataList;
        }

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

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, int position) {
            View root = LayoutInflater.from(context).inflate(R.layout.vp_item, container, false);
            TextView tv = root.findViewById(R.id.tv);
            tv.setText(dataList.get(position).text);
            container.addView(root);
            return root;
        }

        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            container.removeView((View) object);
        }

        @Override
        public boolean isViewFromObject(@NonNull View view, @NonNull Object o) {
            return view == o;
        }

    }

    private class Data {
        String text;

        public Data(String text) {
            this.text = text;
        }
    }
}

但是请注意:adapter的item布局如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="200dp"
    android:background="@color/colorPrimaryDark"
    android:padding="20dp">

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="12dp"
        android:text="111111"
        android:textColor="#ffffffff"
        android:textSize="22sp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

这个布局的根节点宽高为: wrap_content 以及 200dp
原本我的想法是,子view设置了自适应宽,和固定高度,那么viewPager作为父容器,也应该呈现相应的宽高。
但是打脸了。

WTF??
你居然默认充满了???

思考一下,决定一个View宽高的是什么? View的onMeasure方法。
于是看看ViewPageronMeasure,一看吓一跳:

image.png

WTF? 难道我使用ViewPager就只能充满父容器么?万一我一个容器里要放多个ViewPager,那不是还要包一层布局?太low了。

解决方案

既然ViewPager的测量这么坑,那就不要用他自己的onMeasure了。我们帮它测。

package study.hank.com.viewpager;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class MyViewPager extends ViewPager {
    public MyViewPager(@NonNull Context context) {
        super(context);
    }

    public MyViewPager(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int hSpec, wSpec;
        int finalH = 0, finalW = 0;
        final int count = getChildCount();
        View child;

        //我原本想直接使用子view测量之后的宽高,然后发现,尼玛,ViewPager根本就没有针对考虑子View的WrapContent
//        for (int i = 0; i < count; i++) {
//            //算出所有子view的最大宽度
//            child = getChildAt(i);
//            int thisH = child.getMeasuredHeight();
//            int thisW = child.getMeasuredWidth();
//            finalH = finalH > thisH ? finalH : thisH;
//            finalW = finalW > thisW ? finalW : thisW;
//        }
//
//        hSpec = MeasureSpec.makeMeasureSpec(finalH, MeasureSpec.EXACTLY);
//        wSpec = MeasureSpec.makeMeasureSpec(finalW, MeasureSpec.EXACTLY);

        // 那没办法了,那就再对子view进行一次常规测量
        // 坑并不只是 ViewPager没有对子进行测量,而是,它对于子的测量,也是使用的默认matchParent··使得子view充满父容器
        // 那我自己来考虑子view的LayoutParam来测量一次
        for (int i = 0; i < count; i++) {
            child = getChildAt(i);
            measureChild(child, widthMeasureSpec, heightMeasureSpec);
            int thisH = child.getMeasuredHeight();
            int thisW = child.getMeasuredWidth();
            finalH = finalH > thisH ? finalH : thisH;
            finalW = finalW > thisW ? finalW : thisW;
            Log.d("MeasureSpec ", "finalH:" + finalH + "/finalW:" + finalW);
        }

        hSpec = MeasureSpec.makeMeasureSpec(finalH, MeasureSpec.EXACTLY);//这里的模式。。emmm
        wSpec = MeasureSpec.makeMeasureSpec(finalW, MeasureSpec.EXACTLY);

        setMeasuredDimension(wSpec, hSpec);
    }
}

继承ViewPager,重写onMeasure,再次给他setMeasuredDimension.
我们自己给他的测量,考虑子View的宽高,并且得出子view的最大宽高,设置给它。

正确示范

使用我们自己的MyViewPager替换它。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:padding="20dp"
    tools:context=".MainActivity">

    <study.hank.com.viewpager.MyViewPager
        android:id="@+id/vp"
        android:background="#ddccdd"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintRight_toRightOf="parent"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="为了对比ViewPager的高度,特意放个按钮"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent" />

</android.support.constraint.ConstraintLayout>

现在的效果:

v.gif.gif
我们给子view设置的宽高生效了

抛砖引玉

案例确实很简单,但是如果我们遇到问题,不知道如何思考,只知道去找度娘的话,那永远是个lowB。或者 你用一层布局,把ViewPager包起来,那更是lowB。作为一个高级开(码)发(农),要学会从根本上解决问题,从源码层寻找解决方案。
例如今天这个案例,我的目的是,当ViewPager的item 是自适应宽高的时候,我怎么让外部的ViewPager也呈现出自适应呢? 它原本的宽高就是占满父容器。从源码中找到原因,是它的onMeasure出现 了奇葩的逻辑,
虽然不知道谷歌大佬为什么这么写,囧。重写onMeasure之后,解决了问题,满足了需求。
但是,如果有其他需求呢?比如,宽度要求充满,高度要自适应子item。或者高度要自适应,宽度要是精确值。原理相同,按照需求重写onMeasure即可。

今天小小案例,抛砖引玉,希望帮助有缘人打开思路,不要在CV的道路上越走越黑。。。(像是对我自己说的 - -!)

有问题可以留言 ····欧了·

相关文章

  • ViewPager的坑坑

    前言 做程序开发,基础很重要。同样是拧螺丝人家拧出来的可以经久不坏,你拧出来的遇到点风浪就开始颤抖,可见基本功的重...

  • 坑坑坑坑坑

    路是人开出来的,无论多么坚固的路面,时间长了总是会有坑,没有人去填,只会越来越大,越来越深。 有的坑是修建时偷工减...

  • 他坑坑坑坑

    考虑考虑 考虑 兔兔发哦

  • 坑坑坑

    在使用Https下载图片,出现如下问题。最后发现是jdk 1.8.60问题,换成最新的1.8.172javax.n...

  • 坑坑坑

    有几个月的时间都在坑里,不知道要去向哪里,最近自己的很多问题都出来了。不稳定性,自我攻击自我怀疑,自我指责都出来了...

  • 123

    可坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑坑

  • spark遇到的坑坑坑

    问题1:内存溢出WARN TaskSetManager:66 - Lost task 0.1 in stage ...

  • 坑坑更健康之 Fragment+ViewPager懒加载

    前言 做程序开发,基础很重要。同样是拧螺丝人家拧出来的可以经久不坏,你拧出来的遇到点风浪就开始颤抖,可见基本功的重...

  • React Native坑坑坑

    一、以下为出错提示 error: PCH was compiled with module cache path ...

  • Tab & ViewPager & PagerAdapter

    Tab & ViewPager & PagerAdapter ViewPager & ViewPager2View...

网友评论

      本文标题:ViewPager的坑坑

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