简体   繁体   English

为什么 Android Material Design 选项卡布局不起作用?

[英]Why Android Material Design Tab Layout Does not work?

I am trying to create a tablayout with viewpager, but it does not work.我正在尝试使用 viewpager 创建一个 tablayout,但它不起作用。 when it is loaded by tabLayout.setupWithViewPager(viewPager);当它被tabLayout.setupWithViewPager(viewPager);加载时tabLayout.setupWithViewPager(viewPager); in the PatientMain main activity class the output is:在 PatientMain 主要活动类中,输出为:

在此处输入图片说明

and when i use viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));当我使用viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout)); in the PatientMain main activity class the output is:在 PatientMain 主要活动类中,输出为:

在此处输入图片说明

but the fragment is not shown and there is no swiping available.但片段没有显示,也没有可用的滑动。

I do it by the book, exactly as it is said to be done.我按照书上说的那样做。 i used several sources such as 1 , 2 .I personally think that the code might not be the problem and there is something wrong with the version of sdk that is used, but i might be wrong.我使用了几个来源,例如12 。我个人认为代码可能不是问题,并且使用的 sdk 版本有问题,但我可能是错的。 here is my code:这是我的代码:

the main activity:主要活动:

package parsa.lop.adjust;

import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TabHost;

public class PatientMain extends AppCompatActivity {

    private TabHost tabHost;

    private TabLayout tabLayout;
    private ViewPager viewPager;
    private ViewPagerAdapter viewPagerAdapter;

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

        tabLayout = (TabLayout)findViewById(R.id.tabLayout);
        viewPager = (ViewPager)findViewById(R.id.viewPager);

        viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
        viewPagerAdapter.addFragment(new MyDietFragment(), "My Diet"); // `new MyDietFragment()` should be in `FragmentPagerAdapter.getItem()`
        viewPagerAdapter.addFragment(new MyWorkoutFragment(), "My Workout");  // `new MyWorkoutFragment()` should be in `FragmentPagerAdapter.getItem()`
        viewPagerAdapter.addFragment(new StartProgramFragment(), "Start a Program");  // `new StartProgramFragment()` should be in `FragmentPagerAdapter.getItem()`
        viewPager.setAdapter(viewPagerAdapter);



        final TabLayout.Tab myDietTab = tabLayout.newTab();
        final TabLayout.Tab myWorkOutTab = tabLayout.newTab();
        final TabLayout.Tab startProgramTab = tabLayout.newTab();

        myDietTab.setText("My Diet");
        myWorkOutTab.setText("My Workout");
        startProgramTab.setText("Start a Program");

        tabLayout.addTab(startProgramTab,0);
        tabLayout.addTab(myDietTab,1);
        tabLayout.addTab(myWorkOutTab,2);

//        tabLayout.setupWithViewPager(viewPager);
        viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));

    }
}

the ViewPager Adapter: ViewPager 适配器:

package parsa.lop.adjust;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;

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

public class ViewPagerAdapter extends FragmentPagerAdapter {

    private List<String> fragmentTitleList = new ArrayList<String>();
    private List<Fragment> fragmentList = new ArrayList<Fragment>(); // this line can cause crashes

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

    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position); // viewPager fragments should be created here
    }

    @Override
    public int getCount() {
        return 0; // this is the bug :p
    }

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

    public void addFragment(Fragment fragment, String title){
        fragmentList.add(fragment); // this line can cause crashes
        fragmentTitleList.add(title);
    }
}

one of the fragments:片段之一:

package parsa.lop.adjust;


