使用 RecycleView Fragments 滑动标签并滑动刷新

Sliding Tabs with RecycleView Fragments and Swipe to Refresh

我在实现包含 2 个片段和向下滑动以刷新布局的滑动选项卡 activity 时遇到一些问题,即实现向下滑动以刷新部分(其余工作正常)。

首先是我的 XML 个文件。

Main Activity XML ,其中包含包装在 SwipeRefreshLayout 中的 ViewPager :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
 tools:context="com.example.popal.soul.MovieListActivityTEST">

<com.example.popal.soul.SlidingTabLayout
 android:id="@+id/tabs"
 android:layout_width="match_parent"
 android:layout_height="wrap_content"
 android:elevation="2dp"
 android:background="@color/ColorPrimary"/>


<android.support.v4.widget.SwipeRefreshLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipeContainer"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

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

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

</android.support.v4.widget.SwipeRefreshLayout>

第一个选项卡 XML ,两个选项卡之一(两者都很相似,所以我只 post 一个)

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/cardList"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true" />
</RelativeLayout>

现在,我的主要 activity,它处理 ViewPager、Adapter 和 SlidingTabsLayout。

public class MovieListActivityTEST extends AppCompatActivity {

ViewPager pager;
ViewPagerAdapter adapter;
SlidingTabLayout tabs;
CharSequence Titles[]={"Home","Events"};
int Numboftabs =2;

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

    // Creating The ViewPagerAdapter and Passing Fragment Manager, Titles fot the Tabs and Number Of Tabs.
    adapter =  new ViewPagerAdapter(getSupportFragmentManager(),Titles,Numboftabs);

    // Assigning ViewPager View and setting the adapter
    pager = (ViewPager) findViewById(R.id.pager);
    pager.setAdapter(adapter);

    // Assiging the Sliding Tab Layout View
    tabs = (SlidingTabLayout) findViewById(R.id.tabs);
    tabs.setDistributeEvenly(true); 

    // Setting Custom Color for the Scroll bar indicator of the Tab View
    tabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
        @Override
        public int getIndicatorColor(int position) {
            return getResources().getColor(R.color.tabsScrollColor);
        }
    });

    // Setting the ViewPager For the SlidingTabsLayout
    tabs.setViewPager(pager);
}

最后,我的第一个选项卡片段

public class Tab1 extends Fragment {


public MovieListAdapter movieListAdaptor;
public RecyclerView recycleList;
private SwipeRefreshLayout swipeContainer;

private List<MovieListAdapter.MovieDetails> movieList = new ArrayList<MovieListAdapter.MovieDetails>();

private ProgressBar progressBar;
private final static String MOVIES_POST_REQUEST ="//Long String, Edited out since it`s not relevant"


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

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

    recycleList = (RecyclerView) v.findViewById(R.id.cardList);

    progressBar = (ProgressBar) v.findViewById(R.id.progress_bar);
    progressBar.setVisibility(View.VISIBLE);

    swipeContainer = (SwipeRefreshLayout) v.findViewById(R.id.swipeContainer);

    LinearLayoutManager llm = new LinearLayoutManager(getActivity());
    llm.setOrientation(LinearLayoutManager.VERTICAL);
    recycleList.setLayoutManager(llm);


   swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {


            movieListAdaptor.clear();
            new Send_data_to_server().execute(MOVIES_POST_REQUEST);
            swipeContainer.setRefreshing(false);
        }
    }); 
    new Send_data_to_server().execute(MOVIES_POST_REQUEST);
    return v;
}

问题是,我在 swipeContainer.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {...} 方法中遇到 NULL 指针异常,我猜这是因为滑动刷新布局位于主 activity XML,而不是制表符片段。那么实现这个的正确方法是什么?我还尝试在其中一个选项卡 XML 中实施滑动以刷新布局,而不是像上面那样将 ViewPager 包装在其中,但是从选项卡滑动到另一个选项卡时它会崩溃。

这是 Tab1 中整个片段的代码,下面是 tobs 的答案

public class MoviesTabFragment extends Fragment implements Refreshable {


public MovieListAdapter movieListAdaptor;
public RecyclerView recycleList;
//private SwipeRefreshLayout swipeContainer;

public List<MovieListAdapter.MovieDetails> movieList = new ArrayList<MovieListAdapter.MovieDetails>();

public ProgressBar progressBar;
public final static String MOVIES_POST_REQUEST ="";


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

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

    recycleList = (RecyclerView) v.findViewById(R.id.cardList);

    progressBar = (ProgressBar) v.findViewById(R.id.progress_bar);

    LinearLayoutManager llm = new LinearLayoutManager(getActivity());
    llm.setOrientation(LinearLayoutManager.VERTICAL);
    recycleList.setLayoutManager(llm);

    new Send_data_to_server().execute(MOVIES_POST_REQUEST);
    return v;
}

@Override
public void refresh() {
    new Send_data_to_server().execute(MOVIES_POST_REQUEST);
}


public class Send_data_to_server extends AsyncTask<String, Void, String> {

    private String data_poster;
    private String data_fanart;

