TabLayout与ViewPager结合使用时滑动不流畅

40

编辑

我已经按照这些教程来解决这个问题:

http://www.truiton.com/2015/06/android-tabs-example-fragments-viewpager/ https://guides.codepath.com/android/google-play-style-tabs-using-tablayout http://www.voidynullness.net/blog/2015/08/16/android-tablayout-design-support-library-tutorial/

但是很烦人的是,问题仍然存在,尝试了几种解决方案后依然没有解决。这里有一个演示我遇到的问题。我被卡在这个问题上已经好几周了。

演示链接

我正在使用 Nexus 4 和 Nexus 5 来测试。

TabLayoutViewPager 滚动不流畅。我需要滑动两次才能切换到下一个标签页。我搜索了整个网络,但找不到任何解决方案。我正在使用最新的支持库。以下是 gradle 文件:

apply plugin: 'com.android.application'

android {
compileSdkVersion 23
buildToolsVersion "23.0.3"

defaultConfig {
    applicationId "com.softoven.ultron"
    minSdkVersion 15
    targetSdkVersion 23
    versionCode 1
    versionName "1.0"
}
buildTypes {
    release {
        minifyEnabled false
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:23.3.0'
compile 'com.android.support:design:23.3.0'
compile 'org.jsoup:jsoup:1.6.1'
compile 'com.mikhaellopez:circularimageview:3.0.0'
compile 'com.android.support:recyclerview-v7:23.3.0'
compile 'com.mcxiaoke.volley:library:1.0.19'
compile 'com.nostra13.universalimageloader:universal-image-loader:1.9.5'
compile 'com.google.code.gson:gson:2.5'
}

这是活动代码。

private DrawerLayout drawerLayout;
private ViewPager viewPager;
private TabLayout tabLayout;
private NavigationView navigationView;
private CategoriesDTO categoriesDTO;

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

private void initToolbar() {
    Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    getSupportActionBar().setDisplayHomeAsUpEnabled(true);
    getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_action_menu);
}

private void initUi() {
    drawerLayout = (DrawerLayout) findViewById(R.id.drawer);
    navigationView = (NavigationView) findViewById(R.id.navigation);
    viewPager = (ViewPager) findViewById(R.id.viewPager);
    tabLayout = (TabLayout) findViewById(R.id.tab);

}

private void loadCategories() {
    StringRequest request = new StringRequest(Constants.URL_GET_CATEGORIES, new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            categoriesDTO = Constants.gson.fromJson(response, CategoriesDTO.class);
            ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager());
            viewPager.setOffscreenPageLimit(1);
            viewPager.setAdapter(adapter);
            setTabLayout();
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {

        }
    });

    ApplicationController.getmInstance().addToRequestQueue(request);
}

private void setTabLayout() {

    tabLayout.setupWithViewPager(viewPager);

}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    MenuInflater menuInflater = getMenuInflater();
    menuInflater.inflate(R.menu.home_side_menu, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    int id = item.getItemId();
    switch (id) {
        case android.R.id.home:
            drawerLayout.openDrawer(GravityCompat.START);
            return true;
    }
    return super.onOptionsItemSelected(item);
}

private class ViewPagerAdapter extends FragmentPagerAdapter {

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

    @Override
    public Fragment getItem(int position) {
        return new ContentFragment();
    }

    @Override
    public int getCount() {
        return 10;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        String title = categoriesDTO.getCategories().get(position).getTitle();
        return (CharSequence) title;
    }
}

还有一个 XML 文件

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">


    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <include
            android:id="@+id/toolbar"
            layout="@layout/toolbar"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />

        <android.support.design.widget.TabLayout
            android:id="@+id/tab"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabTextColor="#fff"
            app:tabGravity="fill"
            app:tabMode="scrollable"
            >

        </android.support.design.widget.TabLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

    </android.support.v4.view.ViewPager>
</android.support.design.widget.CoordinatorLayout>

<android.support.design.widget.NavigationView
    android:id="@+id/navigation"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="start"
    app:menu="@menu/home_drawer_menu">

</android.support.design.widget.NavigationView>

这里有一张截图,你可以看到指示器被部分分割。

输入图片说明

有什么解决办法吗?


