Android how to open fragment from listview element - android

In MainActivity is FrameLayout MainContainer. I load there a fragment TrainerMyGroups, there is a Listview where I add a few elements (each element has some strings) by use TrainerGroupsAdapter. Actually I want to replace fragment TrainerMyGroups by another (for example TrainersInfo) by click on list's element.
My TrainerGroupsAdapter is:
public class TrainerGroupsAdapter extends ArrayAdapter {
List list = new ArrayList();
public TrainerGroupsAdapter(Context context, int resource) {
super(context, resource);
}
static class Datahandler{
TextView name;
TextView when;
TextView where;
LinearLayout ll;
}
#Override
public void add(Object object) {
super.add(object);
list.add(object);
}
#Override
public int getCount() {
return this.list.size();
}
#Override
public Object getItem(int position) {
return this.list.get(position);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View row;
row=convertView;
Datahandler handler;
SharedPreferences pref = getContext().getSharedPreferences("pref", Context.MODE_PRIVATE);
if(convertView==null){
LayoutInflater inflater = (LayoutInflater) this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
row= inflater.inflate(R.layout.list_mygroups,parent,false);
handler = new Datahandler();
handler.name = (TextView) row.findViewById(R.id.trainermygroupslistname);
handler.where = (TextView) row.findViewById(R.id.trainermygroupslistwhere);
handler.when = (TextView) row.findViewById(R.id.trainermygroupslistwhen);
handler.ll=(LinearLayout) row.findViewById(R.id.trainermygroupslistlayout);
row.setTag(handler);
}
else {
handler = (Datahandler)row.getTag();
}
TrainerGroupsDataProvider dataProvider;
dataProvider = (TrainerGroupsDataProvider)this.getItem(position);
handler.name.setText(dataProvider.getName());
handler.when.setText(dataProvider.getWhen());
handler.where.setText(dataProvider.getWhere());
handler.ll.setBackgroundColor(Color.parseColor(pref.getString(TRANSP_KEY, "#CC") + dataProvider.getColor()));
handler.ll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getFragmentManager().beginTransaction().replace(R.id.MainContainer, new TrainerInfo()).addToBackStack(null).commit();
}
});
return row;
}
}
It doesn't work but method OnClick is probably good because if I replace getFragmentManager().beginTransaction().replace(R.id.MainContainer, new TrainerInfo()).addToBackStack(null).commit(); to code for change some strings (name, when tc) it works. Problem is in getFragmentMenager(), Android Studio shows message that I have to create Getter and AS's suggestion is to generate in OnClick method:
private FragmentManager fragmentManager;
public FragmentManager getFragmentManager() {
return fragmentManager;
}
then problem is in the second argument in replace, I have error that it has to be Fragment (Im sure that TrainersInfo() is fragment because I use it in other place and it works).
How can I solve this problem or what is the best way to open fragment by click on lise's element in another fragment?

#UPDATE2
Better then replacing the Fragment inside the adapter is to say your activity that it should replace the fragment. This can be done with an interface which you implement inside your Activity:
public class TrainerGroupsAdapter extends ArrayAdapter {
// The interface which you have to implement in your activity
public interface OnChangeFragmentListener {
void changeFragment();
}
List list = new ArrayList();
private OnChangeFragmentListener m_onChangeFragmentListener;
public TrainerGroupsAdapter(Context context, int resource) {
super(context, resource);
m_onChangeFragmentListener = (OnChangeFragmentListener) context;
}
// Your other code
}
The OnClickListener in your getView Method:
handler.ll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// Call the method which change the fragment
m_onChangeFragmentListener.changeFragment();
}
});
The Activity:
public class MainActivity extends AppCompatActivity implements TrainerGroupsAdapter.OnChangeFragmentListener {
//...
// Your Other code
// Implement the method which is called in the adapter and replace the fragment here
#Override
public void changeFragment() {
getFragmentManager().beginTransaction().replace(R.id.MainContainer, new TrainerInfo()).addToBackStack(null).commit();
}
}
#UPDATE1
You need an activity context for getSupportFragmentManager() and getFragmentManager().
You can change the Context parameter of the constructor to Activity and create a member variable in your class for the activity so you can use it later:
public class TrainerGroupsAdapter extends ArrayAdapter {
List list = new ArrayList();
private Activity m_activity;
public TrainerGroupsAdapter(Activity context, int resource) {
super(context, resource);
m_activity = context;
}
// Your other code
}
The OnClickListener in your getView method:
handler.ll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
m_activity.getFragmentManager().beginTransaction().replace(R.id.MainContainer, new TrainerInfo()).addToBackStack(null).commit();
}
});

