Deleting a View in a class method - java

In my activity I try to call a method from a class but it's not getting the right values.
This is ActivityTwo:
int id = intent.getIntExtra("id", 0);
LayoutInflater inflater = getLayoutInflater();
View mainActivity = inflater.inflate(R.layout.activity_main, null);
LinearLayout eventsLayout = mainActivity.findViewById(R.id.eventsLayout);
Log.d("ACTIVITY_eventlayout", String.valueOf(eventsLayout)); // gives the layout perfectly
Event.RemoveSpecific(eventsLayout, id);
finish();
This is the class(Event) with the method:
public static void RemoveSpecific(LinearLayout layout, int id){
View event = layout.findViewById(id);
Log.d("INSIDE_removespecific", String.valueOf(event));// event is null
layout.removeView(event);
}
And in my MainActivity it's working fine:
LinearLayout eventsLayout = findViewById(R.id.eventsLayout);
View event = eventsLayout.findViewById(id);
//Log.d("MAIN_event", String.valueOf(event1)); // gives it perfectly
eventsLayout.removeView(event);
I also add this view in my MainActivity and use .setId(id) to give it the right id. So my question is, why is the View in my class method null, while I pass the right LinearLayout from my activityTwo?
Where does the id come from?
I have the Event class which contains an id, name, date, & description. This class also has a static ArrayList called eventsList. Whenever the user creates a new reminder I create a new Event using my class and giving it an id as Event.eventsList().size() (after adding it to my eventsList [so the first event id is always 1]), then I create a new View and pass the recently created Event details and use setId() to give it an id [1]. Then in this View I have a button which has an onClickListener which passes the given View id and the LinearLayout (where the View was added) to my Event.removeSpecific() method.
App flow:
The user clicks a button in MainActivity to create an event, then the button click then use startActivityForResult() with an intent to open a new Activity where the user puts in a name,description and date for the event, then I create an intent with putExtra() methods and then use setResult(RESULT_OK, resultintent) to send the data back to MainActivity. Now when the MainActivity gets the data back I use .getStringExtra() to create a new Event object with the data and add it to may Event.eventsList ArrayList, then I use the following:
LayoutInflater inflater = getLayoutInflater();
View event_Exmaple = inflater.inflate(R.layout.event_example, null);
and set the textView's with the data I got, and use event_example.setId(Event.eventsList.size()) to give it an id.
After that I add this to my LinearLayout (which I declared already in the MainActivity):
eventsLayout.addView(event_example)
Now I mentioned that the Event class has a date field. I use that to set up an alarm with AlarmManager. With the AlarmManager I send the current Event object's data through the intent and when the user gets the notification it opens up a new Activity which gets the given Event object data (with intent.putExtra()'s) and that's the part where the user clicks on a Button. I want to remove the given Event object's View from my LinearLayout in the MainActivity XML.

well since you didn't share where the id value come from (from where the intent gets its data/how you put the values in it/where the values come from), I assumed this solution :
whenever I use a 'switch' statement with View Ids it tells me a very important warning which is
Resource IDs will be non-final in Android Gradle Plugin version 5.0, avoid using them in switch case statements
that means that view Ids doesn't have a stable id in some cases that you shouldn't consider them to be final.
I guess in your case when you get an id to your event view and then inflate the layout view, the inflate procedure involve changing the 'int' id value of the event view so whenever you query the old id it returns null since the event view inside the inflated layout view now has a new 'int' id value.
I don't know why it works in main activity since you didn't share full code where you get the id from.
so I guess the only solution is that you should find a way to get a fresh copy of the id after you inflate the view you want to work on in the method.
Update
the problem is with this block of code
View mainActivity = inflater.inflate(R.layout.activity_main, null);
everytime you inflate a new layout view thinking that you have the one used by main activity back there but instead you're creating an entirely new one with new views and new Id's.
that would be why you could get the view in main activity but you can't get it from a newly inflated view.
Update
if you need to remove this item using the id and have a static array stored in the view custom class, call a getter on the static array and remove the item from it instead of trying to remove the view itself.
a better implementation of the whole situation is to store these events in a database using room instead of the static array, that way you could delete/add/edit any event anytime with just one line of code, if you're willing to do this you can start by reading the developers documentation of room.

Related

Saving state of UI in Android

