Dynamically added Image View disappears onPause() or when rotating device - android

I seem to be going bald trying to fix what probably is a very simple problem.
I have an activity in which users can click a button and an ImageView is dynamically added to the screen. This ImageView is also stored in an ArrayList. When the user pauses the activity or the device is rotated all the images added to the layout disappears. They are still stored in the ArrayList however. I attempted to loop through the ArrayList to add the images to the layout again, however then I get an error thrown - the specified child already has a parent.
Below is my code for adding images to the layout and the ArrayList
public void AddImage() {
RelativeLayout rl = (RelativeLayout) findViewById(R.id.top);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
iv = new ImageView(this);
id++;
iv.setId(id);
params.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
iv.setImageResource(R.drawable.foo);
iv.setLayoutParams(params);
rl.addView(iv);
arrayList.add(iv);
}

You need to create Map instead of List and then do something like :
map.put(imageViewId,R.drawable.imageViewResource);
Then after pause just do :
RelativeLayout rl = (RelativeLayout) findViewById(R.id.top);
ImageView imageView = (ImageView)rl.findViewById(imageViewId);
Integer imageViewResource = map.get(imageViewId);
imageView.setImageResource(imageViewResource);
If this will not update your image try to :
imageView.post(new Runnable() {
#Override
public void run()
{
imageView.setImageResource(imageViewResource);
}
});
If your map is empty after rotation and you haven't time to play with save/restore instance state, just make map static.
Best wishes.

Declare your imageview variable within the scope of the method:
ImageView iv = new ImageView(this);
You cannot reuse the variable once it is added.

Add configChanges for that activity in manifest
<activity name="Your ClassName with Package" android:configChanges="orientation"/>
and call your method AddImage() in OnCongigurationChanged as below:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
int ot = getResources().getConfiguration().orientation;
switch (ot) {
case Configuration.ORIENTATION_LANDSCAPE:
AddImage();
break;
case Configuration.ORIENTATION_PORTRAIT:
AddImage();
break;
}
}
#SuppressWarnings("deprecation")
#Override
public Object onRetainNonConfigurationInstance() {
return super.onRetainNonConfigurationInstance();
}

Add configChanges for that activity in manifest
<activity
....
android:configChanges="orientation"
.... >

Related

Best way of making a class for an object?

I'm trying to create a game. I need a kitten object with a image displayed.
I need to use this object within multiple activities so I decided it's probably better using an outside class.
I used something like this. But the function findViewById(int) is not aviable from outside an activity. How to do this?
public class Kitten {
Context parent;
View parentView;
ImageView imgBody, imgHead;
Kitten(Context context, View view) {
parent = context;
parentView = view;
//Create body & head
imgBody = new ImageView(parent);
imgBody.setImageResource(R.mipmap.img_kitty_body_white);
addImage(imgBody);
imgHead = new ImageView(parent);
imgHead.setImageResource(R.mipmap.img_kitty_head_white);
addImage(imgHead);
}
private void addImage(ImageView img) {
RelativeLayout rl = (RelativeLayout) findViewById(R.id.adoptView);
RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.BELOW, R.id.adoptView);
lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
rl.addView(img, lp);
}
}
The main problem I have, is within the addImage method...
Have you tried using the method from your specific view? Try parentView.findViewById()
You should store the image ID in the "Kitten" object and retrieve it where you need with a getter method, or you can pass the parent View if you want to use the findViewById from there.
But, you don't usually store the Views itself.

Android View width and height have not changed after rotation