Was searching for a similar answer, and got one from a good friend and android dev. I think it's the easiest one to understand and implement. SO I am usually creating a helper class in which I put my two static functions for opening fragments (as replace or dialog):
For example the replace one:
public static void openFragmentReplace(Activity activity, int idOfPlacement, Fragment fragment)
final FragmentManager fm = ((FragmentActivity)activity).getSupportFragmentManager();
final FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(idOfPlacement, fragment);
fragmentTransaction.commit();
}
and the dialog one:
public static void openFragmentAsDialog(Activity activity, DialogFragment fragment) {
final FragmentManager fm = ((FragmentActivity)activity).getSupportFragmentManager();
fragment.show(fm, "tag");
}
Then I just call it from where I want like this:
Helper.openFragmentReplace(getActivity(),R.id.PlaceForFragment,FragmentClass newFrag);
This seems like the best and easiest way to do it!
Hope it helps.

Related

Update ListView adapter in a fragment from outside the onCreateView method

I'm trying to build an app that populates ListView from JSON recievced. I'm getting the below exception when i try to update the listview using notifyDataSetChanged.
Attempt to invoke virtual method 'void
com.example.android.Wordtrend.CustomArrayAdapter.notifyDataSetChanged()'
on a null object reference
The ListView is inside a fragment. Im using the onPostExecute method to call the update method inside the fragment to update the ArrayAdapter. UI loads the dummy data provided in the onCreateView method but fails during updation.
Im a newbie, so any advice would be greatly helpful. Im confused because ArrayAdapter is null and the size of dataArray is zero inside the update method.
Activity:
public class ClassicActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activitycategory);
getSupportFragmentManager().beginTransaction().replace(R.id.container,new ClassicFragment()).commit();
}
}
Fragment:
public class ClassicFragment extends Fragment {
CustomArrayAdapter adapter;
ArrayList<dataobjects> dataArray=new ArrayList<>();
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
dataArray.clear();
dataArray.add(new dataobjects("word","Explanation",R.drawable.placeholder,R.raw.color_black));
dataArray.add(new dataobjects("word","Explanation",R.drawable.placeholder,R.raw.color_black));
dataArray.add(new dataobjects("word","Explanation",R.drawable.placeholder,R.raw.color_black));
View rootView=inflater.inflate(R.layout.word_list,container,false);
ListView listView=(ListView)rootView.findViewById(R.id.list);
adapter=new CustomArrayAdapter(getActivity(),dataArray,R.color.classic);
listView.setAdapter(adapter);
return rootView;
}
public void update(ArrayList<FinalData> e){
ArrayList<dataobjects> newDataArray=new ArrayList<>(); // update Adapter from JSON
for(int i=0;i<e.size();i++) {
FinalData FinalDataObject = e.get(i);
String word=FinalDataObject.getWord();
String meaning=FinalDataObject.getMeaning();
newDataArray.add(new dataobjects(word,meaning,R.drawable.placeholder,R.raw.color_black));
}
Log.d("Classic"," Size of Data"+dataArray.size()); // Shows zero but im not clearing it manually. Should be two.
dataArray.addAll(newDataArray); // Size changes to 7 as expected.
adapter.notifyDataSetChanged();
}
// Calling update method from the class Dataprocess
public class Data extends AsyncTask<String,Void,String> {
String Mdata;
#Override
protected void onPostExecute(String s) {
Mdata=s;
Dataprocess d=new Dataprocess();
d.process(Mdata);
super.onPostExecute(s);
}.....}
public class Dataprocess {
Finaldata f;
ArrayList<Finaldata> wordList=new ArrayList<>();
ClassicFragment classicfragment=new ClassicFragment();
public void process(String s ){
if(s!=null){
try {
JSONObject data = new JSONObject(s);
JSONArray word = data.getJSONArray("WordArray");
for (int i = 0; i < word.length(); i++){
JSONObject c = word.getJSONObject(i);
String word1=c.getString("word");
String meaning=c.getString("meaning");
f=new Finaldata(word1,meaning);
wordList.add(f);
}
classicfragment.update(wordList);
}catch (Exception e){
}
}
}
}
CustomArray Adapter:
public class CustomArrayAdapter extends ArrayAdapter<dataobjects> {
int color;
public static MediaPlayer mediaPlayer;
static AudioManager am;
private TextToSpeech tts;
static MediaPlayer.OnCompletionListener oncom=new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mp) {
release();
}
};
public CustomArrayAdapter(Activity context, ArrayList<dataobjects> a, int color) {
super(context,0,a);
this.color=color;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
View listitemview=convertView;
if(listitemview==null){
listitemview= LayoutInflater.from(getContext()).inflate(R.layout.customlayout,parent,false);
}
dataobjects currentword=getItem(position);
TextView t1=(TextView)listitemview.findViewById(R.id.textView);
TextView t2=(TextView)listitemview.findViewById(R.id.textView2);
View background1=listitemview.findViewById(R.id.back);
View background2=listitemview.findViewById(R.id.back2);
background1.setBackgroundColor(colorid);
background2.setBackgroundColor(colorid);
int ex=currentword.getImageURL();
int aud=currentword.getSound();
t1.setText(currentword.getWord());
t2.setText(currentword.getTransalation());
int colorid= ContextCompat.getColor(getContext(),color);
ImageView im1=(ImageView)listitemview.findViewById(R.id.media);
im1.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
test();
}
});
return listitemview;
}
Your code is a little convoluted, but basically what is happening is, when you call update() the fragment has not yet been initialized.
Read about a Fragment lifecycle.
In a nutshell:
you create a fragment (like in your code with new ClassicFragment() ). So far, only the Fragment constructor has been called
Then you insert it on the screen with a transaction, with something like
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(frame.getId(), mFragment).commit();FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(frame.getId(), mFragment).commit();
Then, asynchronously, the fragment method onAttach() followed by others like onCreateView() and onViewCreated() will be called by the Android rendering engine.
In your code, the adapter is initialized in onViewCreated(), so only after the fragment is inserted and initialized you will be able to call update(). If you call update before, you will get a NullPointerException as the adapter has not yet been initialized.
You need to refactor your code, either detecting a call to update() when the fragment is not inited and postpone the update, or just calling update later.
try to put all your code inside the method onActivityCreated()
use this method below in your fragment:
dataArray.clear();
dataArray = new ArraryList<>();
dataArray = getListBynet(); get the data from net
newAdapter=new CustomArrayAdapter(dataArray);
listView.setAdapter(newAdapter);
hope this is useful
Well, you already have your adapter as a field on your fragment class, so you just need to have the following in your CustomArrayAdapter:
First add a field to your adapter for the ArrayList containing your data
private ArrayList<dataobjects> data;
and in your adapter constructor, set the value of that field from what you're passing in:
public CustomArrayAdapter(Activity context, ArrayList<dataobjects> a, int color) {
super(context, 0, a);
this.data = a;
this.color=color;
}
Then add a new method for changing the data in the adapter:
public void setItems(ArrayList<dataobjects> items){
this.data = items;
notifyDataSetChanged();
}
And you'll also need to override getItem() to return the item from your data field.
#Override
public dataobjects getItem(int position){
return this.data.get(position);
}
And then when you're updating your adapter later, just call adapter.setItems(data); from your fragment.
Hope it helps!

