在TabLayout中的片段之间滑动很慢

时间:2018-08-21 06:33:42

标签: android android-fragments android-viewpager android-tablayout

我有一个用Viewpager设置的TabLayout。 viewpager设置有适配器。适配器中添加了11个片段,每个片段都有一个recyclerview。当我运行应用程序时,在片段之间滑动会变慢。谁能告诉我这个问题的原因?

我的MainActivity:

    myTabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);


    mainViewPager = (ViewPager) findViewById(R.id.mainViewPager);
    tabLayoutAdapter = new TabLayoutAdapter(getSupportFragmentManager());
    tabLayoutAdapter.addFragment(new HomeFragment());
    tabLayoutAdapter.addFragment(new ClipFragment());
    tabLayoutAdapter.addFragment(new MovieFragment());
    tabLayoutAdapter.addFragment(new SerialFragment());
    tabLayoutAdapter.addFragment(new MusicFragment());
    tabLayoutAdapter.addFragment(new BookFragment());
    tabLayoutAdapter.addFragment(new PixFragment());
    tabLayoutAdapter.addFragment(new GameFragment());
    tabLayoutAdapter.addFragment(new NewsFragment());
    tabLayoutAdapter.addFragment(new LiveFragment());
    tabLayoutAdapter.addFragment(new AdvancedSearchFragment());
    mainViewPager.setAdapter(tabLayoutAdapter);
    myTabLayout.setupWithViewPager(mainViewPager);

我的适配器:

public class TabLayoutAdapter extends FragmentStatePagerAdapter {
List<Fragment> tabPages = new ArrayList<>();

public TabLayoutAdapter(FragmentManager fm) {
    super(fm);
}

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

@Override
public int getCount() {
    return tabPages.size();
}

public void addFragment(Fragment page){
    tabPages.add(page);
}

}

样本片段:

public class MovieFragment extends Fragment {
RecyclerView movieRecyclerView;
List<Movie> movieList;
MovieAdapter movieAdapter;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Log.d("Fragment", "thread = " + Thread.currentThread().getName());
    Log.i("Fragment", "thread = " + Thread.currentThread().getName());
}


@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {


    View movieView = inflater.inflate(R.layout.tab_movie, container, false);
    return movieView;
}


@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);

    movieRecyclerView = (RecyclerView) view.findViewById(R.id.movieRecyclerView);
    movieList = new ArrayList<Movie>();
    movieAdapter = new MovieAdapter(view.getContext(), movieList);

    RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(view.getContext(), 2);
    movieRecyclerView.setLayoutManager(mLayoutManager);
    movieRecyclerView.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(10), false));
    movieRecyclerView.setItemAnimator(new DefaultItemAnimator());
    movieRecyclerView.setAdapter(movieAdapter);

    prepareMovies();



}

public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {

    private int spanCount;
    private int spacing;
    private boolean includeEdge;

    public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
        this.spanCount = spanCount;
        this.spacing = spacing;
        this.includeEdge = includeEdge;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view); // item position
        int column = position % spanCount; // item column

        if (includeEdge) {
            outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
            outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)

            if (position < spanCount) { // top edge
                outRect.top = spacing;
            }
            outRect.bottom = spacing; // item bottom
        } else {
            outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
            outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f /    spanCount) * spacing)
            if (position >= spanCount) {
                outRect.top = spacing; // item top
            }
        }
    }
}



private void prepareMovies() {
    int[] covers = new int[]{
            R.drawable.bossbaby,
            R.drawable.abeautifulmind,
            R.drawable.despicableme,
            R.drawable.harrypotter,
            R.drawable.thegodfather,
            R.drawable.piratesofcaribbean,
            };

    Movie movie = new Movie("Boss Baby",10000,4.5,20000,16000, covers[0]);
    movieList.add(movie);
    movie = new Movie("A Beautiful mind",35100,4.9,40000,39000, covers[1]);
    movieList.add(movie);
    movie = new Movie("Despicable me",20000,4.7,33000,31000, covers[2]);
    movieList.add(movie);
    movie = new Movie("Harry Potter",90000,5.0,110000,105000, covers[3]);
    movieList.add(movie);
    movie = new Movie("The God Father",70000,4.9,90000,86700, covers[4]);
    movieList.add(movie);
    movie = new Movie("Pirates of caribbean",50000,4.6,70000,64000, covers[5]);
    movieList.add(movie);



    movieAdapter.notifyDataSetChanged();
}

