I'm trying to integrate azure's app service with my app. In particular, I'm trying to store data on the database hosted by Azure. I can insert data fine, but whenever I try to retrieve it, my app freezes.
MainActivity- starts the activity and initializes the azure connection and table.
public class MainActivity extends ActionBarActivity {
/**
* TODO: view data, put data in ordered lists, rate data, scheduler
* **/
Toolbar toolbar;
ViewPager pager;
ViewPageAdapter adapter;
SlidingTabLayout tabs;
CharSequence Titles[]={"New","Trending","Famous"};
int Numboftabs =3;
MobileServiceClient client;
MobileServiceTable<Post> postTable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Creating The Toolbar and setting it as the Toolbar for the activity
toolbar = (Toolbar) findViewById(R.id.tool_bar);
setSupportActionBar(toolbar);
//manually set action bar color since not happening before
getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
// Creating The ViewPagerAdapter and Passing Fragment Manager, Titles fot the Tabs and Number Of Tabs.
adapter = new ViewPageAdapter(getSupportFragmentManager(),Titles,Numboftabs);
// Assigning ViewPager View and setting the adapter
pager = (ViewPager) findViewById(R.id.pager);
pager.setAdapter(adapter);
// Assiging the Sliding Tab Layout View
tabs = (SlidingTabLayout) findViewById(R.id.tabs);
tabs.setDistributeEvenly(true); // To make the Tabs Fixed set this true, This makes the tabs Space Evenly in Available width
// Setting Custom Color for the Scroll bar indicator of the Tab View
tabs.setCustomTabColorizer(new SlidingTabLayout.TabColorizer() {
#Override
public int getIndicatorColor(int position) {
return getResources().getColor(R.color.colorAccent);
}
});
// Setting the ViewPager For the SlidingTabsLayout
tabs.setViewPager(pager);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
postPrompt();
}
});
//initialize azure
try {
client = new MobileServiceClient("https://firechat.azurewebsites.net", this);
postTable = client.getTable(Post.class);
} catch (MalformedURLException e) {
e.printStackTrace();
}
//testing
//Post post = new Post("haha, so funny :p");
//postTable.insert(post);
}
private String makePost(String input){
Post post = new Post(input);
postTable.insert(post);
return post.text;
}
}
return post.text;
}
}
Tab1- a fragment that populates a list with data retrieved from the server. I believe my error is here when I try to query azure for the data.
public class Tab1 extends Fragment {
MainActivity mainActivity;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.tab_1,container,false);
return v;
}
#Override
public void onStart() {
super.onStart();
try {
//get data from azure
mainActivity = (MainActivity) getActivity();
List<Post> postList= mainActivity.postTable.execute().get();
//List<Post> postList = new ArrayList<Post>();
//set lists
ListView newListView = (ListView) mainActivity.findViewById(R.id.newListView);
NewAdapter newAdapter = new NewAdapter(mainActivity, postList);
newListView.setAdapter(newAdapter);
}catch(Exception e){
}
}
}
I found out I was having the error because you must retrieve data from Azure using an async task. I left an example of one down below.
private void retrievePosts(){
AsyncTask<Void,Void,Void> task = new AsyncTask<Void,Void,Void>(){
#Override
protected Void doInBackground(Void... params) {
try {
postList= mainActivity.postTable.execute().get();
Log.e("TAG",postList.get(0).text);
newAdapter.updateList(postList);
} catch (final Exception e) {
}
return null;
}
};
runAsyncTask(task);
}
Related
I'm using Fragments to change scenes in my application, and I've come across the problem where when the phone back button is pressed, the application closes. How would I make it so when the back button is pressed, the previous fragment opens.
I searched for solutions and there are quite a few like using getChildFragmentManager or back stack. I just don't know how to implement it for my specific state pager adapter, of which I followed a youtube video tutorial for.
SectionsStatePagerAdapter.java:
public class SectionsStatePagerAdapter extends FragmentStatePagerAdapter {
private final ArrayList<Fragment> mFragmentList = new ArrayList<>();
private final ArrayList<String> mFragmentTitleList = new ArrayList<>();
public SectionsStatePagerAdapter(#NonNull FragmentManager fm, int behavior) {
super(fm, behavior);
}
public SectionsStatePagerAdapter(FragmentManager supportFragmentManager) {
super(supportFragmentManager);
}
public void addFragment(Fragment fragment, String title){
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#NonNull
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
}
MainActivity.java:
public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener {
public SectionsStatePagerAdapter mSectionsStatePagerAdapter;
private ViewPager mViewPager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
//Fragment Stuff:
mSectionsStatePagerAdapter = new SectionsStatePagerAdapter(getSupportFragmentManager());
mViewPager = findViewById(R.id.container);
//SetUpPager:
setupViewPager(mViewPager);
}
private void setupViewPager(ViewPager viewPager){
SectionsStatePagerAdapter adapter = new SectionsStatePagerAdapter(getSupportFragmentManager());
adapter.addFragment(new MainPageFragment(),"MainPage"); //0
adapter.addFragment(new CoffeePageFragment(),"CoffeePage"); //1
adapter.addFragment(new RegularCoffeeFragment(),"RegularCoffeePage"); //2
viewPager.setAdapter(adapter);
}
public void setViewPager(int fragmentNumber){
mViewPager.setCurrentItem(fragmentNumber);
}
}
CoffeePageFragment.java:
public class CoffeePageFragment extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.content_coffee_page,container,false);
btnRegularCoffee = view.findViewById(R.id.regular_coffee_button);
btnRegularCoffee.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
((MainActivity)getActivity()).setViewPager(2);
}
});
return (view);
}
}
In conclusion, how do I make it so when the device back button is pressed, it will go back to the previous fragment?
In previous versions of the AppCompatActivity implementation it was enough to override onBackPressed() in your activity, newer versions dictate to implement the OnBackPressedCallback, like shown here: https://developer.android.com/reference/androidx/activity/OnBackPressedDispatcher.html
public class FormEntryFragment extends Fragment {
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
OnBackPressedCallback callback = new OnBackPressedCallback(
true // default to enabled
) {
#Override
public void handleOnBackPressed() {
showAreYouSureDialog();
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(
this, // LifecycleOwner
callback);
}
}
It might still be ok to use onBackPressed on the Activity level, though.
I have weird glitch that I have hard time explaining with words. So a recorded this video to show it. FAB opens a activity which has a EditText and a confirm button. When you press the confirm button the name that written to EditText will be added to a RecyclerView in Library fragment. But if you don't write anything to EditText and press confirm it will add "Johan". But here is the thing, if you didn't even click to EditText it will add Johan to ArrayList but it won't show it until you add another name "properly" or restart the Players activity.
Library Fragment:
public class SetupplayerLibraryFragment extends Fragment {
private static String TAG = "SetupplayerLibraryFragment";
static View view;
InternalOperator IO;
RecyclerView libraryList;
RecyclerView.Adapter adapter;
RecyclerView.LayoutManager manager;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
view = inflater.inflate(R.layout.fragment_setupplayer_library, container, false);
libraryList = view.findViewById(R.id.libraryList);
libraryList.addItemDecoration(new DividerItemDecoration(libraryList.getContext(), DividerItemDecoration.VERTICAL));
manager = new LinearLayoutManager(getActivity());
adapter = new PlayerAdapter(IO.players);
libraryList.setLayoutManager(manager);
libraryList.setAdapter(adapter);
return view;
}
}
New Player Activity:
public class SetupplayerAddActivity extends AppCompatActivity {
private static String TAG = "SetupplayerAddActivity";
EditText nameText;
Button confirmButton;
Toolbar toolbar;
InternalOperator IO;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_setupplayer_add);
nameText = findViewById(R.id.text_add_name);
confirmButton = findViewById(R.id.button_add_confirm);
confirmButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (nameText.getText().length() != 0) {
if (ContextCompat.checkSelfPermission(SetupplayerAddActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
nameText.clearComposingText();
ExternalOperator.savePlayers(nameText.getText().toString());
IO.players.add(new Player(nameText.getText().toString(), 0));
finish();
} else {
Toast.makeText(SetupplayerAddActivity.this, "Permission to use storage is needed for this action!", Toast.LENGTH_SHORT).show();
}
} else {
if (ContextCompat.checkSelfPermission(SetupplayerAddActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
ExternalOperator.savePlayers("Johan");
IO.players.add(new Player("Johan", 0));
finish();//Activityi kapıyor
} else {
Toast.makeText(SetupplayerAddActivity.this, "You are really the Johan! Radiance save us!", Toast.LENGTH_SHORT).show();
}
}
}
});
}
}
I removed some of the useless codes to make it more clear.
You have to call RecyclerView.Adapter.notifyDataSetChanged() everytime you add or delete or update the list that populates RecyclerView to make it aware of the changes.
For Example
IO.players.add(new Player(nameText.getText().toString(), 0));
adapter.notifyDataSetChanged().
In the following code I tried to read data from Firebase, and use this to create dynamically cardviews, but when the program starts, the ArrayList is empty while I click on the third tab, and back to the first. After a few days of finding out the problem, I couldn't solve this by myself.
MainScreen.java:
public class MainScreen extends AppCompatActivity {
private SectionsPagerAdapter mSectionsPagerAdapter;
private ViewPager mViewPager;
private static final String TAG = "MainScreen";
public List<BusNumber> jaratok= new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_screen);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
TabLayout tabLayout = findViewById(R.id.tabs);
tabLayout.getTabAt(0).setText("Járatok");
tabLayout.getTabAt(1).setText("Megállók");
tabLayout.getTabAt(2).setText("Tervező");
FirebaseDatabase database = FirebaseDatabase.getInstance();
DatabaseReference myRef = database.getReference("bus_number");
myRef.addValueEventListener((new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
jaratok = new ArrayList<>();
for(DataSnapshot ds : dataSnapshot.getChildren()) {
jaratok.add(new BusNumber(ds.child("CodeNumber").getValue(String.class),ds.child("BusRouteName").getValue(String.class)));
}
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
Log.w("TAG","Failed to read data.",databaseError.toException());
}
}));
mSectionsPagerAdapter.notifyDataSetChanged();
mViewPager.addOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout));
tabLayout.addOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(mViewPager));
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Fejlesztés alatt", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position)
{
case 0:
JaratokTab1 tab1 = new JaratokTab1();
return tab1;
case 1:
MegallokTab2 tab2 = new MegallokTab2();
return tab2;
case 2:
TervezoTab3 tab3 = new TervezoTab3();
return tab3;
default:
return null;
}
}
}
JaratokTab1.java:
public class JaratokTab1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
final View rootView = inflater.inflate(R.layout.jaratok_tab1, container, false);
MainScreen m2 = (MainScreen) getActivity();
for (BusNumber n : m2.jaratok) {
Log.d("Ertek: ", n.getName());
}
CardView cardView = new CardView(getContext());
TextView tv = new TextView(getContext());
FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
tv.setLayoutParams(params);
cardView.setLayoutParams(params);
cardView.setVisibility(View.VISIBLE);
cardView.setCardElevation(9);
cardView.setPadding(15, 15, 15, 15);
cardView.setRadius(9);
tv.setTextColor(Color.RED);
tv.setBackgroundColor(getResources().getColor(R.color.colorPrimary));
cardView.addView(tv);
LinearLayout linearLayout = rootView.findViewById(R.id.linearID);
linearLayout.setPadding(60,15,50,30);
linearLayout.addView(cardView);
return rootView;
}
//also tried this:
/* #Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
MainScreen m2 = (MainScreen) getActivity();
for (BusNumber n : m2.jaratok) {
Log.d("Ertek: ", n.getName());
}
}*/
}
By the time you are looping through m2.jaratok, the data isn't still available and that's why your list is empty. Firebase APIs are asynchronous, meaning that onDataChange() method returns immediately after it's invoked, and the callback from the Task it returns, will be called some time later.
There is no guarantee about how long it will take, it may take from a few hundred milliseconds to a few seconds before that data is available and can be used. Because that method returns immediately, your jaratok list you're trying to use it outside the callback is empty.
Basically, you're trying use a value synchronously from an API that's asynchronous. That's not a good idea. You should handle the APIs asynchronously as intended.
A quick solve for this problem would be to use jaratok list only inside the onDataChange() method, otherwise I recommend you see the last part of my anwser from this post in which I have explained how it can be done using a custom callback. You can also take a look at this video for a better understanding.
I was looking answer in similar topics but nothing works for me. I have Activity which has a fragment. This fragment has adapter. Everything is fine even if I rotate screen, but when I rotate screen and try to update adapter (for example by sort items in adapter - method sort) I get NULL on adapter.
When Activity starts first time this is happening:
ON CREATE (ACTIVITY)
SETUP VIEW PAGER (ACTIVITY)
CONSTRUCTOR (FRAGMENT)
SET DATA IN ADAPTER (ACTIVITY)
ON CREATE (FRAGMENT)
CREATE ADAPTER (FRAGMENT)
ON CREATE VIEW (FRAGMENT)
UPDATE ADAPTER (FRAGMENT)
ON ACTIVITY CREATED (FRAGMENT)
In the second time when I rotate screen:
ON CREATE (ACTIVITY)
CONSTRUCTOR (FRAGMENT)
ON CREATE (FRAGMENT)
CREATE ADAPTER (FRAGMENT)
SETUP VIEW PAGER (ACTIVITY)
CONSTRUCTOR (FRAGMENT)
SET DATA IN ADAPTER (ACTIVITY)
ON CREATE VIEW (FRAGMENT)
UPDATE ADAPTER (FRAGMENT)
ON ACTIVITY CREATED (FRAGMENT)
So the problem is that in activity method super.onCreate(savedInstanceState) the fragment is recreated istelf. I was trying pass NULL for super.onCreate(null) and also checking if savedInstance is NULL, changing FragmentPagerAdapter for FragmentStatePagerAdapter, and many others things. What I want to do is save and get fragment, adapter in proper way or just make activity start everytime like in the first time.
PART OF MAIN ACTIVITY
public class MainActivity extends AppCompatActivity {
private TabLayout tabLayout;
private ViewPager viewPager;
SongsFragment songsFragment;
TabAdapter tabAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
setContentView(R.layout.activity_main);
//init work
tabAdapter = new TabAdapter(getSupportFragmentManager());
songsFragment = new SongsFragment();
songsFragment.setData(database.getSongs(readOption(), readOrder()));
tabAdapter.addFragment(songsFragment, "TAB");
viewPager.setAdapter(tabAdapter);
tabLayout.setupWithViewPager(viewPager);
}
public void sort() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setItems(R.array.sorts, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if(which == 0) writeSort("number", "ASC");
else if(which == 1) writeSort("number", "DESC");
else if(which == 2) writeSort("title", "ASC");
else if(which == 3) writeSort("title", "DESC");
songsFragment.setData(database.getSongs(readOption(), readOrder()));
songsFragment.updateAdapter();
}
}).show();
}
}
FRAGMENT
public class SongsFragment extends Fragment {
private RecyclerView recyclerView;
List<Song> songs = new ArrayList<>();
SongsAdapter adapter;
public SongsFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v("TAG", "onCr");
if(!(savedInstanceState == null || !savedInstanceState.containsKey("list"))) {
songs = (List<Song>) savedInstanceState.getSerializable("list");
}
createAdapter();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.v("TAG", "onCrV");
View view = inflater.inflate(R.layout.fragment_songs, container, false);
recyclerView = (RecyclerView) view.findViewById(R.id.fragment_songs_SongsList);
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
recyclerView.setAdapter(adapter);
updateAdapter();
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("list", (Serializable) songs);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public void setData(List<Song> songs) {
this.songs = songs;
}
public void createAdapter() {
adapter = new SongsAdapter(getContext(), songs);
}
public void updateAdapter() {
adapter.clear();
adapter.addAll(songs);
adapter.notifyDataSetChanged();
}
}
FRAGMENT PAGER ADAPTER
public class TabAdapter extends FragmentStatePagerAdapter {
List<Fragment> fragments = new ArrayList<>();
List<String> titles = new ArrayList<>();
public TabAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return fragments.get(position);
}
#Override
public int getCount() {
return fragments.size();
}
#Override
public CharSequence getPageTitle(int position) {
return titles.get(position);
}
public void addFragment(Fragment fragment, String title) {
fragments.add(fragment);
titles.add(title);
}
}
i have fragment for display the NavigationDrawer. In that i have the first section the header with some information. This information change and with EventBus i control this event, but how can update this fragment?
The implementation below work only if i rotate the device.
extract from MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_activity);
mNavigationNavDrawerFragment = (Nav_DrawerFragment) getFragmentManager().findFragmentById(R.id.fragment_drawer);
mNavigationNavDrawerFragment.setup(R.id.fragment_drawer, (DrawerLayout) findViewById(R.id.drawer), mToolbar);
inizialized(mServices);
}
....
....
EventBus.getDefault().postSticky(new PersonDetails(mNomeCognome, mEmail));
//HERE HOW CAN REFRESH OR RECREATE THIS mNavigationNavDrawerFragment?
}
Extract from Nav_DrawerFragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_navigation_drawer, container, false);
mDrawerList = (RecyclerView) view.findViewById(R.id.drawerList);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
mDrawerList.setLayoutManager(layoutManager);
mDrawerList.setHasFixedSize(true);
final List<NavigationItem> navigationItems = getMenu();
//HERE I SET THE UPDATE DATA
Nav_adapter adapter = new Nav_adapter(navigationItems,mNomeCognome,mEmail);
adapter.setNavigationDrawerCallbacks(this);
mDrawerList.setAdapter(adapter);
selectItem(mCurrentSelectedPosition,items);
return view;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().registerSticky(this);
}
public void onEventMainThread(PersonDetails event){
mNomeCognome = event.mNomeCognome;
mEmail = event.mEmail;
}
public void setup(int fragmentId, DrawerLayout drawerLayout, Toolbar toolbar) {
mFragmentContainerView = getActivity().findViewById(fragmentId);
mDrawerLayout = drawerLayout;
mActionBarDrawerToggle = new ActionBarDrawerToggle(getActivity(), mDrawerLayout, toolbar, R.string.drawer_open, R.string.drawer_close) {
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
if (!isAdded()) return;
getActivity().invalidateOptionsMenu();
}
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
if (!isAdded()) return;
getActivity().invalidateOptionsMenu();
}
};
I can see you're using a RecyclerView with the adapter
Nav_adapter adapter = new Nav_adapter(navigationItems,mNomeCognome,mEmail);
and then you're updating the values that were used to create the adapter on the event
mNomeCognome = event.mNomeCognome;
mEmail = event.mEmail;
the problem is that you didn't pass those values to the adapter.
So you'll have to create a way to pass those updated values to the existing adapter, something like this:
// first define your adapter as a field:
Nav_adapter adapter;
// then on your event
public void onEventMainThread(PersonDetails event){
mNomeCognome = event.mNomeCognome;
mEmail = event.mEmail;
adapter.setNewValues(mNomeCognome, mEmail);
}
then inside this setNewValues you update the adapter values and don't forget to call one of the notifyItemChanged or notifyItemRangeChanged method on the adapter so that the new value can be updated on the screen.