你在切换标签时是否在主线程上做了很多工作? - Ritt
不,我现在只有一个静态的片段。 - Zeeshan Shabbir
可能是因为你在其中有Volley服务器请求引起的。 - Jay Rathod
1
是的,显然是因为主线程上的某些处理。再次检查一下是否在主线程上执行任何操作以及您的请求。 - Ritt
选项卡的数量是否与getCount()返回的数量匹配? - CodeCody
显示剩余5条评论
9个回答

38

我刚刚浏览了你的代码。问题在于你没有在ContentFragment.java中提供任何要膨胀(inflate)的布局(layout)。

我已经将你的方法更改为:

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        //return super.onCreateView(inflater, container, savedInstanceState);
        return  inflater.inflate(R.layout.feed_item, container, false);

    }

在做了这些更改后,您的选项卡可以平滑地滚动。我不知道这种行为背后的原因,但这个方法确实使它起作用了。


在我的情况下,我没有覆盖Fragment的onCreateView方法(尝试一些示例应用程序),因此我遇到了滚动问题。在从Fragment的onCreateView返回视图后,问题得到解决。 - Prashant

18

在您的Activity中更改此行:

ViewPagerAdapter adapter = new ViewPagerAdapter(getChildFragmentManager());

我们如何在Activity中使用那个管理器?getChildFragmentManager()不是从Fragment继承的吗? - Nikos Hidalgo
是的,它起作用了。如果我们在活动中创建选项卡,我们应该使用getSupportFragmentManager(),而在片段的情况下,我们应该使用getChildFragmentManager()。 - Maniya Joe

6
如果您要在 Fragment 中创建 TabLayout,请使用 getChildFragmentManager() 代替 getSupportFragmentManager() 作为参数。
ViewPagerAdapter adapter = new ViewPagerAdapter(getChildFragmentManager());

4
请将viewPager.setOffscreenPageLimit(1); 设置为 viewPager.setOffscreenPageLimit(5);。

4
我认为你需要使用override方法setUserVisibleHint。你需要在你的Fragment中添加这个方法。
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}

offScreenPageLimit设置为您拥有的选项卡数量。

了解更多关于setUserVisibleHint的信息,请在这里阅读。


这个怎么修复滚动? - Zeeshan Shabbir
将您的offScreenPageLimit设置为选项卡的数量。 - Jay Rathod

2

在您的应用程序上启用所有StrictMode策略,以检测您可能无意中在主线程上执行的任何长时间操作,从而导致滞后,以便您可以采取进一步所需的纠正措施。

StrictMode是一个开发工具,它会检测你可能无意中做的事情并引起你的注意,以便你可以修复它们。StrictMode最常用于捕获应用程序主线程上的意外磁盘或网络访问,这是接收UI操作和执行动画的地方。

有关详细信息,请参见https://developer.android.com/reference/android/os/StrictMode.html

    // Enable all thread strict mode policies
    StrictMode.ThreadPolicy.Builder threadPolicyBuilder = new StrictMode.ThreadPolicy.Builder();

    // Detect everything that's potentially suspect
    threadPolicyBuilder.detectAll();

    // Crash the whole process on violation
    threadPolicyBuilder.penaltyDeath();

    // Log detected violations to the system log
    threadPolicyBuilder.penaltyLog();

    // Crash the whole process on any network usage.
    threadPolicyBuilder.penaltyDeathOnNetwork();


    StrictMode.ThreadPolicy threadPolicy = threadPolicyBuilder.build();
    StrictMode.setThreadPolicy(threadPolicy);

    // Enable all VM strict mode policies
    StrictMode.VmPolicy.Builder vmPolicyBuilder = new StrictMode.VmPolicy.Builder();

    // Detect everything that's potentially suspect
    vmPolicyBuilder.detectAll();

    // Log detected violations to the system log
    vmPolicyBuilder.penaltyLog();

    StrictMode.VmPolicy vmPolicy = vmPolicyBuilder.build();
    StrictMode.setVmPolicy(vmPolicy);

添加了进一步的解释。每行代码已经提供了注释。 - Chebyr