onCreateViewHolder is never called

I am trying to use recyclerView which is inside a fragment.This is fragment is nested inside the viewPager.
public class UniversityDetail extends Fragment {
RecyclerView universityDetailView;
//need to set Adapter
public static UniversityDetail newInstance(){
return new UniversityDetail();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
View view = inflater.inflate(R.layout.fragment_university_lsit,container,false);
universityDetailView = (RecyclerView)view;
setupViews();
return view;
}
private void setupViews(){
//set the adapter
UniversityDetailAdapter detailAdapter = new UniversityDetailAdapter(new ArrayList<UniversityDetails>());
universityDetailView.addItemDecoration(new RecyclerListDecorater(getActivity()));
universityDetailView.setAdapter(detailAdapter);
universityDetailView.setLayoutManager(new LinearLayoutManager(getActivity()));
universityDetailView.setHasFixedSize(false);
}
public RecyclerView getRecyclerView(){
return this.universityDetailView;
}
}
This is the fragment which i want to be inside the viewPager.It returns a recyclerView from onCreateView.
<android.support.v7.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scroll"
android:paddingBottom="8dp"
android:paddingTop="?attr/actionBarSize"
android:scrollbars="vertical">
fragment_university_lsit.xml
recyclerView uses below adapter.
public class UniversityDetailAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<UniversityDetails> universityDetails;
private static Map<String,String> admissionRecommendation;
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
((DetailHolder)holder).bind(universityDetails.get(position));
}
#Override
public int getItemCount() {
return universityDetails.size();
}
public UniversityDetailAdapter(List<UniversityDetails> details){
this.universityDetails = details;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType){
Log.d("CreateUniveristyDetail", "onCreateViewHolder: detail called");
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.detail_fragment,parent,false);
return new DetailHolder(itemView);
}
}
DetailHolder is the class extends ReyclerView.ViewHolder and its implementation is irrelevant here.
The adapter list is upated from handler and notifyDataSetChanged()
private void setup(){
mHandler = new Handler(Looper.getMainLooper()){
#Override
public void handleMessage(Message message){
if(message.what == ConnectionHandler.DETAILMSG){
Log.d("detail handle message", "handleMessage: Called");
List<UniversityDetails> details = (List<UniversityDetails>)message.obj;
UniversityDetailAdapter adapter = (UniversityDetailAdapter)universityDetail.getRecyclerView().getAdapter();
adapter.addAll(details);
adapter.notifyDataSetChanged();
Log.d("size", "handleMessage: " + adapter.getItemCount());
}
}
};
}
Fragment is created in activity..
private void setupNavigation(){
ViewPagerAdapter pagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
universityDetail = UniversityDetail.newInstance();
pagerAdapter.addFragment(universityDetail);
mViewPager.setAdapter(pagerAdapter);
//setup the fragment transaction.
headerTab.setViewPager(mViewPager,0);
//no need to add to back stack
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.view_pager,universityDetail);
transaction.commit();
}
Implementation of DetailHolder
public class DetailHolder extends RecyclerView.ViewHolder{
CardView admissionGeneral;
public View root;
public DetailHolder (View itemView){
super(itemView);
root = itemView;
admissionGeneral = (CardView)root.findViewById(R.id.admission_general);
}
public void bind(UniversityDetails detail){
Log.d("detail bind", "bind: binding to recyclerView");
}
}
here the log of adapter.getItemCount(); return 3; thats means something is being added and onCreateViewHolder is supposed to be called but it never is.
The weird thing is i have implemented another recyclerAdapter is the sameProject and its working perfectly fine.
This problem would be easier to answer if you could show how the universityDetail was assigned from whatever class contains the Handler usage.
It's also not clear how message.obj is able to be cast to a List.
I believe you are using a different instance of the Fragment there than the one that you want to update.
So, you know this line is good
List<UniversityDetails> details = (List<UniversityDetails>)message.obj;
because you see 3 in the output when you do this
Log.d("size", "handleMessage: " + adapter.getItemCount());
Now, these lines could likely be the problem.
UniversityDetailAdapter adapter = (UniversityDetailAdapter)universityDetail.getRecyclerView().getAdapter();
adapter.addAll(details);
adapter.notifyDataSetChanged();
Firstly, there is no addAll method to RecyclerView.Adapter. So I'm not sure how that compiled. Anyways...
Try to keep cross-class references to a minimum. Here, we expose only the method to add the details rather than the entire RecyclerView.
Then, for RecyclerViews, you should notify only the data that was inserted rather than all of it using notifyItemRangeInserted
public class UniversityDetailFragment extends Fragment {
private RecyclerView universityDetailView;
private UniversityDetailAdapter detailAdapter;
private List<UniversityDetails> details = new ArrayList<UniversityDetails>();
...
public void setupViews() {
detailAdapter = new UniversityDetailAdapter(details);
// etc...
}
public void addDetails(List<UniversityDetails> details) {
int curSize = detailAdapter.getItemCount();
this.details.addAll(details);
detailAdapter.notifyItemRangeInserted(curSize, details.size());
}
}
And then just use the instance of your Fragment
if(message.what == ConnectionHandler.DETAILMSG){
Log.d("detail handle message", "handleMessage: Called");
List<UniversityDetails> details = (List<UniversityDetails>) message.obj;
universityDetail.addDetails(details);
Another recommendation would be to use the correct generic type for the holder class to avoid unnecessary casts.
public class UniversityDetailAdapter extends RecyclerView.Adapter<DetailHolder>

Android ViewPager onClickListener reacts even if other fragment is show

I have a ViewPager which holds two fragments. one fragment contains nothing and the other fragment contains a gridview with ImageViews.
The ImageViews have a onClickListener set.
Everything works fine so far... but when i am on the fragment which contains nothing and tap somewhere the onClickListener of the other fragments gridviews imagview reacts to my click even if its elements aren't visible.
I could change my onClickListener so that it checks which fragment is shown but is that really the way i should do it ??? it feels a bit dirty
This is my FragmentStatePageAdapter
public class OwnPagerAdapter extends FragmentStatePagerAdapter{
private BackgroundImage backgroundImage = new BackgroundImage();
private Apps apps = new Apps();
private Home_Screen ac;
public OwnPagerAdapter(FragmentManager fm, Home_Screen activity) {
super(fm);
this.ac = activity;
}
#Override
public Fragment getItem(int position) {
if(position == 0) {
return backgroundImage;
}else if(position == 1) {
return this.apps;
}
return null;
}
#Override
public int getCount() {
return 2;
}
}
This is my BaseAdapter
public class AppAdapter extends BaseAdapter {
private AppLauncher launcherListener = new AppLauncher();
private ArrayList<ApplicationInfo> appList;
private Context ctx;
public AppAdapter(ArrayList<ApplicationInfo> listOfApps, Context ctx){
this.appList = listOfApps;
this.ctx = ctx;
}
#Override
public int getCount() {
return this.appList.size();
}
#Override
public Object getItem(int i) {
ImageView v = new ImageView(this.ctx);
v.setImageDrawable(this.appList.get(i).loadIcon(this.ctx.getPackageManager()));
return v;
}
#Override
public long getItemId(int i) {
return 0;
}
#Override
public View getView(final int i, View view, ViewGroup viewGroup) {
if(view != null){
((ImageView) view).setImageDrawable(this.appList.get(i).loadIcon(this.ctx.getPackageManager()));
view.setTag(i);
return view;
}
ImageView v = new ImageView(this.ctx);
v.setImageDrawable(this.appList.get(i).loadIcon(this.ctx.getPackageManager()));
v.setScaleType(ImageView.ScaleType.CENTER);
v.setOnClickListener(launcherListener);
v.setTag(i);
return v;
}
private class AppLauncher implements View.OnClickListener {
#Override
public void onClick(View view) {
ctx.startActivity(ctx.getPackageManager().getLaunchIntentForPackage(appList.get((int) view.getTag()).packageName));
}
}
}
Thats my fragment class
public class Apps extends Fragment implements LoaderManager.LoaderCallbacks<ArrayList<ApplicationInfo>>{
private GridView gridview;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
getLoaderManager().initLoader(1, null, this).forceLoad();
View rootView = inflater.inflate(R.layout.fragment_apps, container, false);
this.gridview = (GridView)rootView.findViewById(R.id.apptable);
return rootView;
}
#Override
public Loader<ArrayList<ApplicationInfo>> onCreateLoader(int id, Bundle args) {
return new AppLoader(getActivity());
}
#Override
public void onLoadFinished(Loader<ArrayList<ApplicationInfo>> loader, ArrayList<ApplicationInfo> data) {
Toast.makeText(getActivity(), "Done with loading Apps", Toast.LENGTH_LONG).show();
this.gridview.setAdapter(new AppAdapter(data,getActivity()));
}
#Override
public void onLoaderReset(Loader<ArrayList<ApplicationInfo>> loader) {
}
}
While i cant find any specific cause to your problem i have to suggest a cleaner way of achieving the same result you're seeking, using an inner class just to capture click events is dirty and just unnecessary. it is quite possible that using this method will solve your problem as well.
Instead of using AppLauncher class which implements an OnClickListener and then set it manualy for each item, why not using an OnItemClickListener on the whole gridview ? it will take care of click events for each item and is specific only to items inside your gridview so you dont have to worry about any leaks like you would using inner classes.
In your fragment implement OnItemClickListener :
public class Apps extends Fragment implements LoaderManager.LoaderCallbacks<ArrayList<ApplicationInfo>>, OnItemClickListener
Then in your fragment's onCreate simply set the adapter to the gridview:
gridview.setOnItemClickListener(this);
and implement the necessary method:
#Override
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
getActivity().startActivity(getActivity().getPackageManager().getLaunchIntentForPackage(appList.get(position).packageName));
}
Now you can remove the OnClickListener logic from your gridview's adapter and it should work fine, my guess is it will also solve your problem, and even if not, hey at least you end up with a cleaner code.
Another thing i find odd about your code is that you override getItemId() yet always return 0, make sure this is the normal behaviour you're looking for since im not sure it is.
Good luck.