import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MyDietFragment extends Fragment {

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

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

my main layout:我的主要布局:

<?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"
    tools:context=".PatientMain">

    <android.support.design.widget.TabLayout
        android:id="@+id/tabLayout"
        android:layout_height="wrap_content"
        android:layout_width="match_parent"
        android:background="@color/colorPrimary"
        android:layout_alignParentLeft="true"
        android:layout_alignParentRight="true">

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

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

    </android.support.v4.view.ViewPager>

</android.support.constraint.ConstraintLayout>

my fragment layout:我的片段布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Tab 1"
        android:textSize="36sp" />

</RelativeLayout>

build.gradle:构建.gradle:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 27
    defaultConfig {
        applicationId "parsa.lop.adjust"
        minSdkVersion 21
        targetSdkVersion 27
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'com.android.support:design:27.1.1'
}

In your ViewPagerAdapter, replace this:在您的 ViewPagerAdapter 中,替换以下内容:

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

With this:有了这个:

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

You're setting the count of the adapter's contents to be 0, which is the same as the adapter telling other components that it has no items to show.您将适配器内容的计数设置为 0,这与适配器告诉其他组件它没有要显示的项目相同。


Also, you should make sure that a Fragment hosted by a ViewPager is created inside FragmentPagerAdapter.getItem() , and not inside the Activity.此外,您应该确保由 ViewPager 托管的 Fragment 是在FragmentPagerAdapter.getItem()内部创建的,而不是在 Activity 内部创建。

You should also uncomment:您还应该取消注释:

tabLayout.setupWithViewPager(viewPager);

Remember that with this implementation your ViewPager will re-build the fragments inside it every 2 swipes of distance.请记住,使用此实现,您的 ViewPager 将每隔 2 次滑动距离重新构建其中的片段。 I mean: if you have 3 fragments and from 1 you swipe to 2 on 2 it will re-create the 3rd fragment and if from 3 you swipe to 1 on 2 it will re-create the 1st fragment (it will always do this no matters if you have already created the fragment or not).我的意思是:如果你有 3 个片段,并且从 1 滑动到 2 上的 2 它将重新创建第三个片段,如果从 3 滑动到 2 上的 1 它将重新创建第一个片段(它总是会这样做没有如果您已经创建了片段,这很重要)。 To avoid this problem (if you need that fragments don't be re-created) you should use:为避免此问题(如果您需要不重新创建片段),您应该使用:

viewPager.setOffscreenPageLimit(<num_of_your_pages>)

Also remember that Every Adapter (like RecyclerView.Adapter) uses the "getCount()" or "getItemCount()" method to get the number of elements to display, so in every Adapter you should override this method and return the count of your values.还要记住,每个适配器(如 RecyclerView.Adapter)都使用“getCount()”或“getItemCount()”方法来获取要显示的元素数,因此在每个适配器中,您应该覆盖此方法并返回值的计数.

Also, if I'm not wrong, the proper way to implements the TabLayout inside the XML file is by including it in AppBarLayout with a Toolbar.另外,如果我没记错的话,在 XML 文件中实现 TabLayout 的正确方法是将它包含在 AppBarLayout 和工具栏中。 That's because you should normally use a "NoActionBar" style on your activity.那是因为您通常应该在您的活动中使用“NoActionBar”样式。 So something like:所以像:

<com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:background="?attr/colorPrimary"
            app:popupTheme="@style/AppTheme.PopupOverlay"
            android:layout_height="56dp"/>

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabMode="fixed"
            app:tabGravity="fill"
            style="@style/AppTheme.TabLayout"/>
    </com.google.android.material.appbar.AppBarLayout>

And in the manifest for your activity:在您的活动清单中:

android:theme="@style/AppTheme.NoActionBar"

Also the code you used:还有你使用的代码:

final TabLayout.Tab myDietTab = tabLayout.newTab();
        final TabLayout.Tab myWorkOutTab = tabLayout.newTab();
        final TabLayout.Tab startProgramTab = tabLayout.newTab();

        myDietTab.setText("My Diet");
        myWorkOutTab.setText("My Workout");
        startProgramTab.setText("Start a Program");

Is useless because you had already defined the titles of the fragments when you added it to the viewPager.没用,因为你在将片段添加到 viewPager 时已经定义了它的标题。 So you should add:所以你应该添加:

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

/** Public Methods **/
    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment); // this line can cause crashes
        mFragmentTitleList.add(title);
    }

To your ViewPager, in this way you will set the fragment and the title for it.对于您的 ViewPager,您将通过这种方式为其设置片段和标题。 Then use: tabLayout.setupWithViewPager(viewPager);然后使用:tabLayout.setupWithViewPager(viewPager);

Also you shouldn't instantiate Fragments by using the constructor, use instead a Static Factory Method (it is useful if you need to pass values to the fragment).此外,您不应该使用构造函数实例化 Fragments,而是使用静态工厂方法(如果您需要将值传递给 Fragment,这很有用)。 A static factory method is a method which call the constructor to instantiate an instance of the object, then it can do some operations with it (in the fragments case it will set up an Arguments Bundle to pass the properties to the onCreate method of the fragment which will get the Arguments and set the properties of itself).静态工厂方法是一种调用构造函数来实例化对象实例的方法,然后它可以对其进行一些操作(在片段情况下,它将设置一个参数包将属性传递给片段的 onCreate 方法它将获取参数并设置自身的属性)。 Example of fragment initialization:片段初始化示例:

public static void initInstance(int myVal1, int myVal2){
     MyFragment mInstance = new MyFragment();
     Bundle args = new Bundle();
     args.putInt(<BUNDLE_KEY_MYVAL1>, myVal1);
     args.putInt(<BUNDLE_KEY_MYVAL2>, myVal2);
     mInstance.setArguments(args);
     return mInstance;
}

Last I suggest to migrate to androidx because supports library won't be updated anymore (:最后我建议迁移到 androidx 因为支持库不会再更新(:

Hope this is helpful :D have a nice coding!希望这是有帮助的 :D 有一个很好的编码!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM