【问题标题】:Sliding Tabs with RecycleView Fragments and Swipe to Refresh带有 RecycleView 片段的滑动选项卡和滑动刷新
【发布时间】:2015-12-17 11:05:24
【问题描述】:

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

首先是我的 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 ,两个标签之一(两者相似,所以我只发布一个)

<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>

现在,我的主要活动是处理 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() {...} 方法中得到一个空指针异常,我猜这是因为刷卡刷新布局在主要活动XML 中,而不是标签片段中。那么实现这一点的正确方法是什么?我还尝试在其中一个 Tabs XML 中实现 Swipe 以刷新布局,而不是像上面那样将 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;
}

【问题讨论】:

    标签: java android android-viewpager android-recyclerview swiperefreshlayout


    【解决方案1】:

    您获得了NullPointerException,因为您从不包含SwipeRefreshLayoutR.layout.tab_1 扩充了片段布局。

    如果您希望布局成为 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);
        }
    }
    

    【讨论】:

    • 非常感谢,尝试了你的想法,创建了一个 Refreshable 接口,并从第一个选项卡中的接口实现了 refresh method() 以进行测试。但是当它执行 Send_data_to_server().execute(MOVIES_POST_REQUEST) 封闭类时,我得到了一个 NULL 指针异常,这是一个与我的服务器对话的 AsyncTask,从中检索 JSON 数据,并使用 CardList 设置 RecycleView 的适配器。错误正是在我设置 RecycleView 适配器的部分。我在下面的答案中添加了选项卡片段的整个代码(因为您无法在 cmets 中正确格式化代码
    • RecicleView 的适配器的创建是在'OnPostExecute' 方法中,在类的末尾,顺便说一下。也许它与创建适配器时使用的 getActivity() 有关,因为根据我有限的知识,在 Fragments 的情况下,它可以在某些情况下返回 NULL。
    • 是的,只是在刷新方法中添加了if(getActivity()==null){Log.i("Activity Status","Its NULL");}`,根据日志,它是NULL。还在第一个问题中添加了整个代码并删除了答案我添加的
    • 我尝试使用 onAttach 方法 public void onAttach(Activity activity) { super.onAttach(activity); context=activity; } 并在适配器创建方法中使用 getContext() 添加上下文,但仍然是同样的问题。
    • 完成,在我的问题末尾添加了 RecycleView 适配器代码。作为旁注,当我使用活动而不是片段时代码工作正常(在我实现滑动选项卡之前,所以没有片段)他初始化和刷新,只有在使用 public void refresh 方法时才会出现问题片段(当从 onCreateView 调用 Send_data_to_server().execute(MOVIES_POST_REQUEST) 时,它也可以正常工作)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 1970-01-01
    相关资源
    最近更新 更多