 //   protected void onPreExecute() {
   //     progressBar.setVisibility(View.VISIBLE);
 //   }

    protected String doInBackground(String... params)
    {
        String jason_data = params[0];
        HttpClient http_con = new HttpClient();
        String output_from_server = http_con.establish_con(jason_data);
        Log.i("DataFromServer", output_from_server);

        JSONObject json_Obj = null;
        JSONObject child_obj = null; //creating the "result" object in the main JSON Object
        try {
            json_Obj = new JSONObject(output_from_server);
            child_obj = create_subObject("result", json_Obj);
            JSONArray jsonArray = child_obj.optJSONArray("movies");

            for (int i = 0; i < jsonArray.length(); i++) {

                JSONObject jsonObject = jsonArray.getJSONObject(i);
                String title_data = jsonObject.optString("label").toString();
                String plot_data = jsonObject.optString("plot").toString();
                String year_data =  jsonObject.optString("year").toString();
                String movie_id_data = jsonObject.optString("movieid").toString();
                String imdb_score = jsonObject.optString("rating").toString();
                String imdb_score_short = imdb_score.substring(0, 3);

                JSONObject child_obj2 = create_subObject("art", jsonObject);

                data_poster = child_obj2.optString("poster").toString();
                data_fanart = child_obj2.optString("fanart").toString();

                JSONEncodePosterFanart encodePosterFanart = new JSONEncodePosterFanart();

                String jason_dataPoster = encodePosterFanart.GetPosterFanartEncodedURL(data_poster);
                String jason_dataFanart = encodePosterFanart.GetPosterFanartEncodedURL(data_fanart);

                HttpClient http = new HttpClient();
                String output_from_serverPoster = http.establish_con(jason_dataPoster);

                HttpClient http2 = new HttpClient();
                String output_from_serverFanart = http2.establish_con(jason_dataFanart);

                JSONPosterFanart poster_fanart = new JSONPosterFanart();
                String post_dl = poster_fanart.GetPosterFanart(output_from_serverPoster);

                JSONPosterFanart poster_fanart2 = new JSONPosterFanart();
                String fanart_dl = poster_fanart2.GetPosterFanart(output_from_serverFanart);

                if (null == movieList) {
                    movieList = new ArrayList<MovieListAdapter.MovieDetails>();
                }

                MovieListAdapter.MovieDetails item = new MovieListAdapter.MovieDetails(title_data+" ("+year_data+")", post_dl, fanart_dl,plot_data,movie_id_data,imdb_score_short);
                movieList.add(item);
            }

        } catch (JSONException e) {
            e.printStackTrace();
        }
        return output_from_server;
    }
    protected void onPostExecute(String output_from_server) {

        super.onPostExecute(output_from_server);
        //progressBar.setVisibility(View.INVISIBLE);
        movieListAdaptor = new MovieListAdapter(getActivity(), movieList);
        recycleList.setAdapter(movieListAdaptor);
    }

    private  JSONObject create_subObject(String tagName, JSONObject jObj) throws JSONException {
        JSONObject subObj = jObj.getJSONObject(tagName); //getJSONObject returns the value from tagName (in our case jason_Obj that is being passed ar a param)
        return subObj;
    }

}

}

和 RecycleView 适配器:

 public class MovieListAdapter extends RecyclerView.Adapter<MovieListAdapter.MovieViewHolder> {

public List<MovieDetails> movieList;
private Context mContext;

public MovieListAdapter(Context mContext, List<MovieDetails> movieList) {

    this.mContext = mContext;
    this.movieList = movieList;
}


@Override
public int getItemCount() {

    return movieList.size();
}

public void clear() {
    movieList.clear();
    notifyDataSetChanged();
}



@Override
public MovieViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
    View itemView = LayoutInflater.
            from(viewGroup.getContext()).
            inflate(R.layout.card_layout_movies_list, viewGroup, false);
    return new MovieViewHolder(itemView);
}

@Override
public void onBindViewHolder(MovieViewHolder movieViewHolder, int i) {


    MovieDetails mdet = movieList.get(i);

    String fanart = "http://192.168.1.128/"+mdet.getImageViewFanart();
    String poster = "http://192.168.1.128/"+mdet.getImageViewPoster();

    Log.i("fanart", fanart);
    Log.i("poster", poster);

    movieViewHolder.vTitle.setText(mdet.Title);

    Picasso.with(mContext).load(poster)
            .resize(500, 746)
            .error(R.drawable.poster_placeholder)
            .placeholder(R.drawable.poster_placeholder)
            .into(movieViewHolder.vPoster);
    Picasso.with(mContext).load(fanart)
            .resize(960, 540)
            .error(R.drawable.fanart_placeholder)
            .placeholder(R.drawable.fanart_placeholder)
            .into(movieViewHolder.vFanart);

    movieViewHolder.vplot = mdet.getPlot();
    movieViewHolder.vmovie_id = mdet.getMovie_id();


}