I have an activity whose layout I need to change after a rotation and part of the layout is a graph that is drawn using the width and height of the view that it will be placed into. The first time my code runs, the graph is drawn correctly, however after the rotation the width and height of the container view are not correct, in fact they appear to be the view as if it was not rotated.
Here is what I have so far,
In my manifest for the activity I am working:
android:configChanges="keyboardHidden|orientation|screenSize"
In my activity I have these following methods:
onCreate
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle extras = getIntent().getExtras();
patient_id = extras.getInt("patient_id");
patient_name = extras.getString("patient_name");
historyDurationType = 12;
constructLayout();
}
constructLayout
public void constructLayout(){
if(landScape){
setContentView(R.layout.activity_bg_history_static_land);
//Set buttons
btnTwelve = (Button)findViewById(R.id.btnTwelveHoursLand);
btnTwentyFour = (Button)findViewById(R.id.btnTwentyFourHoursLand);
btnSeven= (Button)findViewById(R.id.btnSevenDaysLand);
btnTwelve.setOnClickListener(this);
btnTwentyFour.setOnClickListener(this);
btnSeven.setOnClickListener(this);
btnTwelve.setBackgroundColor(getResources().getColor(R.color.light_blue_regular));
btnTwentyFour.setBackgroundResource(android.R.drawable.btn_default);
btnSeven.setBackgroundResource(android.R.drawable.btn_default);
}else{
setContentView(R.layout.activity_bg_history_static);
//Set buttons
btnTwelve = (Button)findViewById(R.id.btnTwelveHours);
btnTwentyFour = (Button)findViewById(R.id.btnTwentyFourHours);
btnSeven= (Button)findViewById(R.id.btnSevenDays);
btnTwelve.setOnClickListener(this);
btnTwentyFour.setOnClickListener(this);
btnSeven.setOnClickListener(this);
btnTwelve.setBackgroundColor(getResources().getColor(R.color.light_blue_regular));
btnTwentyFour.setBackgroundResource(android.R.drawable.btn_default);
btnSeven.setBackgroundResource(android.R.drawable.btn_default);
btnComment = (Button)findViewById(R.id.btnCommentGraph);
btnComment.setOnClickListener(this);
populateOtherContent(officialReadings12);
TextView tvStats = (TextView)findViewById(R.id.txtStatistics);
Typeface chunkFiveFont = Typeface.createFromAsset(getAssets(), "fonts/chunkfivettfversion.ttf");
tvStats.setTypeface(chunkFiveFont);
TextView tvReading = (TextView)findViewById(R.id.txtReadingTitle);
tvReading.setTypeface(chunkFiveFont);
comment = null;
}
if(needData){
getLatestReadings();
}
populateGraph();
}
populateGraph
public void populateGraph(){
if(landScape){
graph_container = (LinearLayout)findViewById(R.id.graph_land_content_layout);
}else{
graph_container = (LinearLayout)findViewById(R.id.graph_content_layout);
}
//Create graphlayout
mainGraph_Layout = new RelativeLayout(this);
RelativeLayout.LayoutParams glParams = new RelativeLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
mainGraph_Layout.setId(909);
mainGraph_Layout.setLayoutParams(glParams);
graph_container.addView(mainGraph_Layout);
graph_container.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if(needsGraph){
layoutGraph();
needsGraph = false;
}
}
});
}
layoutGraph
public void layoutGraph(){
viewWidth = mainGraph_Layout.getWidth();
viewHeight = mainGraph_Layout.getHeight();
//MORE STUFF IS HERE BUT NOT IMPORTANT
}
onConfigurationChanged
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
ActionBar actionBar = getActionBar();
if(newConfig.orientation==Configuration.ORIENTATION_LANDSCAPE){
//Config is landscape here
actionBar.hide();
needData = false;
landScape = true;
needsGraph = true;
constructLayout();
}else{
//Config is portrait here
actionBar.show();
needData = false;
landScape = false;
needsGraph = true;
constructLayout();
}
}
After rotation, it is at the layoutGraph() viewWidth and viewHeight objects where I have the problem. I had assumed by that point (having used the global layout listener) that the values would be correct. My understanding was that the listener would only have been triggered once "graph_container" was completed (and landscape or portrait) and so when calling layoutGraph() the width and height of "mainGraph_layout" (a child a graph_container, widths and heights set to MATCH_PARENT) would be good to go. It appears that the width and height I am getting are as if the phone is still portrait, and worth noting it appears that the removal of the action bar has also been taken into account.
Sorry for the long question but I thought it best to show all the code. If anything else needs to be shown then please let me know.
Thanks in advance,
Josh
There is a much better way to do this.
Use resource folders
Put your default layout files in res/layout, and the ones for landscape in res/layout-land. In other words, move res/layout/activity_bg_history_static_land.xml to res/layout-land/activity_bg_history_static.xml.
In onCreate, call
setContentView(R.layout.activity_bg_history_static);
The system will pick the file from res/layout-land when you are in landscape orientation, res/layout otherwise.
If you have views that are only present in one layout but not the other e.g. the comment button, wrap the code inside a null check like this:
btnComment = (Button) findViewById(R.id.btnCommentGraph);
if (btnComment != null) {
btnComment.setOnClickListener(this);
}
For populateGraph(), make sure both res/layout/activity_bg_history_static.xml and res/layout-land/activity_bg_history_static.xml has android:id="#+id/R.id.graph_content. Then you can do findViewById(R.id.graph_content) and get the LinearLayout you need.
Save data across rotation
In your activity, override onSaveInstanceState(), and save the data from getLatestReadings() into the bundle.
Then, in onCreate:
if (savedInstanceState == null) {
getLatestReadings();
} else {
// Restore latest readings from savedInstanceState
}
With that, you can let the system handle the rotation i.e. remove this from your manifest:
android:configChanges="keyboardHidden|orientation|screenSize"
Since the system is handling the rotation, you don't need to have a view tree observer any more. And you don't have to override onConfigurationChanged.