I'm making a to-do list app and after user presses the button I create a new GridLayout(and all the data about time and name of the task inside of it) and add it into my RelativeLayout. How do I save those GridLayouts in UI so after the activity is destroyed and launched again those layouts are there.
After pressing the button I trigger the Create Activity method
public void CreateActivity(String name,int hours, int minutes,int i)
{
RelativeLayout.LayoutParams relparams= new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,RelativeLayout.LayoutParams.WRAP_CONTENT);
relparams.addRule(RelativeLayout.BELOW,i);
relparams.setMargins(0,50,0,100);
Glayouts.add(new GridLayout(this));
Glayouts.get(i+1).setBackgroundColor(Color.GRAY);
Glayouts.get(i+1).setMinimumWidth(relative.getWidth());
Glayouts.get(i+1).setId(i+1);
Glayouts.get(i+1).setPadding(10,0,0,0);
GridLayout.LayoutParams namee = new GridLayout.LayoutParams();
namee.columnSpec = GridLayout.spec(0);
namee.rowSpec = GridLayout.spec(0);
namee.setGravity(Gravity.LEFT);
final TextView Actname = new TextView(this);
Actname.setText(name);
GridLayout.LayoutParams checkbox = new GridLayout.LayoutParams();
checkbox.columnSpec = GridLayout.spec(1);
checkbox.rowSpec = GridLayout.spec(0);
checkbox.setGravity(Gravity.RIGHT);
CheckBox check = new CheckBox(this);
// ADDING TO LAYOUT
Glayouts.get(i+1).addView(Actname,namee);
Glayouts.get(i+1).addView(check,checkbox);
relative.addView(Glayouts.get(i+1),relparams);
Theoretically when you extends View, then you can also override onSaveInstanceState and onRestoreInstanceState methods, where you must provide your own SavedState class that typically extends BaseSavedState. You can find info on that here
In your case, your layout is dynamic, therefore this doesn't really work. To tell you the truth, your layout probably shouldn't be constructed this way, you should be rendering the grid using a RecyclerView based on a "model" that describes this layout, render the items of the grid via the RecyclerView.Adapter, and you should persist either the "model", or the data you use to construct this model along with the user-inputted state so that you can re-construct the model that will be rendered via your RecyclerView.
You can read more about RecyclerView here.
You can read more about data persistence here.
You can read about using onSaveInstanceState to save data in Activities/Fragments across config change and process death (but not finishing then restarting the app) here.
You can’t. The best way to save state is to use some persistence mechanism, for example database (I’d recommend Room as it is officially supported by Google).
After clicking a button, you should put all the needed information (name, hours, minutes) in the database and when Activity is created, you can read all persisted data and - basing on it - create all needed layouts again.
Another option is storing data in SharedPreferences - it is much easier to setup, so you can also start with this solution. Please note, I'm suggesting it as a first step in the world of persistency in Android, not as a preferred solution for storing data.

How can I restore the state of an AdapterViewFlipper in a Fragment?

I have a Fragment that shows different view by a AdapterViewFlipper.
The AdapterViewFlipper is being set with MyCustomAdapter that contains 'View 1', 'View 2', 'View 3', and 'View 4', and its in a layout resource file that I inflated in my own Fragment "onCreateView()".
The Problem I'm facing is whenever I rotate my device, the current view in the AdapterViewFlipper goes back to the first view that was added in MyCustomAdapter.
For Example: if the current view in the AdapterViewFlipper is showing 'View 2' and the user rotates the device, it returns back to 'View 1'.
So what I'm trying to do is to restore the current view in the AdapterViewFlipper and its state in the Fragment whenever I rotate my device.
Although I found this method that says I should declare the android:configChanges attribute at the element in the AndroidManifest and it worked like a charm but when I was reading about it Android didn't recommend it.
But this works fine in Activity.
So is there a way I can go through this myself?
So the first thing you need to do, is to make sure you are retaining the fragment itself. And not place a new instance every time your activity is re-created.
You can establish that with a simple check in your onCreate() method.
You can either check if the savedInstance Bundle parameter to onCreate() is null, in that case only you need to replace your fragment OR check if your fragment is already added to your FragmentManager.
if (savedInstanceState == null) {
// This is a brand new activity, and not a re-creation due to config change
getSupportFragmentManager().beginTransaction().replace(id, yourFragmentInstnace, stringTag);
}
OR
if (getSupportFragmentManager().findFragmentByTag(fragmentTag) == null) {
// This is a brand new activity, and not a re-creation due to config change
getSupportFragmentManager().beginTransaction().replace(id, yourFragmentInstnace, fragmentTag);
}
And you also need to call setRetainInstance(true) in your fragment's onCreate() or something.
That will retain the same instance of your fragment during a configuration change.
This should automatically allow your AdapterViewFlipper to maintain its UI state, which is the current item it's showing.
You can find a nice example here

Android ViewPager/PagerAdapter with Data Binding and instantiateItem not executing active view

I am using Android data binding in a MVVM framework. I have a ViewPager setup with a corresponding PagerAdapter. Some of the pages may contain videos. The issue that I am having is that the instantiateItem method in the PagerAdapter always executes the next view in the PagerAdapter instead of the current view. For example say page 1 has no video, but page 2 does. When the user views page 1, the video in page 2 starts playing. Here is the instantiateItem method:
public Object instantiateItem(ViewGroup container, int position) {
LayoutInflater layoutInflater = (LayoutInflater) getApplicationContext().getSystemService(Service.LAYOUT_INFLATER_SERVICE);
ViewDataBinding binding = DataBindingUtil.inflate(layoutInflater, R.layout.main_layout, null, false);
Post post = posts.get(position);
myViewModel = new PostModel(post);
binding.setVariable(BR.model, myViewModel);
container.addView(binding.getRoot());
return binding.getRoot();
}
How can I make the instantiateItem execute the current view instead of the next view? Is the issue related to setOffscreenPageLimit?
You can't because it's main idea of ViewPager - it preload next N pages in order to make swiping smooth. Also actually you can setup it in order to see part of next view.
I think you should use OnPageChangeListener, and manager start/stop of playing of your video when it needed here.
Btw, some advices - you can use that library in order to not write that boilerplate code at all. Here my example of usage.

Programmatically generated text field is not showing up in Android app

I'm trying to programmatically generate some UI elements onto an android app with Java. I have a ScrollView created in the XML file, and I'm trying to create and add a bunch of UI elements to that ScrollView. All of the UI elements make up 1 bounding box that looks like this:
The bounding box is just a linear layout with an image background, and some text boxes and labels sitting on top of it. I want my code to be modular, so I'm making a class called BoundingBox which should create and add a bounding box to the app every time I instantiate the BoundingBox class.
Here's what I have so far for the BoundingBox class (it's not complete, all it adds so far is a textview with text on it).
public class BoundingBox {
//Instantiate the main app activity.
MainActivity theMainActivity = new MainActivity();
//Create Scrollview object and set it to the XML GUI scrollview
ScrollView sv = (ScrollView) theMainActivity.findViewById(R.id.theMainScrollView);
public BoundingBox(){
//Create linear layout that will sit in the scrollview.
LinearLayout mainLinearLayout = new LinearLayout(theMainActivity);
TextView exerciseName = new TextView(theMainActivity);
TextView repBox1 = new TextView(theMainActivity);
TextView repBox2 = new TextView(theMainActivity);
TextView repBox3 = new TextView(theMainActivity);
TextView repBox4 = new TextView(theMainActivity);
//Add the Linear Layout to the existing scroll view.
sv.addView(mainLinearLayout);
//Add text box to linear layout
mainLinearLayout.addView(exerciseName);
exerciseName.setText("This doesn't work.");
}
}
Here's how I instantiate it in my main Activity:
#Override
public boolean onCreateOptionsMenu(Menu menu) {
BoundingBox sc = new BoundingBox();
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
The code compiles successfully but no text shows up in my Android app. If I instantiated the class then it should have already ran the code in the BoundingBox constructor, which should have created a text view. I'm not sure what I'm doing wrong, and since the code compiles successfully logcat doesn't tell me anything. If I instantiate the bounding box class in the onCreate() function, the app crashes when starting it. If I instantiate it in the onCreateOptionsMenu function, it runs but it doesn't create a text view.
Your code is so messed up it isn't even close to working.
1)You should never create an activity via new. Ever. It isn't valid.
2)You create a LinearLayout programmatically. But you add it nowhere. If it isn't added to anywhere, where would you expect it to show?
3)Why would you ever want to create a view in options menu code? And once again, you're creating the box but not doing anything with the views it creates. Also, if you want to create a class that holds a bunch of views, it really ought to derive from ViewGroup.
4)Even if 1 was valid (it isn't), doign a findViewById there would return null, because onCreate wasn't called so no contentView was set. But that isn't valid anyway, so don't try to just call it. Instead you want to either pass in the parent view via constructor, or better yet make BoundingBox a ViewGroup subclass and add it to the parent directly.

android listview pagination implementation

Hi am working on an android application. And am using a listview in some of my activities.
The problem is all of my listviews displayed are much longer so that the user needs to scroll the whole list to go for the last item.
Am trying to implement a pagination for this, like at first say only 20 items need to displayed on the listview. And at the end of my listview i need a titlebar which have next & previous buttons and on clicking on next button the listview will load the next records from 21st to 40 and so on.
Am using java rest webservice to load the listview.
Can anyone give me a good suggestion for solving my problem.?
Solution 1:
You can load all the data at once if its not TOO MUCH, store it locally & then you can navigate in that locally stored data. Define some variables like StartPoint & EndPoint & get the desired data from that stored data. Increment decrement the values of StartPoint & EndPoint by using the PreviouButton & NextButton.
Solution 2:
Get only the desired data from your data source for example 10 records each time when a Navigation button is clicked.
I suggest than you load list data in a custom Adapter class that extends BaseAdapter class. Like #oriolpons suggested, you should add a footer view, and when you click on button next call some method that is fetching next for example 20 rows, and then add them in your adapter object and call notifyDataSetChanged().
For example
private OnClickListener mListener = new OnClickListener() {
public void onClick(View v) {
ArrayList<YourObject> al = getSomeData(int startRow, int endRow);
MyCustomAdapter adapter = new MyCustomAdapter();
for(YourObject a : al)
adapter.add(a);
getListView.setAdapter(adapter);
notifyDataSetChanged();
}
};
Hope this helps.
The easiest solution is to add a footer view to the listview. And on the item click listener you can see if it is the last position (load more items), or not
//add the footer before adding the adapter, else the footer will not load!
View footerView = ((LayoutInflater)this.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(R.layout.listfooter, null, false);
this.getListView().addFooterView(footerView);
#Tijo . Refer this site http://www.androidhive.info/2012/03/android-listview-with-load-more-button/. You can have a button which would call the execute method of Async task and that will load the remaining list for you.

Categories

Resources