1
首先,为了使一切更加顺畅,请不要在主线程上执行重操作,使用异步任务(不要在异步任务中触摸视图),这样无论如何都会很流畅 :)
尝试我的方法:
SlidingTabLayout:
public class SlidingTabLayout extends HorizontalScrollView {
    /**
     * Allows complete control over the colors drawn in the tab layout. Set with
     * {@link #setCustomTabColorizer(TabColorizer)}.
     */
    View oldSelection = null;

    public interface TabColorizer {

        /**
         * @return return the color of the indicator used when {@code position} is selected.
         */
        int getIndicatorColor(int position);

    }

    private static final int TITLE_OFFSET_DIPS = 24;
    private static final int TAB_VIEW_PADDING_DIPS = 8;
    private static final int TAB_VIEW_TEXT_SIZE_SP = 12;

    private int mTitleOffset;

    private int mTabViewLayoutId;
    private int mTabViewTextViewId;
    private boolean mDistributeEvenly;

    private ViewPager mViewPager;
    private SparseArray<String> mContentDescriptions = new SparseArray<String>();
    private ViewPager.OnPageChangeListener mViewPagerPageChangeListener;

    private final SlidingTabStrip mTabStrip;

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

    public SlidingTabLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SlidingTabLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

        // Disable the Scroll Bar
        setHorizontalScrollBarEnabled(false);
        // Make sure that the Tab Strips fills this View
        setFillViewport(true);

        mTitleOffset = (int) (TITLE_OFFSET_DIPS * getResources().getDisplayMetrics().density);

        mTabStrip = new SlidingTabStrip(context);
        addView(mTabStrip, LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    }


    public void hideTab(int tabIndex){
        for (int i = 0; i < mTabStrip.getChildCount(); i++) {
            if(i == tabIndex){
                ((TextView)mTabStrip.getChildAt(i)).setVisibility(GONE);
            }
        }


    }

    /**
     * Set the custom {@link TabColorizer} to be used.
     *
     * If you only require simple custmisation then you can use
     * {@link #setSelectedIndicatorColors(int...)} to achieve
     * similar effects.
     */
    public void setCustomTabColorizer(TabColorizer tabColorizer) {
        mTabStrip.setCustomTabColorizer(tabColorizer);
    }

    public void setDistributeEvenly(boolean distributeEvenly) {
        mDistributeEvenly = distributeEvenly;
    }

    /**
     * Sets the colors to be used for indicating the selected tab. These colors are treated as a
     * circular array. Providing one color will mean that all tabs are indicated with the same color.
     */
    public void setSelectedIndicatorColors(int... colors) {
        mTabStrip.setSelectedIndicatorColors(colors);
    }

    /**
     * Set the {@link ViewPager.OnPageChangeListener}. When using {@link SlidingTabLayout} you are
     * required to set any {@link ViewPager.OnPageChangeListener} through this method. This is so
     * that the layout can update it's scroll position correctly.
     *
     * @see ViewPager#setOnPageChangeListener(ViewPager.OnPageChangeListener)
     */
    public void setOnPageChangeListener(ViewPager.OnPageChangeListener listener) {
        mViewPagerPageChangeListener = listener;
    }

    /**
     * Set the custom layout to be inflated for the tab views.
     *
     * @param layoutResId Layout id to be inflated
     * @param textViewId id of the {@link TextView} in the inflated view
     */
    public void setCustomTabView(int layoutResId, int textViewId) {
        mTabViewLayoutId = layoutResId;
        mTabViewTextViewId = textViewId;
    }

    /**
     * Sets the associated view pager. Note that the assumption here is that the pager content
     * (number of tabs and tab titles) does not change after this call has been made.
     */
    public void setViewPager(ViewPager viewPager) {
        mTabStrip.removeAllViews();

        mViewPager = viewPager;
        if (viewPager != null) {
            viewPager.setOnPageChangeListener(new InternalViewPagerListener());
            populateTabStrip();
        }
    }

    /**
     * Create a default view to be used for tabs. This is called if a custom tab view is not set via
     * {@link #setCustomTabView(int, int)}.
     */
    protected TextView createDefaultTabView(Context context) {
        TextView textView = new TextView(context);
        textView.setGravity(Gravity.CENTER);
        textView.setTextSize(16);
        textView.setTypeface(Constants.TYPE_FACE_FONT_MEDIUM);
        textView.setLayoutParams(new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));

