I have a fragments that builds a songs ArrayList, i want to pass that ArrayList to the framgment's activity . I know i can use interface, but not sure how i could do it
public class SongsListFragment extends Fragment
{
public interface passArrayList {
public void onArticleSelected(Uri articleUri);
// from android guide i don't know what to do
}
}
Here is what i have so far, please let me know why im getting NullPointer
on MainActivity
#Override
public void onFragmentSetSongs(ArrayList<Song> songs){
songsArrayList = songs;
}
#Override
public void onSongListItemClick(int position) {
musicService.setSong(position);
Song playSong = songsArrayList.get(position);
txtCurrentSongTitle.setText(playSong.getTitle());
txtCurrentSongTitle.requestFocus();
musicService.playSong();
btnPlayPause.setImageDrawable(getResources().getDrawable(R.drawable.ic_action_pause));
if (playbackPaused) {
playbackPaused = false;
}
//setDownloaded();
}
on Fragment
OnFragmentInteractionListener songsCallBack;
OnFragmentInteractionListener songsItemClick;
public interface OnFragmentInteractionListener {
public void onFragmentSetSongs(ArrayList<Song> s);
public void onSongListItemClick(int position);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
songsCallBack = (OnFragmentInteractionListener) getActivity();
songsItemClick = (OnFragmentInteractionListener) getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
#Override
public void onDetach() {
super.onDetach();
songsCallBack = null;
songsItemClick= null;
}
listView = (ListView) root.findViewById(R.id.listViewSongs);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
songsItemClick.onSongListItemClick(position);
}
});
on the method where i finish building the songArrayList, i did
songsCallBack.onFragmentSetSongs(songArrayList);
Error When i Click on ListView
at com..activities.MainActivity.onSongListItemClick(MainActivity.java:107)
at com..fragments.SongsListFragment$1.onItemClick(SongsListFragment.java:194)
at android.widget.AdapterView.performItemClick(AdapterView.java:308)
at android.widget.AbsListView.performItemClick(AbsListView.java:1478)
at android.widget.AbsListView$PerformClick.run(AbsListView.java:3480)
Error Position
musicService.setSong(position); //Main Activity (MainActivity.java:107)
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
songsItemClick.onSongListItemClick(position); < -- here
}
Change name of your interface so that it begins from capital letter. Then in your activity add this line to declaration of your activity: public class FragmentActivity extends Activity implements PassArrayList, override interface method. And in fragment add following:
PassArrayList mCallback;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
mCallback = (PassArrayList) getActivity();
}
#Override
public void onDetach() {
super.onDetach();
mCallback = null;
}
And somewhere in your code, when you want to pass your list of songs back to the activity call the method onArticleSelected() on your mCallback object and pass to this method your arraylist. Then this arraylist will come as an argument to the method onArticleSelected() in your activity and you could make with it anything that you like. But don't forget to nullify link to mCallback in onDetach() hook method to prevent context leak
Related
I would like to call activity function from Adapter. I am trying something like this:
((MainActivity)mCtx).SampleVoid();
But i've got the error: java.lang.ClassCastException: android.app.Application cannot be cast to
public void SampleVoid(){
Toast.makeText(this,"Testowa metoda",Toast.LENGTH_LONG).show();
}
That error is happening because you are trying to cast the Application context to the Activity context. First of all, I wouldn't recommend you sending and storing context inside the adapter. I would recommend you to create an interface for communication back to the Activity at specific action from the adapter. For example:
Activity code:
SomeAdapter adapter = new SomeAdapter();
adapter.setOnAdapterActionListener(new OnAdapterActionListener() {
#Override
public void onSomeAdapterAction() {
Toast.makeText(this,"Testowa metoda",Toast.LENGTH_LONG).show();
}
});
Adapter code:
public class SomeAdapter extends ArrayAdapter<String> { // extend from the same adapter that you are extending now.
private OnAdapterActionListener onAdapterActionListener;
public SomeAdapter(#NonNull Context context, int resource, #NonNull List<String> objects) {
super(context, resource, objects);
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
View view = super.getView(position, convertView, parent);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onAdapterActionListener != null) {
onAdapterActionListener.onSomeAdapterAction();
}
}
});
return view;
}
public void setOnAdapterActionListener(OnAdapterActionListener onAdapterActionListener) {
this.onAdapterActionListener = onAdapterActionListener;
}
}
interface OnAdapterActionListener {
void onSomeAdapterAction();
}
You can pass the activity through the Adapter constructor.
Generally you do this through interfaces
public class MainActivity extends AppCompatActivity implements MAdapter.Listener {
private MAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mAdapter = new MAdapter(this);
}
#Override
public void onClick(int position) { }
}
Then
public class MAdapter {
private Listener listener;
MAdapter(Listener listener){
this.listener = listener;
}
interface Listener {
void onClick(int position);
}
}
I am trying to use a static class to pass value to a view instead of using intent as I have to pass a large amount of data. Sometimes I get this error and couldn't find out what is the main reason
Error :-
java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged! Expected adapter item count: 101, found: 200
My Pager class
public class MemeDetailActivity extends AppCompatActivity implements OnDialogClickListner<Void> {
private ViewPager mPager;
private List<Datum> mList;
private ScreenSlidePagerAdapter pagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_meme_detail);
mList = DataResult.getInstance().getData();
DataResult.getInstance().resetData();
if (mList != null)
startPager(selected_position);
else
Toast.makeText(this, "Error loading data", Toast.LENGTH_SHORT).show();
// ATTENTION: This was auto-generated to implement the App Indexing API.
// See https://g.co/AppIndexing/AndroidStudio for more information.
client = new GoogleApiClient.Builder(this).addApi(AppIndex.API).build();
}
private void startPager(int position) {
pagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
mPager.setAdapter(pagerAdapter);
pagerAdapter.notifyDataSetChanged();
mPager.setOffscreenPageLimit(0);
mPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
new Interactors(MemeDetailActivity.this).updateView(Util.getAccessToken(), mList.get(position).getId(), new OnFinishListner<ViewsRM>() {
#Override
public void onFinished(ViewsRM result) {
}
#Override
public void onFailed(String msg) {
}
});
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
mPager.setCurrentItem(position);
}
public class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
public ScreenSlidePagerAdapter(FragmentManager fm) {
super(fm);
}
//todo entry point 3 : showing the image in the viewpager
#Override
public Fragment getItem(int position) {
Fragment fragment = new fragment_mypager_new();
Bundle bundle = new Bundle();
if (frmMyMemes)
bundle.putParcelable("MY_DATA", myList);
bundle.putSerializable("DATA", mList.get(position));
fragment.setArguments(bundle);
return fragment;
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
#Override
public int getCount() {
return mList.size();
}
#Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment fragment = (Fragment) super.instantiateItem(container, position);
registeredFragments.put(position, fragment);
return fragment;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
registeredFragments.remove(position);
super.destroyItem(container, position, object);
}
public Fragment getRegisteredFragment(int position) {
return registeredFragments.get(position);
}
}
}
My static class
public class DataResult<T> {
private static DataResult instance;
private List<T> data = null;
protected DataResult() {
}
public static synchronized DataResult getInstance() {
if (instance == null) {
instance = new DataResult();
}
return instance;
}
public List<T> getData() {
return data;
}
public void setData(List<T> data) {
this.data = data;
}
public void resetData() {
data = null;
}
}
How I call the activity
Intent intent = new Intent(getActivity(), MemeDetailActivity.class);
DataResult.getInstance().setData(mListA);
look at your code:
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
this code is use to refresh view when view change. it is also called notifyDataSetChanged()
its optional ovverride method so you can remove it.
Take a look at this answer: ViewPager PagerAdapter not updating the View
or else you can Change the FragmentStatePagerAdapter to FragmentPagerAdapter
Sometimes this error occurs.
Here is my suggestion:
In your DataResult->getData()
return a copy of the data.
DataResult is a singleton and holds the data. When your view gets the data, the view gets the data reference witch the DataResult is holding. Then setData() would change the data reference. Here comes the IllegalStateException.
Hope it would work :)
I think your DataResult class is not necessary.
According to your comment you are getting the error while you are updating the data set. I think you are getting data from some REST API or another web service. Then assign the data you gets from the API to a temporary Array List and update that temporary array list while you are getting new data. Don't touch the mListA variable until you finish receiving data. After complete getting data from the API assign the temporary array list you used to mListA directly in one line.
mListA = tmpList;
Then again call
mList = mListA;
pagerAdapter.notifyDataSetChanged();
This should solve your error.
try using the following way:
mList=new ArrayList<Datum>();
mList.addAll(DataResult.getInstance().getData());
DataResult.getInstance().resetData();
It may because you first setAdapter() and notifyDataSetChanged in method initUI();
Then, you instantiate the List<?> data in method initData(), the two methods in different lifetime of fragment.
It's the problem about fragment's lifecycle
#Override
public void restoreState(Parcelable state, ClassLoader loader) {}
#Override
public Parcelable saveState() {return null;}
#Add in your adapter after getCount();
I am sending the arraylist from one fragment to the another by using the interface.but when i make the instance of the interface it gives class cast exception.can anyone tell me what i am doing wrong ??
This is my interface:-
public class SaleFragment extends UpdatableFragment {
private SendArrayList mSendArrayList;
public interface SendArrayList {
public void sendData(ArrayList<String> listCustomer);
}
}
And this is the code where i am making the instance of the interface and add the arraylist to it :-
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mSendArrayList = (SendArrayList)getActivity();
dbh = new DatabaseHandler(getContext());
Cursor cursor = dbh.getAllCustomer();
if (cursor.moveToFirst()) {
do {
String customerName = cursor.getString(0);
mListCustomerName.add(customerName);
} while (cursor.moveToNext());
}
mSendArrayList.sendData(mListCustomerName);
}
at this line " mSendArrayList = (SendArrayList)getActivity();" gives class cast exception can anyone tell me how can i use the interface for send the data from one fragment to the another.
You get the Activity instance from the OnAttach() method.
public class HeadlinesFragment extends ListFragment {
OnHeadlineSelectedListener mCallback;
// Container Activity must implement this interface
public interface OnHeadlineSelectedListener {
public void onArticleSelected(int position);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// This makes sure that the container activity has implemented
// the callback interface. If not, it throws an exception
try {
mCallback = (OnHeadlineSelectedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString()
+ " must implement OnHeadlineSelectedListener");
}
}
...
}
The following method in the fragment is called when the user clicks on a list item. The fragment uses the callback interface to deliver the event to the parent activity.
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// Send the event to the host activity
mCallback.onArticleSelected(position);
}
The Activity should implement the your Interface.then you send the data from fragment to activity, it will be shared to another fragment.
public static class MainActivity extends Activity
implements HeadlinesFragment.OnHeadlineSelectedListener{
...
public void onArticleSelected(int position) {
// The user selected the headline of an article from the HeadlinesFragment
// Create your second fragment instance here and share your data (position)
}
}
reference: https://developer.android.com/training/basics/fragments/communicating.html#Deliver
Its simple,get instance of interface in onAttach
private SendArrayList mCallback;
#Override
public void onAttach(Context context) {
super.onAttach(context);
try {
mCallback = (SendArrayList) context;
dbh = new DatabaseHandler(getContext());
Cursor cursor = dbh.getAllCustomer();
if (cursor.moveToFirst()) {
do {
String customerName = cursor.getString(0);
mListCustomerName.add(customerName);
} while (cursor.moveToNext());
}
mCallback.sendData(mListCustomerName);
} catch (ClassCastException e) {
throw new ClassCastException(context.toString()
+ " must implement SendArrayList");
}
}
And in your Parent Activity, you need to implement SendArrayList interface
Just for reference I am been trying to follow the answer to this question
Basic Communication between two fragments
I have 2 Fragments within a ViewPager Adapter along with an Actionbar.
What I have is one fragment produces some data which can (if chosen) inserted to an SQLite table.
The second Fragment simply displays all data in the table, however I am trying to make some communication that as soon as Fragment 1 inserts data into the SQLite table. Fragment 2 is called to refresh its select query (as in do the query again) to automatically show the latest data. At the moment this is manually done with a button which I feel is not great.
This is my interface in Fragment 1
onNumbersSavedListener mCallback;
public interface onNumbersSavedListener
{
public void RequestQueryRefresh();
}
#Override
public void onAttach(Activity activity) {
// TODO Auto-generated method stub
super.onAttach(activity);
try
{
mCallback = (onNumbersSavedListener) activity;
}
catch(ClassCastException e)
{
e.printStackTrace();
}
}
This is the main Activity which contains the ViewPager and implements the interface
public class MainActivity extends FragmentActivity implements TabListener, GenerateFragment.onNumbersSavedListener
This is the main problem I am having which I do not have IDs for the fragments which answer referred in the link stated above does so.
#Override
public void RequestQueryRefresh() {
// TODO Auto-generated method stub
}
TLDR: I am just looking for an easy and clean way for as soon as Fragment 1 saves into DB, fragment 2 updates its list view by re-running its query.
see more about otto lib here : http://square.github.io/otto/
Edited:
public class FragmentA extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
public void saveData(){
//save datas before
BusProvider.getInstance().post(new EventUpdateOtto());
}
}
public class EventUpdateOtto{
public EventUpdateOtto(){
}
}
public class FragmentB extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
#Subscribe
public void subUpdateList(EventUpdateOtto mEventUpdateOtto){
//update yout list here
}
#Override
public void onResume() {
BusProvider.getInstance().register(this);
super.onResume();
}
#Override
public void onPause() {
BusProvider.getInstance().unregister(this);
super.onPause();
}
}
public class BusProvider {
private static final Bus BUS = new Bus();
public static Bus getInstance() {
return BUS;
}
private BusProvider() {
// No instances.
}
}
In your case you can improve your interface:
public interface onNumbersSavedListener
{
public void RequestQueryRefresh(Bunde bundle/*or something other*/);
}
If you are using a cursor loader, the change should automatically be reflected in the fragment displaying it. However, the fragment that wants immediate updates whenever the table is changed can register as an observer to that table:
// observer to the table
class MyObserver extends ContentObserver {
final Handler mHandler;
public MyObserver(Handler handler) {
super(handler);
// I used a handler to get back to my UI thread
mHandler = handler;
}
#Override
public void onChange(boolean selfChange) {
this.onChange(selfChange, null);
}
#Override
public void onChange(boolean selfChange, Uri uri) {
Log.i(TAG, "MyObserver: onChange");
// do what you want to do - this is what I implemented
mHandler.post(myRunnable);
}
}
Then, register it:
mHandler = new Handler();
mObserver = new MyObserver(mHandler);
ContentResolver resolver = getContext().getContentResolver();
resolver.registerContentObserver(uri, false, mEventLogObserver);
The other fragment should then do a notify:
getContext().getContentResolver().notifyChange(uri, null);
The key is the uri - one watches it, the other notifies.
I wanna add clickableItems to Listview. But my problem is, that my class extands Fragment and i can not do that easily. Additionaly i wanna by clicking the item change the page to another page. My code is:
public class MyCoursesFragment extends Fragment implements OnItemClickListener, OnClickListener {
private static final String TAG = MyCoursesFragment.class.getName();
private ListView listView;
String listItem[]={"Vl1", "VL2"};
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View layout = inflater.inflate(R.layout.mycourses_layout, container, false);
listView = (ListView) layout.findViewById(R.id.Course);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
Intent intent = new Intent(getActivity().getBaseContext(), DocumentFragment.class);
intent.putExtra("id", "1");
listView.getContext().startActivity(intent);
FrontPageFragment.this.startActivity(intent);
}
});
return layout;
}
#Override
public void onItemClick(AdapterView<?> adapter, View view, int position, long id) {
}
}
can you guys pls help me.
THX
Change this line because your context is wrong:
listView.getContext().startActivity(intent);
FrontPageFragment.this.startActivity(intent);
You need to start a new activity in the following way:
Intent intent = new Intent(getActivity(),DocumentFragment.class);
startActivity(intent);
What you can to do is set up a callback Interface and control everything from your parent Activity.
Your Activity should implement the interface and your Fragment should call those methods when your View or list item gets clicked.
Something along the lines of:
interface
public interface OnListItemClickCallback {
public void onListItemShortClick(View view);
public void onListItemLongClick(View view);
}
Fragment
public MyListFragment extends ListFragment
implements OnItemClickListener, OnItemLongClickListener {
private OnListItemClickCallback clickCallback;
...
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
// make sure the parent activity implements your interface
try
{
clickCallback = (OnListItemClickCallback) activity;
}
catch (ClassCastException e)
{
Log.e(TAG, "Parent Activity doesn't implement OnListItemClickCallback");
throw new ClassCastException(activity.toString()
+ " must implement OnListItemClickCallback");
}
}
#Override
public View onCreateView(Bundle b) {
...
getListView().setOnListItemClickListener(this):
getListView().setOnListItemLongClickListener(this):
}
#Override
public void onItemClick(...) {
clickCallback.onListItemShortClick(view)
}
// same idea for long click if you want it
}
Activity
public MyActivity extends Activity
implements OnListItemClickCallback {
...
#Override
public void onListItemShortClick(View view) {
// do something like replace your fragment
}
#Override
public void onListItemLongClick(View view) {
// do something else
}
}