public  class MovieViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    protected TextView vTitle;
    protected ImageView vPoster;
    protected ImageView vFanart;
    protected String vplot;
    protected String vmovie_id;
    protected String vimdb_score;

    public MovieViewHolder(View v)
    {
        super(v);
        vplot = new String();
        vmovie_id = new String();
        vimdb_score = new String();
        vTitle  = (TextView)  v.findViewById(R.id.title);
        vPoster = (ImageView) v.findViewById(R.id.imageViewPoster);
        vFanart = (ImageView) v.findViewById(R.id.imageViewFanart);
        v.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int position = getLayoutPosition();
        MovieDetails mov = movieList.get(position);

        Intent intent = new Intent(mContext, MovieDetailsPageActivity.class);

        Bundle bundle = new Bundle();
        bundle.putString("movieid", mov.getMovie_id());
        bundle.putString("plot", vplot);
        bundle.putString("fanart_path", mov.getImageViewFanart());
        bundle.putString("imdb_score", mov.getImdb_score());
        intent.putExtras(bundle);
        mContext.startActivity(intent);

    }
}


public static class MovieDetails {

    protected String Title;
    protected String imageViewPoster;
    protected String imageViewFanart;
    protected String plot;
    protected String movie_id;
    protected String imdb_score;

    public MovieDetails(String Title, String imageViewPoster,String imageViewFanart, String plot, String movie_id ,String imdb_score)
    {
        this.Title = Title;
        this.imageViewPoster = imageViewPoster;
        this.imageViewFanart = imageViewFanart;
        this.plot = plot;
        this.movie_id = movie_id;
        this.imdb_score = imdb_score;
    }

    public String getTitle() {return Title;}

    public String getImageViewPoster() {
        return imageViewPoster;
    }

    public String getImageViewFanart() {
        return imageViewFanart;
    }

    public String getPlot() {return plot;}

    public String getMovie_id() {return movie_id;}

    public String getImdb_score() {return imdb_score;}

}

}

 public class ViewPagerAdapter extends FragmentStatePagerAdapter {

CharSequence Titles[]; 
int NumbOfTabs; 


public ViewPagerAdapter(FragmentManager fm,CharSequence mTitles[], int mNumbOfTabsumb) {
    super(fm);

    this.Titles = mTitles;
    this.NumbOfTabs = mNumbOfTabsumb;

}


@Override
public Fragment getItem(int position) {

    if(position == 0) 
    {
        MoviesTabFragment moviesTabFragment = new MoviesTabFragment();
        return moviesTabFragment;
    }
    else             
    {
        TVShowsTabFragment TVShowsTabFragment = new TVShowsTabFragment();
        return TVShowsTabFragment;
    }

}


@Override
public CharSequence getPageTitle(int position) {
    return Titles[position];
}



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

您得到 NullPointerException 是因为您从 R.layout.tab_1 扩展了不包含 SwipeRefreshLayout.

的片段布局

如果您希望布局成为 ViewPager 的父布局,我建议您将管理 RefreshLayout 的代码移动到 MainActivity:

public class MovieListActivityTEST extends AppCompatActivity {

ViewPager pager;
ViewPagerAdapter adapter;
SwipeRefreshLayout refreshLayout;
SlidingTabLayout tabs;
CharSequence Titles[]={"Home","Events"};
int Numboftabs =2;

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

    // Creating The ViewPagerAdapter and Passing Fragment Manager, Titles fot the Tabs and Number Of Tabs.
    adapter =  new ViewPagerAdapter(getSupportFragmentManager(),Titles,Numboftabs);

    // Assigning ViewPager View and setting the adapter
    pager = (ViewPager) findViewById(R.id.pager);
    pager.setAdapter(adapter);

    // Assiging the Sliding Tab Layout View
    tabs = (SlidingTabLayout) findViewById(R.id.tabs);
    tabs.setDistributeEvenly(true); 

    // Setting Custom Color for the Scroll bar indicator of the Tab View
    tabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
        @Override
        public int getIndicatorColor(int position) {
            return getResources().getColor(R.color.tabsScrollColor);
        }
    });

    // Setting the ViewPager For the SlidingTabsLayout
    tabs.setViewPager(pager);

    // Assign your refresh layout
    refreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeContainer);

    refreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            Refreshable r = (Refreshable) adapter.getItemAt(pager.getCurrentItem());
            r.refresh();
        }
    }); 
}

您的每个选项卡片段都实现了一个 Refreshable 接口:

public interface Refreshable {
    void refresh();
}

并且您的适配器跟踪所有片段:

public class ViewPagerAdapter extends FragmentStatePagerAdapter {
    // list that keeps references to all attached Fragments
    private SparseArray<Fragment> pages = new SparseArray<>();
    ...

    public Fragment getItem(int position) {
        Fragment f;
        if(position == 0) {
            ...
        } else { ... }

        // add fragment to the list
        pages.put(position, f);
    }

     public void destroyItem(ViewGroup container, int position, Object object) {
        // remove fragment from list if it existed
        if(pages.indexOfKey(position) >= 0) {
            pages.remove(position);
        }

        super.destroyItem(container, position, object);
    }

    // return the attached Fragment that is associated with the given position
    public Fragment getItemAt(int position) {
        return pages.get(position);
    }
}