        TypedValue outValue = new TypedValue();
        getContext().getTheme().resolveAttribute(android.R.attr.selectableItemBackground,
                outValue, true);
        textView.setBackgroundResource(outValue.resourceId);
        if(Constants.PRODUCT_DETAILS_TAB_BAR_STYLE == 2) {
            textView.setBackgroundColor(getResources().getColor(R.color.tab_bar_color));
            mTabStrip.setBackgroundColor(getResources().getColor(R.color.tab_bar_color));
        }

        textView.setAllCaps(true);

        int padding = (int) (TAB_VIEW_PADDING_DIPS * getResources().getDisplayMetrics().density);
        textView.setPadding(padding, padding, padding, padding);

        return textView;
    }

    private void populateTabStrip() {
        removeOldSelection();
        oldSelection = null;
        final PagerAdapter adapter = mViewPager.getAdapter();
        final View.OnClickListener tabClickListener = new TabClickListener();

        for (int i = 0; i < adapter.getCount(); i++) {
            View tabView = null;
            TextView tabTitleView = null;

            if (mTabViewLayoutId != 0) {
                // If there is a custom tab view layout id set, try and inflate it
                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
                        false);
                tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
            }

            if (tabView == null) {
                tabView = createDefaultTabView(getContext());
            }

            if (tabTitleView == null && TextView.class.isInstance(tabView)) {
                tabTitleView = (TextView) tabView;
            }

            if (mDistributeEvenly) {
                LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) tabView.getLayoutParams();
                lp.width = 0;
                lp.weight = 1;
            }

            tabTitleView.setText(adapter.getPageTitle(i));

            tabView.setOnClickListener(tabClickListener);
            String desc = mContentDescriptions.get(i, null);
            if (desc != null) {
                tabView.setContentDescription(desc);
            }

            mTabStrip.addView(tabView);
            if(Constants.PRODUCT_DETAILS_TAB_BAR_STYLE == 1)
                tabTitleView.setTextColor(getResources().getColorStateList(R.color.primary_ultralight));
            else
                tabTitleView.setTextColor(getResources().getColor(R.color.primary_text));
            if (i == mViewPager.getCurrentItem()) {
                tabView.setSelected(true);
                tabTitleView.setTextColor(getResources().getColorStateList(R.color.primary_ultralight));
            }

        }

    }

    public void setContentDescription(int i, String desc) {
        mContentDescriptions.put(i, desc);
    }

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();

        if (mViewPager != null) {
            scrollToTab(mViewPager.getCurrentItem(), 0);
        }
    }

    private void scrollToTab(int tabIndex, int positionOffset) {
        final int tabStripChildCount = mTabStrip.getChildCount();
        if (tabStripChildCount == 0 || tabIndex < 0 || tabIndex >= tabStripChildCount) {
            return;
        }

        View selectedChild = mTabStrip.getChildAt(tabIndex);
        if (selectedChild != null) {

            if(positionOffset == 0 && selectedChild != oldSelection) { // added part
                selectedChild.setSelected(true);
                removeOldSelection();
                oldSelection = selectedChild;
            }

            int targetScrollX = selectedChild.getLeft() + positionOffset;

            if (tabIndex > 0 || positionOffset > 0) {
                // If we're not at the first child and are mid-scroll, make sure we obey the offset
                targetScrollX -= mTitleOffset;
            }

            scrollTo(targetScrollX, 0);
        }
    }

    private class InternalViewPagerListener implements ViewPager.OnPageChangeListener {
        private int mScrollState;

        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            int tabStripChildCount = mTabStrip.getChildCount();
            if ((tabStripChildCount == 0) || (position < 0) || (position >= tabStripChildCount)) {
                return;
            }

            mTabStrip.onViewPagerPageChanged(position, positionOffset);

            View selectedTitle = mTabStrip.getChildAt(position);
            int extraOffset = (selectedTitle != null)
                    ? (int) (positionOffset * selectedTitle.getWidth())
                    : 0;
            scrollToTab(position, extraOffset);

            if (mViewPagerPageChangeListener != null) {
                mViewPagerPageChangeListener.onPageScrolled(position, positionOffset,
                        positionOffsetPixels);
            }
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            mScrollState = state;

            if (mViewPagerPageChangeListener != null) {
                mViewPagerPageChangeListener.onPageScrollStateChanged(state);
            }
        }

        @Override
        public void onPageSelected(int position) {
            if (mScrollState == ViewPager.SCROLL_STATE_IDLE) {
                mTabStrip.onViewPagerPageChanged(position, 0f);
                scrollToTab(position, 0);
            }
            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
                mTabStrip.getChildAt(i).setSelected(position == i);

                if(Constants.PRODUCT_DETAILS_TAB_BAR_STYLE == 2){
                    if(position == i)
                        ((TextView)mTabStrip.getChildAt(i)).setTextColor(getResources().getColorStateList(R.color.primary_ultralight));
                    else
                        ((TextView)mTabStrip.getChildAt(i)).setTextColor(getResources().getColor(R.color.primary_text));
                }




            }
            if (mViewPagerPageChangeListener != null) {
                mViewPagerPageChangeListener.onPageSelected(position);
            }
        }

    }
    private void removeOldSelection() {
        if(oldSelection != null) {
            oldSelection.setSelected(false);
        }
    }
    private class TabClickListener implements View.OnClickListener {
        @Override
        public void onClick(View v) {
            for (int i = 0; i < mTabStrip.getChildCount(); i++) {
                if (v == mTabStrip.getChildAt(i)) {
                    mViewPager.setCurrentItem(i);
                    return;
                }
            }
        }
    }

}