private int dpToPx(int dp) {
    Resources r = getResources();
    return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
}

}

4 个答案:

答案 0 :(得分:5)

如果您有很多片段,并且希望对其动画和交互进行优化,则可以使用viewpager.setOffscreenPageLimit(int numberOfPages);

setOffscreenPageLimit()将具有默认值1,即viewpager将在其任一侧仅加载一个页面(片段),并且在滚动时将从适配器重新创建其他页面,因此基于操作的滞后页面即可。因此,当您知道页面数时,使用此方法将有助于平滑页面动画和交互。

来自文档setOffscreenPageLimit()

  
    

设置处于空闲状态的视图层次结构中应保留到当前页面任一侧的页面数。页面超出     需要时将从适配器重新创建此限制。

  
     

这是一种优化。如果您提前知道电话号码   您需要支持的页面或其中具有延迟加载机制的页面   放在页面上,调整此设置可以在以下方面带来好处   感知分页动画和交互的流畅性。如果你有   少数页面(3-4)可以一次全部保持活动状态,   新创建的视图子树在布局上花费的时间更少,因为   用户来回翻页

答案 1 :(得分:0)

这个问题多次提出。解决方案是相同的。

您的Ui正在挂起,这仅表示您没有以一种很好的方式管理UI / Main线程和Worker线程。您应该了解仅将UI线程用于设置UI。

例如。您调用网络服务。它为您提供项目数组。您要在列表中设置。

糟糕的方式

致电Volley / Retrofit网站服务。得到响应,解析它。然后在列表中进行设置。在此过程中,您确实设法使用了任何线程。只是Volly / Retrofit完美地工作了。但是之后所有的事情都在主线程中发生了。

好方法

致电Volley / Retrofit网站服务。获取响应,并在工作线程中解析。然后在列表中进行设置。在此过程中使用辅助线程进行解析。并且仅使用Main线程来设置UI。

