I am trying to display a RecyclerView on a fragment using FireBase Database, but for some unknown reason its giving me this error: E/RecyclerView: No adapter attached; Im pretty sure im actually attaching the adapter, here's the code:
Main Activity
public class MainActivity extends AppCompatActivity {
Fragment currentFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(savedInstanceState == null){
currentFragment = new MapsFragment();
changeFragment(currentFragment);
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu,menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()){
case R.id.menu_bookmarkList:
currentFragment = new ListaFragment();
break;
case R.id.menu_mapa:
currentFragment = new MapsFragment();
break;
}
changeFragment(currentFragment);
return super.onOptionsItemSelected(item);
}
private void changeFragment(Fragment currentFragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container,currentFragment).commit();
}
}
ListaFragment (the one that should display the Recycler):
public class ListaFragment extends Fragment implements MyAdapter.RecyclerItemClick{
private MyAdapter myAdapter;
private RecyclerView recycler;
FirebaseDatabase database;
DatabaseReference myRef;
public ListaFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); }
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_lista, container, false);
database = FirebaseDatabase.getInstance();
myRef = database.getReference("Marcador");
recycler = (RecyclerView) v.findViewById(R.id.recyler);
recycler.setLayoutManager(new LinearLayoutManager(getContext()));
FirebaseRecyclerOptions<Marcador> options = new FirebaseRecyclerOptions.Builder<Marcador>()
.setQuery(myRef, Marcador.class).build();
myAdapter = new MyAdapter(options,this);
recycler.setAdapter(myAdapter);
return v;
}
#Override
public void onStart(){
super.onStart();
myAdapter.startListening();
}
#Override
public void onStop() {
super.onStop();
myAdapter.stopListening();
}
#Override
public void itemClick(Marcador marcador) {
}
}
And the adapter i've done (im skipping the xml's since they are quite simple):
public class MyAdapter extends FirebaseRecyclerAdapter<Marcador,MyAdapter.MarcadorHolder> {
private Context context;
private RecyclerItemClick itemClick;
public MyAdapter(#NonNull FirebaseRecyclerOptions<Marcador> options, RecyclerItemClick itemClick) {
super(options);
this.itemClick = itemClick;
}
#Override
protected void onBindViewHolder(#NonNull final MarcadorHolder holder, int position, #NonNull Marcador model) {
final Marcador marcador = getItem(position);
holder.textViewNom.setText(model.getNom());
holder.textViewLatitude.setText(String.valueOf(model.getLatitude()));
holder.textViewLongitude.setText(model.getLongitude());
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
itemClick.itemClick(marcador);
}
});
}
#NonNull
#Override
public MarcadorHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recylcer_view_item,parent,false);
context = parent.getContext();
return new MarcadorHolder(v);
}
public class MarcadorHolder extends RecyclerView.ViewHolder{
TextView textViewNom;
TextView textViewLatitude;
TextView textViewLongitude;
public MarcadorHolder(#NonNull View itemView) {
super(itemView);
textViewNom = itemView.findViewById(R.id.textViewNom);
textViewLatitude = itemView.findViewById(R.id.textViewLatitud);
textViewLongitude = itemView.findViewById(R.id.textViewLongitut);
}
}
public interface RecyclerItemClick {
void itemClick(Marcador marcador);
}
}
I hope someone can give me an answer of why this is happening, since i've checked other options and i can't really get through it.
XML's attached of fragment:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context=".fragments.ListaFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyler"
android:layout_width="match_parent"
android:layout_height="545dp" />
</LinearLayout>
XML attached of item of recycler view:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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="60dp" android:background="#CDCDCD" android:layout_marginBottom="5dp"
>
<TextView
android:id="#+id/textViewNom"
android:layout_width="410dp"
android:layout_height="55dp"
android:layout_marginLeft="15dp"
android:layout_marginTop="5dp"
android:text="Title"
android:textSize="20sp"
android:textStyle="bold" />
<TextView
android:id="#+id/textViewLatitud"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginTop="35dp"
android:text="latitud" />
<TextView
android:id="#+id/textViewLongitut"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="125dp"
android:layout_marginTop="35dp"
android:text="longitut" />
<ImageView
android:id="#+id/imageView"
android:layout_width="45dp"
android:layout_height="45dp"
android:layout_marginLeft="265dp"
android:layout_marginTop="10dp"
/>
</RelativeLayout>
Do use below lines
LinearLayoutManager layoutManager = new LinearLayoutManager(getContext());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recycler.setLayoutManager(layoutManager);
recycler.setAdapter(adapter);
According to Fragment API Reference:
It is recommended to only inflate the layout in this method and move logic that operates on the returned View to onViewCreated(View, Bundle).
So in onCreateView you should only inflate your layout hence the View v = inflater.inflate(R.layout.fragment_lista, container, false); you used and move logic of your code to onViewCreated and the reason you facing E/RecyclerView: No adapter attached; is because you're trying to setting adapter for your recyclerview before even creation/inflation of the view which is failing to complete therefore after the creation/inflation is done there's no adapter attached to recyclerview.
It is generally recommended to not initialize the component using findViewById in onViewCreated() as per the The Google Developer Documents.
You should inflate your layout in onCreateView but shouldn't
initialize other views using findViewById in onCreateView.
Because in this method not every View is not Properly initialized. So, prefer to use onViewCreated() to get your view's Id and do setUp the UI.
Related
I copied the following code from the MainActivity to a separate fragment, but I can't get findViewById to work:
I get "cannot resolve method findViewById(int)"
these are the related files:
**Also as a beginner, could you let me know if there's a general problem with my code that needs to fixed?
MyFragment.java:
public class MyFragment extends Fragment {
public myFragment() {
// Required empty public constructor
}
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_my, container, false);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
//placeholder data
String[] myDataset = new String[16];
myDataset[0] = "Data0";
myDataset[1] = "Data1";
myDataset[2] = "Data2";
myDataset[3] = "Data3";
myDataset[4] = "Data4";
myDataset[5] = "Data5";
myDataset[6] = "Data6";
myDataset[7] = "Data7";
myDataset[8] = "Data8";
myDataset[9] = "Data9";
myDataset[10] = "Data10";
myDataset[11] = "Data11";
myDataset[12] = "Data12";
myDataset[13] = "Data13";
myDataset[14] = "Data14";
myDataset[15] = "Data15";
// specify an adapter (see also next example)
mAdapter = new MyAdapter(myDataset);
mRecyclerView.setAdapter(mAdapter);
}
}
MyAdapter.java:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private String[] mDataset;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class MyViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public TextView upTv;
public TextView downTv;
public View layout;
public MyViewHolder(View v) {
super(v);
layout = v;
upTv = (TextView)v.findViewById(R.id.upTv);
downTv = (TextView)v.findViewById(R.id.downTv);
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(String[] myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.my_text_view, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
holder.upTv.setText(mDataset[position]);
holder.downTv.setText(mDataset[position]);
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
}
fragment_my.xml:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:scrollbars="vertical"/>
</android.support.constraint.ConstraintLayout>
my_text_view.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:padding="6dip" >
<ImageView
android:id="#+id/icon"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentTop="true"
android:layout_marginRight="6dip"
android:contentDescription="TODO"
android:src="#drawable/ic_launcher_background" />
<TextView
android:id="#+id/downTv"
android:layout_width="fill_parent"
android:layout_height="26dip"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_toRightOf="#id/icon"
android:text="downTv"
android:textSize="12sp" />
<TextView
android:id="#+id/upTv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:layout_toRightOf="#id/icon"
android:gravity="center_vertical"
android:text="upTv"
android:textSize="16sp" />
</RelativeLayout>
Here's your problem:
return inflater.inflate(R.layout.fragment_my, container, false);
You cannot add more code after the return statement. You will need to take the reference of the inflated view and use it to find the reference of child views.
View rootView = inflater.inflate(R.layout.fragment_my, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view);
// other code
return rootView;
A really good way to use fragments is to initialize certain variables(e.g- activity,context,root view etc) associated with the parent activity when you switch to a fragment.
for example you can do sth like,
private Context context;
private MainActivity activity; //Let MainActivity is your parent activity
private View view; //fields kept inside the fragment class,now we need to keep them initialized
//initialize activity/context from onAttach
#Override
public void onAttach(Context c) {
super.onAttach(c);
Activity a;
context=c;
if (c instanceof Activity){
a=(Activity) c;
if(a instanceof MainActivity)
activity=(MainActivity) a;
}
}
//initialize view from onViewCreated
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
this.view=view;
}
now you can use them wherever u want inside the fragment class without triggering any nullptrs and other stuff
e.g- you can go
this.view.findViewById(your_resId);
this.activity.getSupportFragmentManager();
etc and a lot of other stuffs when you need ,using these fields you initialized
also specifically in this case of using recyclerView dont forget to call,
adapter.notifyDataSetChanged() whenever you think the list you are showing in the recyclerView went through some change.
I do not have enough reputation to comment so i am writing this as answer to your comment.
You need to notify adapter using below code.
mAdapter.notifyDataSetChanged();
I saw there are similar post but my code is the same as the codes in the solutions so they weren't useful in my case. My app starts but and two tabs are shown. However the one that is supposed to show the items from a RecyclerView that's in it is empty.I also get this error:
E/RecyclerView: No adapter attached; skipping layout
So I have an Activity with a TabLayout and a ViewPager
public class MainActivity extends AppCompatActivity {
TabLayout tabLayout;
ViewPager viewPager;
PagerAdapter pagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tabLayout=findViewById(R.id.tab_layout);
viewPager=findViewById(R.id.view_pager);
tabLayout.addTab(tabLayout.newTab().setText("CitiesFragment"));
tabLayout.addTab(tabLayout.newTab().setText("My CitiesFragment"));
tabLayout.setTabGravity(TabLayout.GRAVITY_FILL);
pagerAdapter=new PagerAdapter(getSupportFragmentManager(),tabLayout.getTabCount());
viewPager.setAdapter(pagerAdapter);
viewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
}
This is a fragment with only a RecyclerView in it
public class CitiesFragment extends Fragment {
RecyclerView recyclerView;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View itemView = inflater.inflate(R.layout.cities_layout, container, false);
recyclerView = itemView.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
List<City> data = Database.getDatabase();
CitiesAdapter adapter = new CitiesAdapter(data);
recyclerView.setAdapter(adapter);
return itemView;
}
}
This is the XML for the fragment
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
And this is the Adapter
public class CitiesAdapter extends RecyclerView.Adapter<CitiesVIewHolder> {
List<City> data;
private int itemCount;
public CitiesAdapter(List<City> data) {
this.data = data;
itemCount=data.size();
}
#NonNull
#Override
public CitiesVIewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.city_layout, viewGroup, false);
CitiesVIewHolder viewHolder = new CitiesVIewHolder(itemView);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull CitiesVIewHolder citiesVIewHolder, int position) {
City city = data.get(position);
citiesVIewHolder.txtCityName.setText(city.getCityName());
citiesVIewHolder.txtCityInfo.setText(city.getCityInfo());
LoadImageTask loadImageTask = new LoadImageTask(citiesVIewHolder.imgCity);
loadImageTask.execute(city.getImageUrl());
}
#Override
public int getItemCount() {
return itemCount;
}
}
And the XML for the Adapter
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/img_city"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/txt_city_name"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="#+id/txt_city_info"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
My PagerAdapter only returns one of the two fragments(one of them is currently empty and the other on is with the RecyclerView)
public class PagerAdapter extends FragmentStatePagerAdapter {
private int itemCount;
public PagerAdapter(FragmentManager fm, int itemCount) {
super(fm);
this.itemCount = itemCount;
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new CitiesFragment();
case 1:
return new MyCitiesFragment();
default:
return null;
}
}
#Override
public int getCount() {
return itemCount;
}
}
I am sorry for the code overload but I really can not understand what I'm doing wrong. Thanks for the help in advance.
First of all your adapter xml root LinearLayout has height set to match_parent so change that to wrap_content.
Second one override onViewCreated and there set recyclerView not in onCreateView. For example:
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
recyclerView = view.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setHasFixedSize(true);
List<City> data = Database.getDatabase();
CitiesAdapter adapter = new CitiesAdapter(data);
recyclerView.setAdapter(adapter);
}
Also consider adding line: recyclerView.setHasFixedSize(true);
E/RecyclerView: No adapter attached; skipping layout
TabLayout display fragment with recyclerView but fragment takes more time to load datasource that makes recycler created without adapter attached.
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View itemView = inflater.inflate(R.layout.cities_layout, container, false);
recyclerView = itemView.findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
// List<City> data = Database.getDatabase(); // Don't fetch data on Main (UI) Thread
CitiesAdapter adapter = new CitiesAdapter(new ArrayList<City>()); //
recyclerView.setAdapter(adapter);
// Display city here one background and then update adapter.
return itemView;
}
If Datasource fetch data from local database then you can just use handler or postDelay on RecyclerView
recyclerView.postDelay(new Runnable(){
public void run(){
adapter.setCityList(result);
}
},500);
If you load data for server datasource the use background task, You can check this answer for fetch data from server with smooth way.
// load datasource inside Async Task.
new AsyncTaskHandler(new OnFinishCallback() {
#Override
public void onSuccess(List<City> result) {
adapter.setCityList(result);
}
}).execute("param that need to load datasource");
I have create a simple app to simulate your case.
RecyclerView width must be match_partent
<!--RecyclerView width must be match_parent-->
<android.support.v7.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content">
You can find the complete sample/CityPreview app on Github
I am doing a sample project as an excercise and want to display two fragments in the same activity when dealing with tablets(7' and 10').
So what I have so far is this.
As you can see I can display the data of my recyclerview in the left (static) fragment. However the right fragment is empty.
So I have two questions.
1) How to display by default in the right fragment the data of the first row of recyclerview?(ie image and article)
2) How to implement the click listener and update the right fragment?
Here is my code:
MainActivity
public class MainActivity extends AppCompatActivity {
private boolean mTwoPane;
private static final String DETAIL_FRAGMENT_TAG = "DFTAG";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(findViewById(R.id.detailed_match_reports)!=null) {
mTwoPane = true;
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.detailed_match_reports, new DetailedActivityFragment(),DETAIL_FRAGMENT_TAG)
.commit();
}else{
mTwoPane = false;
}
}
}
}
layout/activity_main.xml
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/match_reports"
android:name="theo.testing.androidservices.fragments.MainActivityFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
tools:context="theo.testing.androidservices.activities.MainActivity"
tools:layout="#android:layout/list_content" />
layout-sw600dp/activity_main
<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:baselineAligned="false"
android:divider="?android:attr/dividerHorizontal"
android:orientation="horizontal"
tools:context="theo.testing.androidservices.activities.MainActivity">
<fragment
android:id="#+id/match_reports"
android:name="theo.testing.androidservices.fragments.MainActivityFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2"
tools:layout="#android:layout/list_content" />
<FrameLayout
android:id="#+id/detailed_match_reports"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="4" />
</LinearLayout>
MainActivityFragment
public class MainActivityFragment extends Fragment {
public static final String TAG = "AelApp";
public static ArrayList<MyModel> listItemsList;
RecyclerView myList;
public static MatchReportsAdapter adapter;
public MainActivityFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
updateMatchReport();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
getActivity().setTitle("Match Report");
View rootView = inflater.inflate(R.layout.fragment_main_activity, container, false);
listItemsList = new ArrayList<>();
myList = (RecyclerView)rootView.findViewById(R.id.listview_match_reports);
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity());
myList.setHasFixedSize(true);
myList.setLayoutManager(linearLayoutManager);
adapter = new MatchReportsAdapter(getActivity(), listItemsList);
myList.setAdapter(adapter);
return rootView;
}
public void updateMatchReport(){
Intent i = new Intent(getActivity(), MatchReport.class);
getActivity().startService(i);
}
}
First let me answer the second question. To show a fragment on the right side of the screen add something like this (note how I'm using that id of the frame layout to replace the fragment):
Fragment fragment = new MyRightSideFragment();
FragmentManager fm = getFragmentManager();
fm.beginTransaction().replace(R.id.detailed_match_reports, fragment).commit();
Now, for the first question, you need to implement click listener, you can find an example here.
public class ReactiveAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = (Integer)v.getTag();
showDetail(position);
}
});
}
}
Notice that I'm using the tag of the view to identify the position (so somwhere in your onBindViewHolder code you must set this tag).
public function showDetail(int position) {
... show fragment por position
}
and finally, when your in your OnCreateView or somewhere in your setup code, call showDetail(0).
public class ReferralStatus extends Fragment {
public MyPendingAdapter pAdapter;
public RecyclerView recyclerView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View layout= inflater.inflate(R.layout.fragment_referral_status, container, false);
recyclerView = (RecyclerView) layout.findViewById(R.id.pendingRecyclerList);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
return layout;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
PendingAsyncTask pendingRunner = new PendingAsyncTask();
pendingRunner.execute("someURL");
}
public class MyPendingAdapter extends RecyclerView.Adapter<MyPendingAdapter.PendingUserHolder> {
Context context;
String[] pendingUserArray, socialSitesArray;
public MyPendingAdapter(Context c, String[] pendingUserList, String[] socialSites) {
Log.d("golu", "pendingConstructor");
this.context=c;
this.pendingUserArray=pendingUserList;
this.socialSitesArray=socialSites;
}
public class PendingUserHolder extends RecyclerView.ViewHolder{
TextView pendingUsername;
TextView socialSite;
public PendingUserHolder(View v){
super(v);
pendingUsername = (TextView) v.findViewById(R.id.pendingUsername);
socialSite= (TextView) v.findViewById(R.id.socialSiteLabel);
}
}
#Override
public PendingUserHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Log.d("golu", "pendingHolder");
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View row = inflater.inflate(R.layout.pending_list_row,parent,false);
PendingUserHolder holder = new PendingUserHolder(row);
return holder;
}
#Override
public void onBindViewHolder(PendingUserHolder holder, int position) {
holder.pendingUsername.setText(pendingUserArray[position]);
holder.socialSite.setText(socialSitesArray[position]);
}
#Override
public int getItemCount() {
Log.d("golu", "pendingCount");
return pendingUserArray.length;
}
}
public class PendingAsyncTask extends AsyncTask<String,Void,Boolean>{
String [] pendingUserList, socialSitesMedium;
#Override
protected Boolean doInBackground(String... params) {
//just returning simple boolean value
return true;
}
#Override
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if(aBoolean)
{
//Hardcoding Arrays for now
pendingUserList=getResources().getStringArray(R.array.pendingUserList);
socialSitesMedium=getResources().getStringArray(R.array.socialSites);
pAdapter = new MyPendingAdapter(getActivity(),pendingUserList,socialSitesMedium);
recyclerView.setAdapter(pAdapter);
}
}
}
//Here is the XML file
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/pending"
android:id="#+id/pendingLabel"
android:textColor="#FF0000"
android:textAllCaps="true"
android:textStyle="bold"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:layout_marginLeft="10dp" />
<android.support.v7.widget.RecyclerView
android:id="#+id/pendingRecyclerList"
android:layout_width = "match_parent"
android:layout_height = "match_parent">
</android.support.v7.widget.RecyclerView>
</LinearLayout>
//Its showing the Skipping layout error ....What am I doing wrong?
//FYI: I tried initialising RecyclerView and setting the LinearLayoutManager in onPostExecute....it still didnt work :/
The problem lies in OnCreateView method
You need to initialise the adapter there and set the adapter to the recycler view.
As the adapter will pass no data you can update the display later.
As the onCreateView Method is the method which updates the display,so its important to initialise it else it will give an error "no adapter attached skipping display"
What am I doing wrong?
RecyclerView (and also ListView) requires Adapter to be able to work, because adapter is the way these widgets gets the data to display from. You have adapter in your code but you lack the code that tells RecyclerView to use it: setAdapter().
See https://developer.android.com/training/material/lists-cards.html for more details
so, before latest update, I use onListItemClick listener and it works fine, but now I tried to use RecyclerView, and I'm not sure how to implement onClick for each item, that will open up a new activity..
this is what I used to have
public class SermonsFragment extends Fragment {
#Override
public void onListItemClick(ListView list, View v, int position, long id) {
Intent mediaStreamIntent = new Intent(getActivity(), MediaStreamPlayer.class);
mediaStreamIntent.putExtra("sermon_details", (android.os.Parcelable) list.getItemAtPosition(position));
startActivity(mediaStreamIntent);
}
}
but now, instead of using listview I create a sermon adapter and it looks like this
public class SermonListAdapter extends RecyclerView.Adapter<SermonListAdapter.ViewHolder>{
private ArrayList<Sermon> mDataset;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
//Note: need to remove static class no idea why
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
// each data item is just a string in this case
public View mView;
public ViewHolder(View v) {
super(v);
v.setOnClickListener(this);
mView = v;
}
#Override
public void onClick(View v) {
Log.d("SermonsListAdapter.java.debug", "itemClick " + mDataset.get(getPosition()).getName());
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public SermonListAdapter(ArrayList<Sermon> myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public SermonListAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.sermon_cardview, parent, false);
// set the view's size, margins, paddings and layout parameters
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
// - get element from your dataset at this position
// - replace the contents of the view with that element
TextView title = (TextView) holder.mView.findViewById(R.id.sermon_title);
TextView series = (TextView) holder.mView.findViewById(R.id.sermon_series);
TextView pastor = (TextView) holder.mView.findViewById(R.id.sermon_pastor);
TextView sermonDate = (TextView) holder.mView.findViewById(R.id.sermon_date);
title.setText(mDataset.get(position).getName());
series.setText(mDataset.get(position).getSeries());
pastor.setText(mDataset.get(position).getPastor());
sermonDate.setText(mDataset.get(position).getSermonDate());
}
and the fragment is more or less the same, it's just I can't use onListItemClick anymore
public class SermonsFragment extends Fragment {
private static final int MAX_SERMONS_LIST = 20;
private ArrayAdapter<Sermon> listAdapter;
private String imageUrl;
private static String sermonListJSONUrl = “http://someurl”;
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
//Check if there is internet, if yes call JSONParser
ConnectionDetector myConnection = new ConnectionDetector(getActivity().getApplicationContext());
Boolean isInternetOnline = false;
isInternetOnline = myConnection.isConnectingToInternet();
if(isInternetOnline) {
//Call JSONParser Asynchronously to get sermonList in JSON Format
new callJSONParserAsync().execute(sermonListJSONUrl);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_sermons, container, false);
mRecyclerView = (RecyclerView) rootView.findViewById(R.id.my_recycler_view);
return rootView;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mLayoutManager = new LinearLayoutManager(getActivity());
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
//Just an Empty Class
ArrayList<Sermon> mySermon = new ArrayList<Sermon>();
//specify an adapter
mAdapter = new SermonListAdapter(mySermon);
mRecyclerView.setAdapter(mAdapter);
}
I have the cardview xml look like this
<!-- A CardView that contains a TextView -->
<android.support.v7.widget.CardView
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:id="#+id/card_view"
android:layout_margin="5dp"
android:layout_width="match_parent"
android:layout_height="100dp"
card_view:cardCornerRadius="1dp">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="#+id/sermon_title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/sermon_series" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/sermon_pastor" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/sermon_date" />
</LinearLayout>
</android.support.v7.widget.CardView>
I've got this error when try to create new intent
12-18 22:31:48.469 31887-31887/org.ifgfseattle.ifgfseattle E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: org.ifgfseattle.ifgfseattle, PID: 31887
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
at android.app.ContextImpl.startActivity(ContextImpl.java:1232)
at android.app.ContextImpl.startActivity(ContextImpl.java:1219)
at android.content.ContextWrapper.startActivity(ContextWrapper.java:322)
at org.ifgfseattle.ifgfseattle.adapter.SermonListAdapter$1.onClick(SermonListAdapter.java:81)
at android.view.View.performClick(View.java:4756)
at android.view.View$PerformClick.run(View.java:19749)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
You could implement an onClick on the view in the onBindViewHolder method of yours inside the adpater.
Assign an id to the view that holds the item cell
Get the view just the way you have for the textviews
set an onClick to the root inside the method like this:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
viewHolder.relLayout.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
// perform your operations here
}
});
}
EDIT:
This is how you assign an id in the xml
<LinearLayout android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/lnrLayout" ---------->> This is new
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:id="#+id/sermon_title" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/sermon_series" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/sermon_pastor" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceSmall"
android:id="#+id/sermon_date" />
</LinearLayout>
This is how you define the views (or probably instantiate them)
public ViewHolder(View mView) {
super(view);
title = (TextView) holder.mView.findViewById(R.id.sermon_title);
series = (TextView) holder.mView.findViewById(R.id.sermon_series);
pastor = (TextView) holder.mView.findViewById(R.id.sermon_pastor);
sermonDate = (TextView) holder.mView.findViewById(R.id.sermon_date)
lnrLayout = (LinearLayout)holder.mView.findViewById(R.id.lnrLayout);
}
That's your custom viewholder, so declare the TextViews just the way we declare variables.. your onBindView method wil therefore look like this now:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.title.setText(mDataset.get(position).getName());
holder.series.setText(mDataset.get(position).getSeries());
holder.pastor.setText(mDataset.get(position).getPastor());
holder.sermonDate.setText(mDataset.get(position).getSermonDate());
holder.lnrLayout.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
// on click action here
//-- use context to start the new Activity
Intent mediaStreamIntent = new Intent(mContext, MediaStreamPlayer.class);
mediaStreamIntent.putExtra("sermon_details", (android.os.Parcelable) mDataset.get(position));
mContext.startActivity(mediaStreamIntent);
}
});
}
I really have no idea why there is difference between the two, may be its because you are intializing the views inside onbind instead of the viewholder constructor.
You could also refer to this
EDIT 2: (2nd method)
Change you adapter to the following:
// Provide a suitable constructor (depends on the kind of dataset)
public SermonListAdapter(ArrayList<Sermon> myDataset, Fragment fragment) {
mDataset = myDataset;
mFragment = fragment;
}
In the onClick do this:
if(mFragment != null && mFragment instanceof SermonFragment) {
((SermonFragment)mFragment).sendToNextActivity(position); -> you can pass any data you wsh to
}
In the fragment class create a public method with the name sendToNextAcitivity with the same param definition and then call the next intent.
3rd method
Create an interface in the adapter, create a set method for the interface, implement the interface in the fragment and then initialize it, and then pass it to the set method of the adapter.
then use this:
if(mListener!= null) {
mListener.sendToNextActivity(position); -> you can pass any data you wsh to
}