滑动选项卡条:

class SlidingTabStrip extends LinearLayout {

    private static final int DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS = 0;
    private static final byte DEFAULT_BOTTOM_BORDER_COLOR_ALPHA = 0x26;
    private static final int SELECTED_INDICATOR_THICKNESS_DIPS = 3;
    private static final int DEFAULT_SELECTED_INDICATOR_COLOR = 0xFF33B5E5;

    private final int mBottomBorderThickness;
    private final Paint mBottomBorderPaint;

    private final int mSelectedIndicatorThickness;
    private final Paint mSelectedIndicatorPaint;

    private final int mDefaultBottomBorderColor;

    private int mSelectedPosition;
    private float mSelectionOffset;

    private SlidingTabLayout.TabColorizer mCustomTabColorizer;
    private final SimpleTabColorizer mDefaultTabColorizer;

    SlidingTabStrip(Context context) {
        this(context, null);
    }

    SlidingTabStrip(Context context, AttributeSet attrs) {
        super(context, attrs);
        setWillNotDraw(false);

        final float density = getResources().getDisplayMetrics().density;

        TypedValue outValue = new TypedValue();
        context.getTheme().resolveAttribute(android.R.attr.colorForeground, outValue, true);
        final int themeForegroundColor =  outValue.data;

        mDefaultBottomBorderColor = setColorAlpha(themeForegroundColor,
                DEFAULT_BOTTOM_BORDER_COLOR_ALPHA);

        mDefaultTabColorizer = new SimpleTabColorizer();
        mDefaultTabColorizer.setIndicatorColors(DEFAULT_SELECTED_INDICATOR_COLOR);

        mBottomBorderThickness = (int) (DEFAULT_BOTTOM_BORDER_THICKNESS_DIPS * density);
        mBottomBorderPaint = new Paint();
        mBottomBorderPaint.setColor(mDefaultBottomBorderColor);

        mSelectedIndicatorThickness = (int) (SELECTED_INDICATOR_THICKNESS_DIPS * density);
        mSelectedIndicatorPaint = new Paint();
    }

    void setCustomTabColorizer(SlidingTabLayout.TabColorizer customTabColorizer) {
        mCustomTabColorizer = customTabColorizer;
        invalidate();
    }

    void setSelectedIndicatorColors(int... colors) {
        // Make sure that the custom colorizer is removed
        mCustomTabColorizer = null;
        mDefaultTabColorizer.setIndicatorColors(colors);
        invalidate();
    }