replace layout on orientation change

My app has a webview and some buttons inside a LinerLayout.
the problem is, I want the buttons to be on bottom in portrait mode and on left in landscape mode while the webview maintains it's state.
Two different layout doesn't work as it force recreation of the activity that refresh the webview. for now I use android:configChanges="orientation" in activity tag so webview doesn't get refreshed on orientation change.
Is there anyway to replace the layout of buttons on the change of screen mode?
portrait mode
landscape mode
I tested fragments, but dealing with fragment makes things much more complex and the fragment itself needs saving and restoring which may not work in a webview which has javascript state, So I searched more and find a nice article somewhere and with some modification I came to a solution which I suggest:
First, add android:configChanges="orientation|screenSize|keyboard|keyboardHidden" so the app handles the config changes instead of android.
Make two different layout for landscape and portrait. In both layouts instead of webview place a FrameLayout which acts as a placeholder for the webview.
Define initUI method like this and put everything related to UI initialization in this method:
public void initui()
{
setContentView(R.layout.main);
if (wv == null) wv = new WebView(this);
((LinearLayout)findViewById(R.id.webviewPlace)).addView(wv);
findViewById(R.id.home).setOnClickListener(this);
}
If the webview doesn't exist yet it will be created and after setContentView(R.layout.main) it will be added to the layout. Any other UI customization you need came after this.
and in onConfigurationChanged:
#Override
public void onConfigurationChanged(Configuration newConfig)
{
((LinearLayout)findViewById(R.id.webviewPlace)).removeAllViews();
super.onConfigurationChanged(newConfig);
initUI();
}
In onConfigChange the webview is removed from old placeholder and initui will be called which will add it back to the new layout.
In oncreate() call initui() so the ui will be initialized for the first time.
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
initUI()
}
I wish it would be helpful for someone.
If you are using android:configChanges="orientation|screenSize"
In manifest,it ignores the XML in "layout-land". If you create a different XML for landscape don't use the android:configChanges="orientation|screenSize" tag for that activity in manifest.
put that at layout-land for the layouts you want as landscape.
The idea is, you shouldn't really use configChange="orientation" because it has its downsides. You can find a detailed post here . You should manually handle your state if you want to change your layout. Of course you can programmaiclly do this but if you want to use xml to do this. You can maintain you webView with fragments.
Try to use this code:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.activity_splash);
}
Make your view a relative layout. When the orientation changes just adjust the layout params for each view in code.
Have your buttons be contained in a linear layout
As in..
Portrait
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
buttonLinearLayout.setOrientation(LinearLayout.HORIZONTAL);
buttonLinearLayout.setLayoutParams(lp);
LayoutParams webParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.addRule(RelativeLayout.ABOVE, R.id.buttons);
webView.setLayoutParams(webParams);
Landscape
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
lp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
buttonLinearLayout.setOrientation(LinearLayout.VERTICAL);
buttonLinearLayout.setLayoutParams(lp);
LayoutParams webParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
lp.addRule(RelativeLayout.TO_RIGHT_OF, R.id.buttons);
webView.setLayoutParams(webParams);
Make sure the LayoutParams you are using are the RelativeLayout params (always use the Layoutparams of the view's parent)
Add in the manifest
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:label="#string/app_name">
and
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Resources.getSystem().getDisplayMetrics().widthPixels>
Resources.getSystem().getDisplayMetrics().heightPixels)
setContentView(R.layout.activity_layout1);
else setContentView(R.layout.activity_layout2);
...
}
and
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
setContentView(R.layout.activity_layout1);
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
setContentView(R.layout.activity_layout2);
}
}
Documents here:
https://developer.android.com/guide/topics/resources/runtime-changes.html
You can add values-(configuration that you require - eg. land,portrait) folder under resources and mention the layout you need for this configuration.. It's as simple as that.
Please checkout the below link for further information-
http://developer.android.com/training/multiscreen/screensizes.html#TaskUseAliasFilters