关键点

  • 使用 AsyncTask WorkerThread 进行非UI操作。像获取数据,解析数据,根据需要处理数据(例如迭代列表)一样。
  • 在代码中的UI线程上标识长期运行的任务。将它们移动工作线程
  • 使用无限滚动列表,而不是一次设置所有数据。 (在ViewPager片段列表中很重要
  • 使用大尺寸图像也会挂起。确保图像尺寸适合您所需。

如果您问我为什么早些时候没有遇到这个问题?

因为您是在Single Activity / Fragment中进行工作的,UI线程在很短的时间内处理了您的解析。但是您遇到了这个问题,因为现在您有11个片段和11个列表。

编辑

现在,当您显示代码时。

  • 您的图片似乎很大。检查图像尺寸是否较小。 (50-150kb就足够了)。您可以使用 Tiny Image 批量压缩图像。
  • 第二件事是您不将ArrayList保存在本地以备将来使用。您可以检查RecyclerView是否为空,在其为空时设置“列表”。

检查此代码,在此我使用了旧列表

public class MovieFragment extends Fragment {
    RecyclerView movieRecyclerView;
    List<Movie> movieList;
    MovieAdapter movieAdapter;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("Fragment", "thread = " + Thread.currentThread().getName());
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View movieView = inflater.inflate(R.layout.tab_movie, container, false);
        return movieView;
    }

    private void setAdapter() {
        movieRecyclerView = (RecyclerView) view.findViewById(R.id.movieRecyclerView);
        movieAdapter = new MovieAdapter(view.getContext(), movieList);
        RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(view.getContext(), 2);
        movieRecyclerView.setLayoutManager(mLayoutManager);
        movieRecyclerView.addItemDecoration(new GridSpacingItemDecoration(2, dpToPx(10), false));
        movieRecyclerView.setItemAnimator(new DefaultItemAnimator());
        movieRecyclerView.setAdapter(movieAdapter);
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        setAdapter();
        if (movieList == null || movieList.size() == 0) {
            prepareMovies();
        }
        movieAdapter.notifyDataSetChanged();
    }

    public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {

        private int spanCount;
        private int spacing;
        private boolean includeEdge;

        public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
            this.spanCount = spanCount;
            this.spacing = spacing;
            this.includeEdge = includeEdge;
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            int position = parent.getChildAdapterPosition(view); // item position
            int column = position % spanCount; // item column

            if (includeEdge) {
                outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
                outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)

                if (position < spanCount) { // top edge
                    outRect.top = spacing;
                }
                outRect.bottom = spacing; // item bottom
            } else {
                outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
                outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f /    spanCount) * spacing)
                if (position >= spanCount) {
                    outRect.top = spacing; // item top
                }
            }
        }
    }

    private void prepareMovies() {
        movieList = new ArrayList<>();

        int[] covers = new int[]{
                R.drawable.bossbaby,
                R.drawable.abeautifulmind,
                R.drawable.despicableme,
                R.drawable.harrypotter,
                R.drawable.thegodfather,
                R.drawable.piratesofcaribbean,
        };

        movieList.add(new Movie("Boss Baby", 10000, 4.5, 20000, 16000, covers[0]));
        movieList.add(new Movie("A Beautiful mind", 35100, 4.9, 40000, 39000, covers[1]));
        movieList.add(new Movie("Despicable me", 20000, 4.7, 33000, 31000, covers[2]));
        movieList.add(new Movie("Harry Potter", 90000, 5.0, 110000, 105000, covers[3]));
        movieList.add(new Movie("The God Father", 70000, 4.9, 90000, 86700, covers[4]));
        movieList.add(new Movie("Pirates of caribbean", 50000, 4.6, 70000, 64000, covers[5]));

    }

    private int dpToPx(int dp) {
        Resources r = getResources();
        return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, r.getDisplayMetrics()));
    }
}

答案 2 :(得分:0)

与@Khemraj答案一起,还请尝试以下操作,因为当我们回到包含同一类的geometry = client.directions(**params_route)['routes'][0] geometry = pd.DataFrame({k : pd.Series(v) for k, v in geometry.iteritems()}) geometry = geometry[:-2] geometry['coordinates'] = geometry['geometry'].apply(Point) geometry['myline'] = 1 geometry = gpd.GeoDataFrame(geometry, geometry='coordinates') geometry = geometry.groupby('myline')['geometry'].apply(lambda x: LineString(x.tolist())) geometry = gpd.GeoDataFrame(geometry, geometry='geometry') myroute = LineString(geometry['geometry'].iloc[0]).wkb_hex # Update table: insert_query = """UPDATE schema.contents SET geom = ST_GeomFromWKB(%(geometry)s::geometry, 4326) WHERE id='1'""" engine.execute(insert_query, geometry=myroute) 时,就有可能出现有关recyclerview适配器的问题

viewpager

代替

tabLayoutAdapter = new TabLayoutAdapter(getChildFragmentManager());

答案 3 :(得分:0)

尝试以下操作以使导航更加顺畅:

1)在每个片段上将代码从onViewCreated移到onActivityCreated,因为它是在片段完全出现时调用方法:

 @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // put code of setting data to views etc.
    }

2)在FragmentStateAdapter类中实现以下方法:

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
//        super.destroyItem(container, position, object);
    }

    // Force a refresh of the page when a different fragment is displayed
    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

3)在片段中使用一些低分辨率图像(如果从资源文件夹中使用它们)。不要使用像-1024 * 960这样的大字体,尝试将它们压缩然后添加。

4)避免在片段的布局文件中使用wrap_content。因为这样会花时间计算宽度height,如果可以在dp中提供match_parent或任何特定值,则它将更快地工作。

尝试这些要点可能会帮助您。