In my Android app, I have two custom view classes - PortraitClass and LandscapeClass. They both do the same thing. On running the app, the view class fetches some pictures from an SDCard and then maniputlates (skew etc) and displays them. The only difference between the two classes is that the layout of pictures is slightly different on the screen.
I have two display.xml files (one under layout folder and the other under layout-land). The one under the layout folder adds Portrait and the other adds the Landscape class.
On orientation change, I would like to send information (picture numbers and few bitmaps) from one class to another so I won't have to fetch all the bitmaps again and also so that I display the ones that were being displayed.
I find the parcelable thing kind of confusing. I tried following this_example, but I noticed that in onRestoreInstance, the Parcelable has null for the "mSuperState" and I get a classCastException # "SavedState ss = (SavedState)state". The integer (picture number) that I am trying to pass is there. I am not sure what I am doing wrong.
You could use a global singleton in the Application class. For example an "AssetManager" is available here:
public class YourApplication extends Application {
public AssetManager assetManager = new AssetManager();
#Override
public void onCreate()
{
super.onCreate();
}
}
You can then call it from another activity:
YourApplication application = ((YourApplication ) this.getApplication());
application.assetManager.someFunction();
Not sure if this is what you are looking for but my stuff always works in orientation change with onCreate(Bundle savedInstanceState). Mine is all text in edit text boxes though so I am not sure how it would work for you.
Also check this out about midway down under "Persisting State Information During Configuration Change"
http://www.devx.com/wireless/Article/40792/1954
Related
I'm new to Android programming, and still teaching myself to code.
Currently I'm teaching myself about GridViews and still coding that project with tutorials so I have nothing to show right now, but the basic idea is the following...
If I have images of groceries in GridView in the first activity and when you click an image you will be able to open a new activity with a larger image and you could input the number how many you things you need, like 5 apples or whatever.
All of that is more or less clear to me how to do.
But how would I send the number and image to a new (third) activity with a ListView that would list all the items you need to buy at the grocery store? How would I be able to fill the list only with items after you enter the number on the previous activity with the large picture and click an "OK" or "Add" button or whatever and not list absolutely everything?
Thanks!
It's difficult at first, but you can use an SQLiteDatabase to store the data.
It's not a quick solution for you, but definitely worth learning about if you're serious to learn android. Here's a link to the official stuff:
https://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html
I personally used this tutorial:
http://www.androidwarriors.com/2016/02/android-sqlite-database-tutorial-sqlite.html?m=1
Sharing some data between multiple activities or fragments is a very common situation. One way around it is implementing a Singleton Pattern.
In your case you can design some kind of data structure for your purpose and manage it inside shared singleton class. For example something like this:
public class ShoppingListManager {
private static ShoppingListManager instance = new ShoppingListManager();
private List<ShoppingItem> shoppingList;
public static ShoppingListManager getInstance() {
return instance;
}
public List<ShoppingItem> getShoppingList() {
return shoppingList;
}
public void addShoppingItem(ShoppingItem item) {
this.shoppingList.add(item);
}
...
// Make the constructor private so that this class cannot be instantiated
private ShoppingListManager(){
shoppingList = new ArrayList<String>();
}
}
Then you can access your data anywhere in your code and manage shared data in any way you'd like.
ShoppingListManager.getInstance().getShoppingList();
// You can add items in any activity
ShoppingListManager.getInstance().addShoppingItem(item);
One point to remember never store context in singleton classes as it will lead to memory leaks.
I have an activity which can take a few seconds to load its content (mainly pictures) from the cloud. Let's say this content is pictures & description from a person. The user can go to pictures & description from another person by clicking on the next button. I'd like to avoid the loading time When this button is clicked.
To do this I have two activities : firstPersonActivity for the first person content, and secondPersonActivity for the second person content.
What I try to do is to load the content of the secondPersonActivity when the user is in the firstPersonActivity, so that the secondPersonActivity can be displayed "directly" (= without needing to load content from the cloud when the next button is clicked in the firstPersonActivity).
But I do not succeed in doing this, I don't know how to modify the views of the secondPersonActivity layout from the firstPersonActivity class.
I tested the following code in my firstPersonActivity but it doesn't work (there is no connexion to the cloud, it's just a test to better understand how it works). R.id.first_image_second_person is the id of my imageview in the secondPersonLayout (= the layout used in the secondPersonActivity).
ImageView firstImageSecondPerson = (ImageView) findViewById(R.id.first_image_second_person);
firstImageSecondPerson.setImageResource(R.drawable.mypicture);
When I click on the next button to go from the firstPersonActivity to the secondPersonActivity, my firstImageSecondPerson imageview is not filled.
Do you see what's wrong ?
Is there a better way to avoid the loading time when the user click on the next button ?
It is not possible to change activity if activity instance is not created. In my opinion to do what You need I would go to single activity with hidden content of second person ( VIEW.INVISIBLE ) and show/hide it when it is needed.
But if second activity must be there, then create some structure for saving bitmaps in memory. In Your code sample You get picture from drawable so we are not talking about some delays, but if images are downloaded from some server then You can create some class which will have those bitmaps created from url and instance of this class can be used on any activity.
So for example Your class for caching pictures would have some method for creating bitmaps like -How to load an ImageView by URL in Android?. And those bitmaps should be saved in some Array or HashMap to have access to it, for example ( pseudo code ):
//some structure for cashing pictures in app
class PictureCache {
static PictureCache instance;
final HashMap<Integer, Bitmap> bitmaps;
//singleton
static PictureCache getInstance(){
if (instance==null)
instance = new PictureCache();
return instance;
}
public PictureCache(){
bitmaps = new HashMap<>;
}
private Bitmap loadBitmap(String url);//#1
public addPicture(Integer personId, String href){
//add to hashMap
bitmaps.put(personId, loadBitmap(href));
}
public Bitmap getPicture(Integer personId){
return bitmaps.get(personId);
}
}
#1 - method from How to load an ImageView by URL in Android?
Using it in first activity:
PictureCache.getInstance().addPicture(12,"http://url.to.bitmap");
Using it in second activity:
Bitmap pic = PictureCache.getInstance().getPicture(12);
Important note - above code was written here and was not tested, it shows solution concept.
Important second note - using such approach with bitmaps in memory can cause to much memory usage
You cannot access the views of SecondActivity before its creation. If you called this activity once only then you are able to access its views by making them static.
One more Solution for this is..
Access the whole data at once and save it in static arraylist with the help of getter-setters. Then on SecondActivity set data from that arraylist.
Hope this will work.
I don't think you can access the view before creating the activity.
You can try to use Glide for caching your images and minimizing loading time.
Try this
Picasso.with(getContext()).load(R.drawable.generatedId).into(imageView);
or
imageView.setImageDrawable(ActivityCompat.getDrawable(getContext(),
R.drawable.generatedID));`
I have a screen, that show different user interface when device is lanscape and portrait. It shows a ListView when device is portrait, and it shows a GridView when device is lanscape.
It looks like youtube app or google play store app.
Thanks for reading and answering my question.
EDIT: I want to add more details about my question:
I am using fragment to manage this view.
I need save all data and in this view when it rotate (avoid reloading a lot of data).
I tried to add android:configChanges on manifest and use onConfigurationChanged in this fragment. But it was not success.
I hope see the particular example or a detail solution.
But any answer is appropriated.
Thanks
There is two things to develop this functionnality.
First you will have to make two different layouts:
res
|- layout
|- mylayout.xml
|- layout-land.xml
|- mylayout.xml
In the layout/mylayout.xml file, include a ListView where you need it and in layout-land/mylayout.xml, include your GridView.
Android will select automatically the correct layout, depending of the current screen orientation, when you call setContentView.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mylayout);
mListView = (ListView) findViewById(R.id.mylistview);
mGridView = (GridView) findViewById(R.id.mygridview);
// !!!!!!!
// mListView will be null in landscape
// mGridView will be null in portrait
// !!!!!!!
}
setContentView(R.layout.mylayout);
Now, in code you will need to check in which orientation you are to make if/else statements.
To detect in which orientation you are, follow the following states:
create a file in res/values/bool.xml
<resources>
<bool name="isLandscape">false</bool>
</resources>
create a file in res/values-land/bool.xml
<resources>
<bool name="isLandscape">true</bool>
</resources>
Now in your activity you can easily test, every time you need, if you are in landscape or portrait mode and apply the corresponding action
boolean landscape = getResources().getBoolean(R.bool.isLandscape);
if (landscape) {
// do something with your GridView
} else {
// do something with your ListView
}
To respond to your edit:
Fragments are very similar to Activities for this.
If you want to save data during orientation change, follow the official documentation on this topic, especially the part on Retaining an Object During a Configuration Change.
You should not use the android:configChangesin Manifest as explained in the documentation:
Handling the configuration change yourself can make it much more
difficult to use alternative resources, because the system does not
automatically apply them for you. This technique should be considered
a last resort when you must avoid restarts due to a configuration
change and is not recommended for most applications.
Try this and changing your layout accordingly.
public String getOrientation(Context context){
final int rotation = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay().getOrientation();
switch (rotation) {
case Surface.ROTATION_0:
return "portrait";
case Surface.ROTATION_90:
return "landscape";
case Surface.ROTATION_180:
return "reverse portrait";
default:
return "reverse landscape";
}
}
This method detects and returns the device orientation change. Might be quite useful.
A good way of saving data in an application which is independent of the activities in the application is to have an application object. Although the documentation says "There is normally no need to subclass Application", I find them very useful. Your application class needs to be mentioned in the application manifest, and it gets created before any activity objects. So store all your data in your application class, and then it won't matter what happens to your activities when the orientation changes.
I'm fairly new to Java & Android, so I have little idea what I am doing.
My test program successfully creates view objects, so now I am trying to organize my code.
I want to make a separate class to manage my GUI, but it always fails.
Basic info about my gui class:
package horc.gui;
...
public class GUI{
private Context context;
From my main activity, I constructed that gui class with the app context.
gui=new GUI(getApplicationContext()); // gui is a var of type GUI, & this sets the context of the class
The problem is when I make/modify a view object that is in the class from outside, it throws an exception.
My main activity...
package horc.test;
...
GUI gui;
LinearLayout test=gui.newLinear("ff", "v"); // <-- this sets fill parent for width/height
// & vertical orientation of the vertical layout.
// Doesnt work for the reason stated above.
// I cannot manage any view objects from a separate class.
gui.lastText.setText("##########"); // <-- a variable in the class to hold the view object I am manipulating
setContentView(t);
...calls this class function:
public TextView newText(String text){
TextView test=new TextView(context);
lastLinear.addView(test);
return test;
}
I tested this similar body within the main activity & it worked fine. It only fails when I do anything from outside that gui class.
Is there a common issue that people run into when managing view objects in separate classes? I have absolutely no idea what I am doing at this point. Coming from C++, java seems like a nutty language. I cannot plan things the way I would in C++.
instead of
gui=new GUI(getApplicationContext());
try
gui=new GUI(MyActivity.this);
Please put your activity name instead of MyActivity
Everything I've read about Intents talks about using them to push data, or to start one Activity from another Activity. I want to pull data from an Activity that's already running.
The Tab Layout tutorial at http://developer.android.com/resources/tutorials/views/hello-tabwidget.html illustrates what I want to do. (My app is doing some engineering calculations instead, but the tutorial code provides a good analogy to my app.) The tutorial creates an app with three tabs, and each tab hosts a separate activity.
To expand on the example in the tutorial, suppose I select an artist in the Artists tab/activity. I want to be able to select the Albums tab/activity and have it display all the albums featuring that artist.
It seems to me that I need to use an Intent to do this. All of the tutorials I've found assume that I would create a "See albums" Button in the Artists tab/activity, and that pressing the Button would execute an Intent that starts the Albums activity and passes artistName.
I DO NOT want to create that Button. Real estate on the Artists layout is precious, and I have a perfectly good Albums tab, AND the HelloTabWidget activity already contains an intent to create the Albums tab.
Besides, a user will want to skip back and forth between Album and Artist in order to change artist selections, and the tabs are a perfectly good way to do this. There's no need to complicate the UI with another button.
So how can I have the Albums activity PULL artistName from the Artists activity when the Albums tab is selected (or the Albums layout is displayed), rather than have the Artists activity START Albums and PUSH the artistName?
Equivalents I can think of from other programming worlds:
Global variables. Discouraged in Android devt, right? And if they do exist, what are they called?
A getter, like artistName = Artists.getArtistName(); . I get the feeling that it's not that easy.
Writing to, and reading from, a file - that is, mass storage or non-volatile memory. I don't need the artistName value to be permanent. It will be reset to null every time the user launches the application.
So how is it done in the Android world? Do I use an Intent - and if so, how?
Global variables were the right answer.
I thought Java discouraged their use, but a couple of links that appeared in the "Related" links on the right margin of this window mentioned them directly. One was "Android: How to declare global variables?" and the other was "how to pass value betweeen two tab in android". Both pointed to the Application Class as the place to define global variables and methods. Armed with this new knowledge, I found an article called "Android Application Class" on the Xoriant blog that expanded on the StackOverflow answers.
It's best to review those three links first. I need to add some tips to what those authors have said.
Your Application class has to be in its own separate file. (That might be a "duh" to some people, but not to everybody.) Here's a good framework for an example called Something.java:
public class Something extends Application {
// Put application wide (global) variables here
// Constants are final, so they don't have to be private
// But other variables should be declared private;
// use getters/setters to access them
public final boolean FEET = false;
public final boolean METERS = true;
private boolean units = FEET;
#Override
public void onCreate() {
super.onCreate();
// Put any application wide (global) initialization here
}
// Put application wide (global) methods here
public boolean getUnits() {
return units;
}
public void setUnits(boolean whichOne) {
units = whichOne;
}
}
I'm using Eclipse with the ADT plug-in, in Windows XP. Eclipse doesn't always behave properly if you edit XML code directly, so it's best to open AndroidManifest.xml, then select the Application tab and enter your application name in the Name field. You don't need to put a dot or period in front of the name. Just type in the name of your class, like "Globals" or "MyApplication" or whatever. (Note that this is the default application in your Manifest. You don't have to create a separate <application></application> tag.
This step may not be necessary on an actual Android device, but it was necessary for the emulator: you need to use the getApplicationContext() command in every onCreate() and every method that will be accessing the global variables and methods. I tried to put it outside of onCreate() with the rest of my activity wide variables, and it didn't work. Putting it inside every method seems wasteful, but both the emulator and the Android device work fine with it that way. Here's a sample showing how I used it:
public void fooBar() {
// Access to global variables and methods
final Something s = (Something)getApplicationContext();
// ...
// This next line demonstrates both a global method and a global variable
if (s.getUnits() == s.FEET) {
// do something with feet
} else {
// do something with meters instead
}
// ...
}
Those were the only hiccups I encountered. The three references that I have listed, taken together, are otherwise pretty complete.