How to set correctly dynamically defined LinearLayout with landscape/portrait rotation?

I defined dynamically all GUI components.When the Cntrl+F11 is first pressed the layout updates correctly to from portrait to landscape. But when I press Cntrl+F11 again, layout remains in its "landscape" look. How can I update it for "portrait" look again? The source code:
//Main Layout ll
LinearLayout ll ;
ll.setOrientation (LinearLayout.VERTICAL);
ll.setLayoutParams(new ViewGroup.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT));
//Inner layout ll2 is inside of the main layout ll
LinearLayout ll2 = new LinearLayout (this);
ll2.setWeight(1.0f);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
lp.weight = 0.25f;
//Button b is inside of the inner layout ll2
Button b = new Button (this);
b.setLayoutParams(lp);
ll2.addView (b);
ll.addView(ll2);
I tried this solution (from this site):
I added function
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(ll);
}
and set I in AndroidManifest
android:configChanges="keyboardHidden|orientation"
but it stiil doesn't work.Thanks.
use this in xml file
android:orientatoin="portrait "
it is also possible to write in onCreate() method also
You Also specify in ANDROID_MANIFEST like this
<activity android:name=".activityName" android:screenOrientation="landscape"></activity>
To revert configuration back to Portrait use key-combinations ctrl+F9

Panning video in runtime

I want to make the view move around the screen. Is that possible?
in other words, I want panning to be possible and I think that has something to do with the view.
How do you do Panning a video preview?
If you want to move your view all over the screen, its possible. Presuming this is indeed your requirement, here's what you could do. Make the view a child of Relative Layout. Everytime you want to move the view, get the RelativeLayout.LayoutParams of the child view, change relevent margins and set this as the child view's LayoutParam.
If you are doing this to a SurfaceView (needed to play the video), you get surfaceChanged callback everytime you change the margin.
Here's a sample code of the tweak I did for API Demos' CameraPreview activity which does the same. The SurfaceView is moved from left to right. Hope this helps.
Regards,
Anirudh.
public class CameraPreview extends Activity {
protected static final String TAG = "CameraPreview";
private Preview mPreview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Hide the window title.
requestWindowFeature(Window.FEATURE_NO_TITLE);
// Create our Preview view and set it as the content of our activity.
mPreview = new Preview(this);
mPreview.setId(100);
RelativeLayout mainLayout = new RelativeLayout(this);
RelativeLayout.LayoutParams mainLp = new RelativeLayout.LayoutParams(640, 480);
mainLp.leftMargin = 20;
mainLayout.addView(mPreview, mainLp);
Button btn = new Button(this);
btn.setOnClickListener(new OnClickListener(){
public void onClick(View v) {
RelativeLayout.LayoutParams nLp = (LayoutParams) mPreview.getLayoutParams();
nLp.leftMargin += 10;
Log.v(TAG,"nLp.leftMargin: " + nLp.leftMargin);
mPreview.setLayoutParams(nLp);
}
});
btn.setText("Click me!");
RelativeLayout.LayoutParams btnLp = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
btnLp.addRule(RelativeLayout.BELOW, mPreview.getId());
mainLayout.addView(btn ,btnLp);
setContentView(mainLayout);
}
}

Categories

Resources