    void onViewPagerPageChanged(int position, float positionOffset) {
        mSelectedPosition = position;
        mSelectionOffset = positionOffset;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        final int height = getHeight();
        final int childCount = getChildCount();
        final SlidingTabLayout.TabColorizer tabColorizer = mCustomTabColorizer != null
                ? mCustomTabColorizer
                : mDefaultTabColorizer;

        // Thick colored underline below the current selection
        if (childCount > 0) {
            View selectedTitle = getChildAt(mSelectedPosition);
            int left = selectedTitle.getLeft();
            int right = selectedTitle.getRight();
            int color = tabColorizer.getIndicatorColor(mSelectedPosition);

            if (mSelectionOffset > 0f && mSelectedPosition < (getChildCount() - 1)) {
                int nextColor = tabColorizer.getIndicatorColor(mSelectedPosition + 1);
                if (color != nextColor) {
                    color = blendColors(nextColor, color, mSelectionOffset);
                }

                // Draw the selection partway between the tabs
                View nextTitle = getChildAt(mSelectedPosition + 1);
                left = (int) (mSelectionOffset * nextTitle.getLeft() +
                        (1.0f - mSelectionOffset) * left);
                right = (int) (mSelectionOffset * nextTitle.getRight() +
                        (1.0f - mSelectionOffset) * right);
            }

            mSelectedIndicatorPaint.setColor(color);

            canvas.drawRect(left, height - mSelectedIndicatorThickness, right,
                    height, mSelectedIndicatorPaint);
        }

        // Thin underline along the entire bottom edge
        canvas.drawRect(0, height - mBottomBorderThickness, getWidth(), height, mBottomBorderPaint);
    }

    /**
     * Set the alpha value of the {@code color} to be the given {@code alpha} value.
     */
    private static int setColorAlpha(int color, byte alpha) {
        return Color.argb(alpha, Color.red(color), Color.green(color), Color.blue(color));
    }

    /**
     * Blend {@code color1} and {@code color2} using the given ratio.
     *
     * @param ratio of which to blend. 1.0 will return {@code color1}, 0.5 will give an even blend,
     *              0.0 will return {@code color2}.
     */
    private static int blendColors(int color1, int color2, float ratio) {
        final float inverseRation = 1f - ratio;
        float r = (Color.red(color1) * ratio) + (Color.red(color2) * inverseRation);
        float g = (Color.green(color1) * ratio) + (Color.green(color2) * inverseRation);
        float b = (Color.blue(color1) * ratio) + (Color.blue(color2) * inverseRation);
        return Color.rgb((int) r, (int) g, (int) b);
    }

    private static class SimpleTabColorizer implements SlidingTabLayout.TabColorizer {
        private int[] mIndicatorColors;

        @Override
        public final int getIndicatorColor(int position) {
            return mIndicatorColors[position % mIndicatorColors.length];
        }

        void setIndicatorColors(int... colors) {
            mIndicatorColors = colors;
        }
    }
}

使用方法:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:background="@color/layouts_background"
    android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <RelativeLayout
                android:id="@+id/page_root_container"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:orientation="vertical">
                <include
                    android:id="@+id/app_bar"
                    layout="@layout/app_bar"/>
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_below="@+id/app_bar"
                    android:orientation="vertical">
                    <com.app.myapp.tab.SlidingTabLayout
                        android:id="@+id/detailsTabs"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">

                    </com.app.myapp.tab.SlidingTabLayout>
                    <android.support.v4.view.ViewPager
                        android:id="@+id/detailsPager"
                        android:layout_width="match_parent"
                        android:layout_height="0dp"
                        android:layout_weight="1">
                    </android.support.v4.view.ViewPager>
                </LinearLayout>
            </RelativeLayout>
        </LinearLayout>
</RelativeLayout>

工具栏作为应用栏:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="?android:attr/actionBarSize"
    android:background="@color/primary"
    app:theme="@style/CustomToolbarTheme">


</android.support.v7.widget.Toolbar>

在活动中:

class DetailsTabsAdaptor extends FragmentPagerAdapter {
        String [] tabsTitlesPrepare = {getString(R.string.details),getString(R.string.features), getString(R.string.spects), getString(R.string.downloads)};
        List<String> tabsTitles = new ArrayList<>();
        int tabsCount = 0;
        boolean featuresExist = false;
        boolean specificationsExist = false;
        boolean downloadsExist = false;

