I am making an android app, and when I open up a page I can then swipe between 'Line Details' and 'Verify Line'. However when I swipe to the second page 'Verify Line' the objects i.e. textboxes in 'Line Details' move across as well but I don't want this to happen, could anyone tell me why? I also have a fragment in my code.
#Override
public int getCount() {
return mCount;
}
#Override
public Fragment getItem(int position) {
Fragment fragment = new LineDetails();
fragment.setArguments(extras);
switch (position) {
case 0:
return new LineDetails();
case 1:
return new VerifyLine();
default:
return new LineDetails();
}
}
#Override
public void finishUpdate(ViewGroup container) {
super.finishUpdate(container);
}
Related
I am coding in Java in Android Studio and I am currently making an Calendar app that save, show, edit events. I had to put some view, like MonthView WeekView and DailyView. At first I made it in a way so all of them were activities and when i want to go back the back pressed button almost done my job. Because of some odds, I turned out to keep only one activity and do the same job with some methods instead of making activities. I have a navigation drawer, so in onItemNavigationClick the user can select which view want. So, my problem is that I cant find a way to act the previous method, like if I go to WeekView and press backButton, get back in Month or in Daily view.
As I see backpressed is to go back an activity, so I think it cant help me. I would apreciate any help.
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menuSchedule:
setAllEvents();
drawerLayout.closeDrawer(GravityCompat.START);
break;
case R.id.daysView:
setDaily();
drawerLayout.closeDrawer(GravityCompat.START);
break;
case R.id.weekView:
setWeek();
drawerLayout.closeDrawer(GravityCompat.START);
break;
case R.id.monthView:
setMonthView();
drawerLayout.closeDrawer(GravityCompat.START);
break;
case R.id.refreshItem:
finish();
startActivity(getIntent());
break;
case R.id.syncItem:
break;
default:
onNavigationItemSelected(item);
}
return true;
}
Example of how my set Methods are working:
private void setMonthView() {
monthYearText.setText(monthYearFromDate(CalendarUtils.selectedDate));
ArrayList<LocalDate> daysInMonth = daysInMonthArray();
CalendarAdapter calendarAdapter = new CalendarAdapter(daysInMonth, this, getApplicationContext());
RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getApplicationContext(), 7);
calendarRecyclerView.setLayoutManager(layoutManager);
calendarRecyclerView.setAdapter(calendarAdapter);
ViewGroup.LayoutParams params = calendarRecyclerView.getLayoutParams();
params.height=1500;
calendarRecyclerView.setLayoutParams(params);
monthListView.setVisibility(View.GONE);
monthYearText.setVisibility(View.VISIBLE);
daysOfWeekDaily.setVisibility(View.GONE);
daysOfWeek.setVisibility(View.VISIBLE);
prevMonth.setVisibility(View.VISIBLE);
nextMonth.setVisibility(View.VISIBLE);
calendarRecyclerView.setVisibility(View.VISIBLE);
nestedScrollView.setVisibility(View.VISIBLE);
}
SOLUTION
Thanks to David Wesser, the code works in the way I want to, here source code of my problem:
public void onMyBackPressed() {
// Pop current view type off the stack
stack.removeFirst();
// Check the previous view type
String previousViewType = stack.peekFirst();
if (previousViewType == null) {
// Nothing to go back to, so finish this Activity
super.onBackPressed();
return;
}
if (previousViewType.equals("daily")) {
setDaily();
} else if (previousViewType.equals("week")) {
setWeek();
} else if (previousViewType.equals("all"))
{
setAllEvents();
}else if (previousViewType.equals("month"))
{
setMonthView();
}
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menuSchedule:
setAllEvents();
stack.addFirst("all");
drawerLayout.closeDrawer(GravityCompat.START);
break;
case R.id.daysView:
setDaily();
stack.addFirst("daily");
drawerLayout.closeDrawer(GravityCompat.START);
break;
case R.id.weekView:
setWeek();
stack.addFirst("week");
drawerLayout.closeDrawer(GravityCompat.START);
break;
case R.id.monthView:
setMonthView();
stack.addFirst("month");
drawerLayout.closeDrawer(GravityCompat.START);
break;
case R.id.refreshItem:
finish();
startActivity(getIntent());
break;
case R.id.syncItem:
break;
default:
onNavigationItemSelected(item);
}
return true;
}
You can do this by building a stack that represents the type of view you are showing. Whenever the user selects something from the navigation drawer, you call the method to show that view and you push something on the stack (a String or an Integer constant) that represents the kind of view you are showing. Then override onBackPressed() so that instead of the default behaviour (which is to finish the current Activity and return to the previous Activity), you so something like this:
Pop off the last thing from your stack (this represents the currently shown view)
If there is nothing left on the stack, you should call the default behaviour with super.onBackPressed(), which will send the user back to the HOME screen or whatever
Otherwise, examine the topmost thing on the stack (this represents the previously shown view) and use it to call the appropriate method to show the view that it represents. In this case remember not to push a new thing onto the stack because the top item on the stack already represents the view that is being shown.
Code Example:
// Declare the stack as a member variable in the Activity
ArrayDeque<String> stack = new ArrayDeque<String>();
inside your switch statement, something like this for each different view type:
case R.id.daysView:
setDaily();
// push the current view type onto the stack
stack.addFirst("day");
case R.id.weekView:
setWeek();
// push the current view type onto the stack
stack.addFirst("week");
etc...
Now override the behaviour of onBackPressed():
#Override
public void onBackPressed() {
// Pop current view type off the stack
stack.removeFirst();
// Check the previous view type
String previousViewType = stack.peekFirst();
if (previousViewType == null) {
// Nothing to go back to, so finish this Activity
super.onBackPressed();
return;
}
if (previousViewType.equals("day")) {
setDaily();
} else if (previousViewType.equals("week")) {
setWeekly();
} else ... // rest of the view types here
}
I am trying to use a GitHub library (MeowBottomNavigation)in Android Studio.But its written in kotlin and i cant use the listeners in it.
The only thing which is given is this
bottomNavigation.setOnShowListener {
}
bottomNavigation.setOnClickMenuListener {
}
the suggestions shows to use
(Function1)
i am not sure as to how to implement this in java . Any help will be appreciated.
I am familiar with java but the library is written in Kotlin. Is there any way to use these listeners in java?
bottomNavigation.setOnClickMenuListener(new
Function1<MeowBottomNavigation.Model, Unit>() {
#Override
public Unit invoke(MeowBottomNavigation.Model p1) {
int i = p1.getId();
switch (i){
case 4:
Toast.makeText(UserMainActivity.this, i, Toast.LENGTH_SHORT).show();
break;
case 1:
Toast.makeText(UserMainActivity.this, i, Toast.LENGTH_SHORT).show();
break;
case 2:
Toast.makeText(UserMainActivity.this, i, Toast.LENGTH_SHORT).show();
break;
case 3:
Toast.makeText(UserMainActivity.this, i, Toast.LENGTH_SHORT).show();
break;
}
return Unit.INSTANCE;
}
});
Function0, Function1, Function2, ... FunctionN are higher-order functions in kotlin.
After converting to java, your click listeners become something like below.
// Set Menu Click Listener
bottomNavigation.setOnClickMenuListener(new Function1<MeowBottomNavigation.Model, Unit>() {
#Override
public Unit invoke(MeowBottomNavigation.Model p1) {
return Unit.INSTANCE;
}
});
// Set Menu Show listener
bottomNavigation.setOnShowListener(new Function1<MeowBottomNavigation.Model, Unit>() {
#Override
public Unit invoke(MeowBottomNavigation.Model s) {
return Unit.INSTANCE;
}
});
something like This::
bottomNavigation.setOnShowListener( new IBottomNavigationListener(Model model)
{
} );
if you are using fragments
//1.-declare fragments globally in your activity
private HomeFragment homeFragment = new HomeFragment();
//2.- declare a method to switch between fragments
public void loadFragment(Fragment fragment){
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.yourFragmentContainer,fragment);
transaction.commit();
}
//3.- in the Set Menu Click/show Listener call the fragment to show
// Set Menu Click Listener
bottomNavigation.setOnClickMenuListener(new Function1<MeowBottomNavigation.Model, Unit>() {
#Override
public Unit invoke(MeowBottomNavigation.Model model) {
int i = model.getId();
switch (i){
case 1:
loadFragment(homeFragment);
break;
//...other cases
}
return Unit.INSTANCE;
}
});
// Set Menu Show listener
bottomNavigation.setOnShowListener(new Function1<MeowBottomNavigation.Model, Unit>() {
#Override
public Unit invoke(MeowBottomNavigation.Model model) {
int i = model.getId();
switch (i){
case 1:
loadFragment(homeFragment);
break;
//...other cases
}
return Unit.INSTANCE;
}
});
use
implementation 'com.etebarian:meow-bottom-navigation-java:1.2.0'
for details watch
https://www.youtube.com/watch?v=MiphbOtSyWY
So I have bottom navigation bar with 4 fragments for each tab, and inside each one I call an API request to fetch some data, but the problem is each time I press any tab of the bar, at least two of the fragments gets created and they call their own method and by extension they fires the API request..! I just want the fragment that I select to be instantiated.!
I know the adapter behaves like this to pre-render the fragment to ensure better transaction between tabs and whatnot..! but I really can't afford to call multiple api calls with each select..!
Adapter
public class My_PagerAdapter extends FragmentPagerAdapter {
// I've tried FragmentStatePagerAdapter but same thing
public My_PagerAdapter (FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
new MyFragment_1();
case 1:
new MyFragment_2();
case 2:
new MyFragment_3();
case 3:
new MyFragment_4();
}
}
#Override
public int getCount() {
return 4;
}
}
Edit
This how I call the adapter..
ViewPager viewPager = main.findViewById(R.id.vp);
viewPager.setOffscreenPageLimit(1);
viewPager.setAdapter(new My_PagerAdapter (getChildFragmentManager()));
navigationTabBar.setModels(models); // just UI stuff for each tab offered by the bottom navigation bar library,
navigationTabBar.setViewPager(viewPager);
I ran into this same exact issue on a project that I'm working on
The solution for me was to add the API calls on the OnResume method in each fragment.
That way they will only be triggered when the fragment is fully visible.
Check out the fragment lifecycle
Ok this is exactly an issue that i faced. The solution i have does not stop the viewpager from creating the fragments but it will stop the calls to network api.
Heres the gist:
1) Create an interface
public interface ViewPagerLifeCycleManagerInterface {
void onResumeAndShowFragment();
void onPauseAndHideFragment();
//void refreshFragment();
}
2) modify your FragmentPagerAdapter to override the onInstantiateItem method
Here each Fragment will have a weakReference declared inside the Adapter class in order to store a reference to the fragments created
#Override
public Object instantiateItem(ViewGroup container, int position){
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
switch (position){
case 0:
xyzWeakReference=null;
xyzFragmentWeakReference=new WeakReference<>((xyz)createdFragment);
break;
case 1:
xyz1WeakReference=null;
xyz1WeakReference=new WeakReference<>((xyz1WeakReference)createdFragment);
break;
}
return createdFragment;
};
3) Inside the FragmentPagerAdapter, add the following method in order to fetch the weak reference of the fragment in picture
public Fragment getFragmentAtGivenPosition(int i){
switch (i){
case 0:
if(xyzFragmentWeakReference == null){
return null;
}
return xyzFragmentWeakReference.get();
case 1:
if(xyz1FragmentWeakReference == null){
return null;
}
return xyz1FragmentWeakReference.get();
}
}
4) Now in the activity where the TabLayout is created and the view pager instantiated, attach a listener to the TabLayout for listening to tab changes
tabLayout_bookmarks.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(final TabLayout.Tab tab) {
//let the instantiateItem have some time to be called by the adapter
currentFragmentIndex = tab.getPosition();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ViewPagerLifeCycleManagerInterface currentFragment = (ViewPagerLifeCycleManagerInterface)btca.getFragmentAtGivenPosition(tab.getPosition());
if(currentFragment!=null){
currentFragment.onResumeAndShowFragment();
}else{
//Log.d("FragmentCreate","Current fragment is null and fucked up in adapter");
//if it is null ... that means the adapter hasn't yet called instantiate item ... this internally calls get item any way
//.....
//This shouldn't really hit but in case it does ... keep a handler in order to ensure that everything is created
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ViewPagerLifeCycleManagerInterface localFragment = (ViewPagerLifeCycleManagerInterface)btca.getItem(tab.getPosition());
//getItem never returns a null fragment unless supplied a horrendous value for position
//by the time these 50 ms pass, the instantiate item should surely have been called
//else it will be an empty space ... no crash though
localFragment.onResumeAndShowFragment();
}
},50);
}
}
},100);
}
#Override
public void onTabUnselected(final TabLayout.Tab tab) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ViewPagerLifeCycleManagerInterface currentFragment = (ViewPagerLifeCycleManagerInterface)btca.getFragmentAtGivenPosition(tab.getPosition());
if(currentFragment!=null){
currentFragment.onPauseAndHideFragment();
}else{
//Log.d("FragmentCreateTab","the fucking fragment was null");
//if it is null ... that means the adapter hasn't yet called instantiate item ... this internally calls get item any way
//.....
//This shouldn't really hit but in case it does ... keep a handler in order to ensure that everything is created
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
ViewPagerLifeCycleManagerInterface localFragment = (ViewPagerLifeCycleManagerInterface)btca.getItem(tab.getPosition());
//getItem never returns a null fragment unless supplied a horrendous value for position
//by the time these 50 ms pass, the instantiate item should surely have been called
//else it will be an empty space ... no crash though
localFragment.onPauseAndHideFragment();
}
},50);
}
}
},100);
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
//do nothing
}
});
5) In each of the Fragments inside the Viewpager, implement the Interface we created in step 1 and override the methods.
Create a boolean variable in each fragment amIVisible... This will help decide when the fragment is visible and when it can call the network api
a) here for the first fragment in viewpager, i.e at 0 index, the network api call has to happen immediately after the view gets created. This fragment is obviously visible by default. This is written inside onCreateView method
if(dataList!=null && dataList.size()==0) {
if (savedInstanceState==null) {
//your api call to load from net
} else {
if (savedInstanceState.getBoolean("savedState")) {
//If you have saved data in state save, load it here
} else {
//only fire the async if the current fragment is the one visible, else the onResumeAndShowFragment will trigger the same async when it becomes visible
if (savedInstanceState.getBoolean("amIVisible")) {
//Load data from net
}
}
}
}
The other methods are as follows for the first fragment
#Override
public void onResumeAndShowFragment() {
amIVisible=true;
if(dataList!=null && dataList.size()==0){
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//Load data from net if data was not found,
//This basically means auto refresh when user scrolls back and the fragment had no data
}
},400);
}
}
#Override
public void onPauseAndHideFragment() {
amIVisible=false;
}
Here i have overriden onSaveInstanceState method and saved the value of amIVisible and savedState is a boolean which indicates if the list has at least 1 item or not.
b) For the other fragments, Data will be loaded by the following process
if(savedInstance!=null){
if (savedInstance.getBoolean("savedState")) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//load data from saved State
}
},100);
} else {
//only fire the async if the current fragment is the one visible, else the onResumeAndShowFragment will trigger the same async when it becomes visible
if (savedInstance.getBoolean("amIVisible")) {
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
//load data from net
}
},100);
}
}
}
The interface methods are the same for the other fragments.
This is pretty complicated but does the job. The weak references inside the adapter even allow garbage collection and avoid context leaks.
I have a view pager with 2 fragments. One of them has a blue screen that should be hidden once the fragment is selected. So, I want to grasp a pointer to this fragment that I can call a certain method on once the user selects this fragment. I have implemented the addOnPageChangeListener interface. I want to call that method, to hide the blue screen, in the onPageSelected call.
My question is how can I grasp a pointer to the current selected fragment?
I have tried the following:
AnswersFragment fragment = (AnswersFragment) getSupportFragmentManager().findFragmentByTag("android:switcher:" + this.pager + ":" + position);
if (fragment == null)
fragment = new AnswersFragment();
But it always returns null.
I have also tried to keep an instance variable called currentAnswerFragment, but it doesn't necessarily point to the current fragment as the view pager keeps track of 3 fragments at the same time.
Also, my model is not an array (or 2) of fragments. I call a function that initializes a fragment and return it. With that said, I can't use the index of the current item to get the fragment being displayed.
You need to override onPageSelected method and fire respected method on desired screen, for example:
#Override
public void onPageSelected(int position) {
if (position == 0) {
callForPage();
} else if (position == 1) {
callForPage1();
}
}
and you can use following code to go to selected screen.
viewPager.setCurrentItem(pageposition,false);
setCurrentItem takes two parameters: item number and boolean for smooth scrolling.
UPDATE
make a PageAdapter class for example
public class PagerAdapter extends FragmentPagerAdapter {
Context mcontext;
public PagerAdapter(FragmentManager fm, Context context) {
super(fm);
mcontext = context;
}
#Override
public Fragment getItem(int position) {
if (position == 0) {
return new FragmentA();
}
else if(position==1){
return new FragmentB();
}
else
return new FragmentC();
}
#Override
public int getCount() {
return 3;
}
}
Then in your Activity class
mViewPager.setAdapter(new PagerAdapter(getSupportFragmentManager(), this));
mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
///Here you handle the pointer thing
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
Edit:
A better implementation of the solution I came up with is found here
After a while of debugging, I figured out a solution.
By keeping an external data structure of the previous fragments visited. In my case I kept a HashMap as a normal array won't work. In some cases I call the pager.setCurrentItem(position). Meaning that I jump fragments. So a HashMap is more appropriate.
Below is the code for my submenu buttons and I'm trying to make it delete the note and return to the main list view. The delete option is called "Red" for now.
I copied my delete code from my main activity thinking it would work, but it does not. I'm very new to android coding, so help would be appreciated.
This is how I delete in my Main Activity.java
#Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuInfo;
currentNoteId = (int)info.id;
menu.add(0, MENU_DELETE_ID, 0, "Delete");
}
#Override
public boolean onContextItemSelected(MenuItem item) {
if (item.getItemId() == MENU_DELETE_ID) {
Noteitem note = notesList.get(currentNoteId);
datasource.remove(note);
refreshDisplay();
}
return super.onContextItemSelected(item);
}
Here is my code for my NoteEditorActivity.java
Again I'm trying to delete, but I can't seem to figure out how to delete the note from the submenu.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_exit:
EditText et = (EditText)findViewById(R.id.noteText);
if (et.length() > 0) {
saveAndFinish();
}
else
{
finish();
}
case R.id.menu_red:
currentNoteId = (int) MENU_DELETE_ID;
datasource.remove(note);
return true;
default:
return super.onOptionsItemSelected(item);
}
Put break statements in your switch case
switch (item.getItemId())
{
case R.id.action_exit:
EditText et = (EditText)findViewById(R.id.noteText);
if (et.length() > 0){
saveAndFinish();
}else{
finish();
}
//you are missing this!!!
break;
case R.id.menu_red:
datasource.remove(note);
finish();
break;
default:
return super.onOptionsItemSelected(item);
break;
}
Try reading this here also: http://docs.oracle.com/javase/tutorial/java/nutsandbolts/switch.html
its just weird..you aren't calling notesList.add() method anywhere in your code, so I just think it's empty at all..
you are certainly missing the break statement there, but I guess it is not the issue why your note is not deleted after clicking in the menu item. You are saving your note in the "previous" (in terms of backstack) activity? if so, you might try to just alter the return code for the setActivityResult() call (or add some extras to the intent) and then check for it in your onActivityResult() callback..
because right now everytime you close the activity via back key, the notes is saved (the saveAndFinish() method gets called);
please describe better where you actually do save notes (to DB or so) and where you wanna delete them..I could then provide you with some code snippet probably.