How to call getFragmentManager on Recycler.Adapter?

I am converting ListView of my app to RecyclerView. On ListView, it was very easy to implement OnClickListener but in RecyclerView, we have to do it in adapter. I want to open a new Fragment when user clicks on a item. To do this I have to call FragmentManager in adapter which I am not able to do.
This is my code of RecyclerAdapter:
public ListItemViewHolder(View itemView) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.title);
description = (TextView) itemView.findViewById(R.id.description);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
//Call FragmentManager and add Fragment to it.
}
}
So, how to call FragmentManager and add Fragments in it. Is there any better way than this like sendingBroadcast or any other method.
You just need an activity context passed in your constructor. Be sure to call new Adapter(this,...) from activities and new Adapter(getActivity(),...) from fragments.
private Context context;
#Override
public void onClick(View v) {
FragmentManager manager = ((AppCompatActivity)context).getSupportFragmentManager();
}
To add on to the approved answer: if you still get an error you might need to replace this line;
FragmentManager manager = ((Activity)context).getFragmentManager();
With this
FragmentManager manager = ((AppCompatActivity)context).getSupportFragmentManager();
For me this was because I was using the support.v4.app.FragmentManager instead of the regular fragmentmanager
Still get an error?
As one comment below pointed out, this might throw a java.lang.ClassCastException: and log ... cannot be cast to android.support.v7.app.AppCompatActivity (check comments for details)
Their fix was to use this instead (I haven't tested it but it worked for them):
((AppCompatActivity)activity).getSupportFragmentManager()
Make sure to pass context to the ArrayAdapter or RecyclerViewAdpater,So that we can get it inside Adapter Class.
If your mainActivity is extending Activity then use :
FragmentManager fragmentManager = ((Activity)context).getFragmentManager();
If your mainActivity is extending AppCompatActivity then use :
FragmentManager fragmentManager = ((AppCompatActivity)context).getSupportFragmentManager();
Best option would probably be to have the Fragment that instantiates the RecyclerAdapter to implement and interface like this:
public class MyRecyclerAdapter extends Adapter<RecyclerAdapter.ViewHolder> {
private ItemType[] mItems;
private MRAItemClickedListener mListener;
public MyRecyclerAdapter(Context ctx, MRAItemClickedListener listener){
mListener = listener;
...
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
holder.itemView.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View v) {
mListener.onItemClicked(mItems[position]);
}
});
}
interface MRAItemClickedListener {
void onItemClicked(ItemType item);
}
}
public class MyFragment ... implements MRAItemClickedListener {
public void onItemClicked(ItemType item){
// do stuff with item
}
}
in kotlin you can use this code:
val fm : FragmentManager= (context as AppCompatActivity).supportFragmentManager
You just need an activity context passed in your constructor. Be sure to call new Adapter(this,...) from activities and new Adapter(getActivity(),...) from fragments.
private Context context;
#Override
public void onClick(View v) {
FragmentManager manager = ((Activity) context).getFragmentManager();}
I know this is too late for you but to anyone else who might see this.
So instead of doing m vai did you can pass the context of the fragment when you first initialize your adapter.
So in your constructor for your adapter you can add an argument like this
// variable to hold fragment
private Fragment fragment;
public MyCustomAdapter(Fragment fragment)
{
this.fragment = fragment;
}
and in your fragment you can just initialize if like this
MyCustomAdapter myAdapter = new MyCustomAdapter(this);
finally you can call
Fragment fragment = new myNewFragment();
FragmentManager fragmentManager = context.getFragmentManager();
fragmentManager.beginTransaction().replace(R.id.fragment_container, fragment)
.commit();
so you can start a new fragment
Instantiate the fragmentManager in the activity itself.
MyActivity.java
FragmentManager fragmentManager = getFragmentManager();
adapter = new CustomAdapter(productsList, fragmentManager);
CustomAdapter.java
CustomAdapter implements MyClickListner{
CustomAdapter(Arraylist<MyProduct>productsList, FragmentManager fragmentManager) {
this.productList = productsList;
this.fragmentManager = fragmentManager;
}
#override
clickFunction(){
Fragment myFragment= new MyFilterFragment();
((MyFilterFragment) myFragment).show(this.fragmentManager,"tag");
}
MyClickListener.java
public interface MyClickListener(){
public clickFunction();
}
You could pass FragmentManager reference while creating viewholder so your class for Viewholder could be as follows
class ListItemViewHolder {
FragmentManager manager;
public ListItemViewHolder(View itemView,FragmentManager manager) {
super(itemView);
title = (TextView) itemView.findViewById(R.id.title);
description = (TextView) itemView.findViewById(R.id.description);
itemView.setOnClickListener(this);
this.manager = manager;
}
#Override
public void onClick(View v) {
//Call FragmentManager and add Fragment to it.
manager.beginTransaction...
}
}

Replacing ListFragment with another ListFragment

I have two almost identical ListFragment A & B, except that they draw data from their respective Asynctasker.
onListItemClick at A will result in Fragment A being replaced by B. However, my app always get stuck at the constructor of its CustomAdapter in B.
private class CustomAdapter_B extends ArrayAdapter<MatchType> {
// Stuck Here
CustomAdapter_B() {
super(getActivity(), R.layout.color_match_type_s2, matches_type_s2);
}
Fragment A
public class Fragment A extends ListFragment implements AsyncTaskCompleteListener<ArrayList<MatchType>>{
ArrayList<MatchType> matches_type = new ArrayList<MatchType>();
private ProgressDialog dialog;
private static FirstPageFragmentListener main_caller;
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
//Interface, triggering Fragment Replace in my Fragment Adapter
main_caller.onMySignalWithNum(1, (matches_type.get(position).getLink()));
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.dialog.show();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// AsyncTasker to draw data for ArrayAdapter
LoadMatchType lmt = new LoadMatchType(this);
lmt.execute();
}
#Override
public void onTaskComplete(ArrayList<MatchType> result) {
dialog.dismiss();
//result.remove(result.size()-1);
matches_type = result;
setListAdapter(new CustomAdapter());
}
}
//Custom Adapter
private class CustomAdapter extends ArrayAdapter<MatchType> {
CustomAdapter() {
super(getActivity(), R.layout.color_match_type, matches_type);
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub
View row = convertView;
if (row == null) {
LayoutInflater inflater = getActivity().getLayoutInflater();
row = inflater.inflate(R.layout.color_match_type, parent, false);
}
TextView tv_type = (TextView) row.findViewById(R.id.textView_type);
tv_type.setText(matches_type.get(position).getType());
TextView tv_tweet = (TextView) row.findViewById(R.id.textView_tweet);
tv_tweet.setText(matches_type.get(position).getTweet());
TextView tv_tph = (TextView) row.findViewById(R.id.textView_tph);
tv_tph.setText(matches_type.get(position).getTph());
return row;
}
}
public static Fragment newInstance(
FirstPageFragmentListener pageFragmentListener) {
// TODO Auto-generated method stub
footOddsFragment frag = new footOddsFragment();
main_caller = pageFragmentListener;
return frag;
}
}
//INTERFACE
interface AsyncTaskCompleteListener<T> {
public void onTaskComplete(ArrayList<MatchType> result);
}
Fragment B is about the same.
The app will get stuck at Fragment B's CustomAdapter constructor and the screen will be blank.
Oh, and everything works fine if I abandon Fragment B and just call the Asynctasker_B in onListItemClick in Fragment A. Just that I can't press back to view the previous list.
Forget Fragment B. Override your back press, put an if in there to determine if List B is active, if it is, change it back, otherwise perform the normal back function.
public void onBackPressed() {
// Code here to change list or go back.
return;
}

Categories

Resources