        public DetailsTabsAdaptor(FragmentManager fm) {
            super(fm);
            tabsTitles.add(tabsTitlesPrepare[0]);
            tabsCount++;
            if ((mProductDetails.ProductStructure.Features != null && mProductDetails.ProductStructure.Features.size()>0)) {
                tabsTitles.add(tabsTitlesPrepare[1]);
                featuresExist = true;
                tabsCount++;
            }
            if ((mProductDetails.ProductStructure.SpecificationBlocks != null && mProductDetails.ProductStructure.SpecificationBlocks.size()>0)) {
                tabsTitles.add(tabsTitlesPrepare[2]);
                specificationsExist = true;
                tabsCount++;
            }
            if ((mProductDetails.ProductStructure.SupportFiles != null && mProductDetails.ProductStructure.SupportFiles.size()>0)) {
                tabsTitles.add(tabsTitlesPrepare[3]);
                downloadsExist = true;
                tabsCount++;
            }

        }

        @Override
        public CharSequence getPageTitle(int position) {
            return tabsTitles.get(position);
        }

        @Override
        public Fragment getItem(int position) {
            if(position == 0){
                ProductDetailsFragment fragment = new ProductDetailsFragment();
                return fragment;
            }
            if(position == 1 && featuresExist){
                ProductFeaturesFragment fragment = new ProductFeaturesFragment();
                return fragment;
            }
            if((position == 2 && specificationsExist) || (position == 1 && (!featuresExist) && specificationsExist)){
                ProductSpectsFragment fragment = new ProductSpectsFragment();
                return fragment;
            }
            if((position == 3 && downloadsExist) || (position == 1 && (!featuresExist) && (!specificationsExist) && downloadsExist ) ||
                    (position == 2 && (!specificationsExist) && downloadsExist )){
                ProductDownloadsFragment fragment = new ProductDownloadsFragment();
                return fragment;
            }

            return null;
        }

        @Override
        public int getCount() {
            return tabsCount;
        }
    }

设置选项卡

public void setupTabs() {
        mDetailsPager.setAdapter(new DetailsTabsAdaptor(getSupportFragmentManager()));
        mDetailsTabs.setViewPager(mDetailsPager);
        mDetailsTabs.setBackgroundResource(R.color.primary);
        mDetailsTabs.setCustomTabView(R.layout.custom_details_view_tab_layout, R.id.tabText);
        mDetailsTabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
            @Override
            public int getIndicatorColor(int position) {
                return getResources().getColor(R.color.primary_light);
            }
        });


        mDetailsTabs.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {

                for (int i = 0; i < mDetailsTabs.getChildCount(); i++)
                    mDetailsTabs.getChildAt(i).setSelected(i == position);
                if (position == DESCRIPTION_PAGE_ID)
                    mFavoritesButton.setVisibility(View.VISIBLE);
                else
                    mFavoritesButton.setVisibility(View.GONE);

            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

    }

custom_details_view_tab_layout :

自定义详情视图选项卡布局:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:background="@color/primary"
    android:layout_height="match_parent">
    <com.avad.avaddroid.customModels.TextViewMedium
        android:id="@+id/tabText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/primary_light"
        android:layout_gravity="center"/>

</LinearLayout>

最后注意事项:没有在此处初始化所有变量,请不要懒惰,尽量学习一些知识;)

1

在安卓的坐标布局和应用栏布局中,这并不是什么新鲜事;无论你做什么,我相信你都无法使其更加流畅。不过

https://github.com/henrytao-me/smooth-app-bar-layout

这可能满足您的要求;但首先需要从以下链接下载示例应用程序:
https://play.google.com/store/apps/details?id=me.henrytao.smoothappbarlayout

看看它是否符合您的需求。(只是为了确保)


实际上,使用示例应用程序不能解决问题,这只是一种替代方案,而Ankit Aggarwal的解决方案完美地解决了问题。 - Prashant

0
在你的Android清单文件中,在<Application>标签中添加这行代码。
 android:largeHeap="true" 

这个标签会做什么? - Zeeshan Shabbir
我认为这可能会对你有所帮助.. https://github.com/henrytao-me/smooth-app-bar-layout/issues/50 - Krupa
http://www.voidynullness.net/blog/2015/08/16/android-tablayout-design-support-library-tutorial/ - Krupa
增加堆大小是最后的手段,不建议使用。 - CodeCody
2
非常糟糕的建议 - 增加堆大小以解决ViewPager滚动问题 - Lester

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接