I have a tabbed activity that has a spinner in its Toolbar. I need to trigger changes in the tab fragments whenever the spinner value changes.
I have so far implemented it by getting the cropsSpinner instance from the Fragment. However it's not working as needed. I am new to listeners; is there a way I can do this with Android callbacks?
ExtensionActivity.java
public class ExtensionActivity extends AppCompatActivity {
private SectionsPageAdapter mSectionsPageAdapter;
#BindView(R.id.container) ViewPager mViewPager;
#BindView(R.id.tabs) TabLayout tabLayout;
#BindView(R.id.toolbar) Toolbar toolbar;
#BindView(R.id.spinner_nav) public Spinner cropsSpinner;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_extension);
ButterKnife.bind(this);
mSectionsPageAdapter = new SectionsPageAdapter(getFragmentManager());
setupViewPager(mViewPager);
tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE);
tabLayout.setupWithViewPager(mViewPager);
}
private void setupViewPager(ViewPager viewPager){
SectionsPageAdapter adapter = new SectionsPageAdapter(getFragmentManager());
adapter.addFragment(new ExtDescFragment(),"Description");
adapter.addFragment(new ExtLandPFragment(),"Land Preparation");
viewPager.setAdapter(adapter);
}
ExtDescFragment.java
public class ExtDescFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_ext_desc, container, false);
ButterKnife.bind(this,view);
((ExtensionActivity)getActivity()).cropsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
CROP =((ExtensionActivity)getActivity()).cp.get(position);
Log.e("CROP",CROP);
getCropInf();
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
For your additional info I suggest you let your cropperSpinner private and using list custom listener like:
interface OnCropperSpinnerSelectedListener {
void onItemSelected(int pos);
}
Then store a list of listener in ExtensionActivity:
List<OnCropperSpinnerSelectedListener> listeners;
after that write a setter method to add listener to the listener list:
void addCropperSpinnerSelectedListener(OnCropperSpinnerSelectedListener listener) {
listeners.add(listener);
}
Then, you have to implement OnItemSelectedListener in ExtensionActivity:
cropsSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
for(int i = 0; i < listeners.size(); i++) {
listeners.get(i).onItemSelected(position);
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
Finally, when you want any Fragment to listen to spinner value changes event (Eg: ExtDescFragment), just call the method addCropperSpinnerSelectedListener() inside onCreate() of this fragment and implement your own logic.
Explain: So every time user select item on spinner, the AdapterView.OnItemSelectedListener will call onItemSelected(), here we loop through our custom listener list that have connection to fragments and call our onItemSelected(int pos) to notify all fragment.
Related
In my activity I have a OnPageChangeListener and view Pager implemented.
On onPageSelectedin function I call function name viewPagerHandler, when I have all the logic for single viewPager Page (Buttons, actions depended of current page etc.)
public class ActivityClass extends Activity implements ViewPager.OnPageChangeListener, View.OnClickListener{
protected void onCreate(Bundle savedInstanceState) {
viewPager=(ViewPager)findViewById(R.id.Pager);
adapter= new
ViewPageAdapter(ActivityClass.this,list,imagesList);
viewPager.setAdapter(adapter);
viewPager.setCurrentItem(0);
viewPager.addOnPageChangeListener(ActivityClass.this);
}
#Override
public void onClick(View v) {
}
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
this.position=position;
viewPagerHandler(viewPager.findViewWithTag(position),position);
}
#Override
public void onPageScrollStateChanged(int state) {
}
public View viewPagerHandler(final View view, final int position){
//ALL LOGIC BUTTONS ACTIONS ETC.
view.invalidate();
return view;
}
}
public class ViewPageAdapter extends PagerAdapter{
#Override
public Object instantiateItem(final ViewGroup container, final int position) {
final View itemView = LayoutInflater.from(mContext).inflate(R.layout.art_work_item, container, false);
itemView.setTag(position);
container.add(itemView); // edited after suggested
if(mContext instanceof ActivityClass && position==0) //The page listener in ActivityClass is trigger only when page is changed so i tried to hack it when the viewPager is instantiate for the first time
return ((ActivityClass) mContext).viewPagerHandler(itemView,position);
return itemView;
}
}
EDIT: The logic works perfectly on the first and the last item. For rest of them the view is not refreshing (however its still working, because i have buttons which changing color after clicked, when I click on them and then slide to next item and back to the previous one the color is changed so its trigger)
You are missing the line in the instantiateItem() method,
public Object instantiateItem(final ViewGroup container, final int position) {
final View itemView = LayoutInflater.from(mContext).inflate(R.layout.art_work_item, container, false);
itemView.setTag(position);
container.addView(itemView);
if(mContext instanceof ActivityClass && position==0) //The page listener in ActivityClass //is trigger only when page is changed so i tried to hack it when the viewPager is //instantiate for the first time
return ((ActivityClass) mContext).viewPagerHandler(itemView,position);
return itemView;
}
I have a fragment with a viewpager with adapter extending FragmentStatePagerAdapter with three slidable fragments inside. the main fragment has a static list of data, and three sub-fragments references and show the same list (don't ask why). The list item can be removed by a button inside the row layout or by clicking clear-all button after the list in each of those three fragments.
My problem is that after removing one or all the items with the buttons, sometimes i get an instant index out of bounds exception (no application code in stack trace, to find where the exception is coming from) or randomly sometimes removal work, but fragments nearby display old data with the extra item, and clicking to remove it ofcourse throws out of bounds exception, because after some debugging I can see that size of list passed to adapter to recreate the nearby fragment is lower by one (removal is successful) so I believe the list is not notified/invalidated correctly. Any help, since stacktrace can't help?
Btw I'm using FragmentStatePagerAdapter.POSITION_NONE to recreate the fragments with new data on each swipe, notifyDataSetChanged to notify the adapter of changed data, and invalidate() the listview in onViewCreated(), but they don't help.
relevant code:
Main Fragment
public class MainFragment extends BaseFragment<CategoryTreeItem> {
public static List<Map.Entry<EventTreeItem, String>> betList = new ArrayList<>();
...
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
ViewPager mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
mViewPager.setAdapter(new MyPagerAdapter(getChildFragmentManager()));
SlidingTabLayout mSlidingTabLayout = (SlidingTabLayout) view.findViewById(R.id.sliding_tabs);
mSlidingTabLayout.setViewPager(mViewPager);
}
}
class MyPagerAdapter extends FragmentStatePagerAdapter {
private List<String> items;
public MyPagerAdapter(FragmentManager fm) {
super(fm);
items = getResources().getStringArray(R.array.three_categories);
}
#Override
public Fragment getItem(int i) {
Fragment fragment;
switch (i) {
case 0:
fragment = new FirstFragment();
return fragment;
case 1:
fragment = new SecondFragment();
return fragment;
case 2:
fragment = new ThirdFragment();
return fragment;
default:
return null;
}
}
...
#Override
public int getItemPosition(Object object) {
return FragmentStatePagerAdapter.POSITION_NONE;
}
}
Three fragments (they are needed since the layout and functionality is a bit different)
public class First/Second/ThirdFragment extends BaseListFragment<ArrayList<EventTreeItem>> {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MyListAdapter adapter = new MyListdapter(getActivity(), R.layout.item_first/second/thirdfragment, MainFragment.betList);
getListView().setItemsCanFocus(true);
View footerView = getLayoutInflater(savedInstanceState).inflate(R.layout.include_first/second/third_footer, getListView(), false);
getListView().addFooterView(footerView);
adapter.notifyDataSetChanged();
setListAdapter(adapter);
getListView().invalidate();
//handles REMOVE ALL button for all fragments footer button
Helper.setUpClearOption(footerView, adapter);
}
}
MyListAdapter
#CompileStatic
public class MyListAdapter extends ArrayAdapter<Map.Entry<EventTreeItem,String>> {
private int mResourceId;
private List<Map.Entry<EventTreeItem,String>> mObjects;
public MyListAdapter(Context context, int resource, List<Map.Entry<EventTreeItem,String>> objects) {
super(context, resource, objects);
mResourceId = resource;
mObjects = objects;
}
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
View view = convertView;
if (view == null) {
view = LayoutInflater.from(getContext()).inflate(mResourceId, parent, false);
}
//------setting a lot of text for textViews
// .....
//------
ImageView ivRemove = (ImageView) view.findViewById(R.id.ivRemove);
ivRemove.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Tried removing directly from adapter, not helping
// MyListAdapter.this.remove(mObjects.get(position));
MainFragment.betList.remove(position);
MyListAdapter.this.notifyDataSetChanged();
}
});
return view;
}
}
Remove All helper method (also returning instant OOB or not notifying neighbor fragments)
public static void setUpClearOption(View view, final ArrayAdapter adapter) {
ImageView ivRemoveAll = (ImageView) view.findViewById(R.id.ivRemoveAll);
ivRemoveAll.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
adapter.clear();
adapter.notifyDataSetChanged();
}
});
}
So any help is much appreciated!
I managed to fix My problem by creating three listadapters for each of my fragments (static adapters instantiaiting with null value ) and in onClick I check each of those three adapters if they are not null (since adapters are null until after onViewCreated, and it is called only when the current fragment is a neighbor). All three adapters use the same data.
I have a main activity which consists of spinner + 3 fragments (tabs). I want to access spinner value from fragment. So I wrote this code into onActivityCreated inside my fragment:
final Spinner spinner = (Spinner) getView().findViewById(R.id.spinnerOblasti);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parentView, View selectedItemView, int position, long id) {
/*TextView[] vystrahy = new TextView[1];
vystrahy[0] = (TextView) getView().findViewById(R.id.txtVystrahy);
(new htmlParser(vystrahy, 0) ).execute(new Integer[]{spinner.getSelectedItemPosition()});*/
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
but when I run my application spinner throws nullpointerexception. Any ideas on how to access it from fragment?
Here is my onCreateView which I believe is causing problem but I dont know how to fix it:
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_tab1, container, false);
return rootView;
}
Thanks in forward
Doesn't work like that, what do you mean for spinner value? If you want to access to the spinner object you have to setup an interface which will be implemented by your activity and then in onActivityCreated you can call the method of that interface to do something. For example:
public class MyClass implements MyInterface {
public interface MyInterface {
void doSomethingWithSpinner();
}
public void doSomethingWithSpinner() {
mySpinner.setVisibility(View.VISIBLE);
}
}
And your onActivityCreated:
if(getActivity() instanceof MyInterface) {
((MyInterface)getActivity()).doSomethingWithSpinner();
}
At the moment my App has two Fragments in form of two (swipe)tabs.
On one Fragment the user can add information to the database.
On the other Fragment a ListView displays the content of the DB-table.
If the user adds a new DB-Entry and swiped to the second tab, the ListView gets not updated, because I do all the loading in my onCreateView() method.
I already tried using the onResume() method and calling an "update method" of the Fragment from my FragmentActivity (in the onPageSelected() method).
onResume() never gets called.
And i if i call getActivity() from my "update method"(in my fragment) it always returns null, but i need the activity object for my database connection.
My Fragment looks like this:
public class ListFragment extends Fragment implements AdapterView.OnItemLongClickListener, OnRefreshListener{
private ListView listView;
private SpeedsArrayAdapter adapter = null;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View table = inflater.inflate(R.layout.fragment_list, container, false);
listView = (ListView)table.findViewById(R.id.listViewSpeeds);
MySQLiteHelper mSQLHelper = new MySQLiteHelper(getActivity());
List <MichealSpeedBean> mSpeeds = mSQLHelper.getAllSpeedEntries();
adapter = new SpeedsArrayAdapter(getActivity(),
R.layout.list_item, mSpeeds);
listView.setAdapter(adapter);
listView.setOnItemLongClickListener(this);
return table;
}
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int position, long id) {
MySQLiteHelper mSQLHelper = new MySQLiteHelper(getActivity());
MichealSpeedBean michealSpeedBean = adapter.getItem(position);
mSQLHelper.deleteSpeedById(michealSpeedBean.getObjectId());
adapter.remove(michealSpeedBean);
Toast.makeText(getActivity(),
"Entry deleted " + position, Toast.LENGTH_LONG)
.show();
return false;
}
#Override
public void onRefresh() {
//getActivity Returns null here, if called form FragmentyActivity
}
And my Fragment Activity looks like this:
public class MainActivity extends FragmentActivity {
ViewPager viewPager;
TabPagerAdapter tabAdapter;
ActionBar actionBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tabAdapter = new TabPagerAdapter(getSupportFragmentManager());
viewPager = (ViewPager)findViewById(R.id.pager);
viewPager.setOnPageChangeListener(
new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar = getActionBar();
actionBar.setSelectedNavigationItem(position);
if (position == 1) {
//Here i tried to update my list from outside of my fragment
((OnRefreshListener)tabAdapter.getItem(position)).onRefresh();
}
}
}
);
viewPager.setAdapter(tabAdapter);
actionBar = getActionBar();
//Enable Tabs on Action Bar
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.TabListener tabListener = new ActionBar.TabListener(){
#Override
public void onTabReselected(android.app.ActionBar.Tab tab,
FragmentTransaction ft) {
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(android.app.ActionBar.Tab tab,
FragmentTransaction ft) {
}};
//Add New Tab
actionBar.addTab(actionBar.newTab().setText("General").setTabListener(tabListener));
actionBar.addTab(actionBar.newTab().setText("Speed entries").setTabListener(tabListener));
}
So the question is where/ how can i update my table content?
Thank you in advance!
you can use following code:
public class ListFragment extends Fragment
#Override
public void setMenuVisibility(final boolean visible) {
super.setMenuVisibility(visible);
if (getActivity() != null )
{
if (visible) {
// update your list, your fragment is visible now
}
}
}
you can use this:
listview.invalidateViews();
enjoy!
I developed an app which fills a list. It works fine in the way I did it but I'm not conviced that I solved the problem in a recommended way. I read that you should override onActivityCreated in a Fragment and fill the list there instead of doing this in onCreateView. onCreateView should only be used to inflate static views. Is this true? If yes, how should these two methods look like in the end?
This is my Fragment class:
public class FragmentMain extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
List<MyItem> items = createListItems();
ListView listView = (ListView) view.findViewById(R.id.list);
MyListAdapter adapter = new MyListAdapter(view.getContext(), items);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(view.getContext(),
"Clicked " + position, Toast.LENGTH_LONG)
.show();
}
});
return view;
}
.
.
.
}
My MainActivity just adds the fragment:
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentMain fm = new FragmentMain();
getFragmentManager().beginTransaction()
.add(R.id.fragment_main_container, fm).commit();
}
.
.
.
}
That is true to a certain extend only because onCreateView happens on the UI thread and you don't want anything slowing that down otherwise your UI will be slow and choppy. For example, in your fragment class you have a call to a method "createListItems()". I don't know how many items you're making but if it's a lot it could slow down your UI (especially if youre accessing a database and querying objects and so on). So you could do it in onActivityCreated but you could also use an AsyncTask. So your code would become something like this:
public class LoadListObjectsTask extend AsyncTask<Void, List<MyItem>, Void> {
private MyListAdapter myListAdapter;
private Context mContext;
public LoadListObjectsTask(Context context) {
this.mContext = context;
}
#Override
public void doInBackground(Void...params) {
//create your list objects here instead of on UI thread. This will run on a separate thread.
myListAdapter = new MyListAdapter(mContext, items);
return items; //return list of MyItems
}
//This is called when doInBackground is done. THIS WILL RUN ON THE UI THREAD So don't do
//anything slow here
#Override
public void onPostExecute(List<MyItem>...params //don't really need the list here//) {
listView.setAdapter(myListAdapter);
}
}
then in your fragment
public class FragmentMain extends Fragment {
private ListView listView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
List<MyItem> items = new ArrayList<MyItem>();
listView = (ListView) view.findViewById(R.id.list);
//new code
new LoadListObjectsTask(getActivity()).execute();
listView.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
Toast.makeText(view.getContext(),
"Clicked " + position, Toast.LENGTH_LONG)
.show();
}
});
return view;
}
public void onResume()... {
//also add the task here so your content is reloaded on resume..
new LoadListObjectsTask(getActivity()).execute();
}
.
.
.
}
If you don't want to do this just make your List of MyItems a private field and move
List<MyItem> items = createListItems();
to onActivityCreated().
Hope that helps!