Crash when setting parameter to layout in Android - java

Expected Result
Clicking on the toggle button will show up the menu and slide out the content view rightward. After animation is finished, the layout parameters of the content view gets updated to the final position.
Problem
When updating the final position of the content view, the statement mViewContent.setLayoutParams(params); causes the crash. The error message is java.lang.ClassCastException: android.widget.LinearLayout$LayoutParams
Source Code
Main.java > public class MainActivity extends Activity {}
public void onToggleButtonMenuClicked(View view) {
// Is the toggle on?
boolean toggleTurnedOn = ((ToggleButton) view).isChecked();
if (toggleTurnedOn) { // If the toggle is turned on
// Show menu
LinearLayout mViewMenu = (LinearLayout) findViewById(R.id.linear_layout_menu);
Animation animMenuOn = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim_menu_on);
mViewMenu.startAnimation(animMenuOn);
LinearLayout mViewContent = (LinearLayout) findViewById(R.id.linear_layout_content);
Animation animContentOff = AnimationUtils.loadAnimation(MainActivity.this, R.anim.anim_content_off);
mViewContent.startAnimation(animContentOff);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(480, 800);
params.leftMargin = 384; // Shift 384 pixels from left screen border
params.rightMargin = -96; // Exceed 96 pixels from right screen border
mViewContent.setLayoutParams(params); // This statement causes crash!
} else {
// Hide menu...
} // End of toggle events handling
} // End of onToggleButtonMenuClicked()
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="329dp"
android:layout_height="wrap_content" >
<!-- The Menu View -->
<LinearLayout
android:id="#+id/linear_layout_menu"
android:layout_width="263dp"
android:layout_height="wrap_content"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/table_row_1_search_bar"
android:layout_width="match_parent"
android:layout_height="40dp"
android:weightSum="10"
android:orientation="horizontal" >
<EditText
android:id="#+id/edit_text_search_id"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="7"
android:hint="#string/edit_text_search_id"
android:textSize="14sp" />
<Button
android:id="#+id/button_search_id"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_weight="3"
android:text="#string/button_search_id" />
</LinearLayout>
<!-- Other rows in the menu are omitted -->
</LinearLayout> <!-- End of Menu -->
<!-- The Content View -->
<LinearLayout
android:id="#+id/linear_layout_content"
android:layout_width="329dp"
android:layout_height="match_parent"
android:orientation="vertical" >
<ToggleButton
android:id="#+id/toggle_button_menu"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onToggleButtonMenuClicked" />
<TextView
android:id="#+id/text_content"
android:layout_width="480dp"
android:layout_height="wrap_content"
android:text="#string/text_content" />
</LinearLayout> <!-- End of Content -->
</FrameLayout> <!-- End of the root linear layout -->

mViewContent params should be added with resp to your Parent view, suppose you have parent view as LinearLayout then, LinearLayout.LayoutParams must be used.
Explanation (Adapted from here):-
Take for example, LinearLayout.LayoutParams and RelativeLayout.LayoutParams, they are different independent classes. They store different additional information about child views... say..
LinearLayout.LayoutParams can associate weight value with each
view, while RelativeLayout.LayoutParams can't.
RelativeLayout.LayoutParams can have values like alightWithParent,above, below
with each view while LinearLayout.LayoutParams can't.
Although the code will not give compile time error because all LayoutParams have same parent class i.e. ViewGroup.LayoutParams. So, it is always essential, to assign Layout params with respect to parent layout..

make sure you imported the correct layout params
import android.widget.LinearLayout.LayoutParams;

Related

Children from custom LinearLayout not displaying ripple effect

I'm using a custom LinearLayout (extends LinearLayout), so that and Adapter (BaseAdapter) can be attached to it.
(Thanks to the original author ofc.)
* #author Vincent Mimoun-Prat # MarvinLabs
*/
public class MyLinearLayout extends LinearLayout {
private static final String TAG = "MyLinearLayout";
private Adapter adapter;
....
private void reloadChildViews() {
removeAllViews();
if (adapter == null) return;
int count = adapter.getCount();
for (int position = 0; position < count; ++position) {
View v = adapter.getView(position, null, this);
if (v != null) {
int[] attrs =new int[]{R.attr.selectableItemBackground};
TypedArray typedArray =getContext().obtainStyledAttributes(attrs);
int backgroundResource =typedArray.getResourceId(0, 0);
v.setBackgroundResource(backgroundResource);
typedArray.recycle();
addView(v);
}
}
requestLayout();
}
}
So I'm basically adding each child dynamically via addView() method.
As you can see I've already attempted to place a ripple effect on each child view added programatically.
The child view that is added to this ViewGroup has the according attrs:
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<data>
<variable
name="contentProduct"
type="com.example.flyhigh.data.pojos_entities.content.variations.ContentProduct" />
<variable
name="touchListener"
type="android.view.View.OnTouchListener"/>
</data>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context="com.example.flyhigh.ui.DailyJournalFragment"
android:clickable="true"
android:focusable="true"
setOnTouchListener="#{touchListener}"
android:background="?android:attr/selectableItemBackground"
>
<TextView
.....
/>
</LinearLayout>
</layout>
The touchListener binded is working accordingly.
The LinearLayout:
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="com.example.flyhigh.adapters.PhaseTypePagerAdapter2"
>
<com.example.flyhigh.custom_artifacts.MyLinearLayout
android:id="#+id/myLinearLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
android:orientation="vertical"
android:background="?selectableItemBackground" //I've tried with this on and off
android:clickable="true" //I've tried with this on and off
android:focusable="true" //I've tried with this on and off
android:divider="#drawable/divider"
android:dividerPadding="10dp"
android:showDividers="middle"
/>
</layout>
This LinearLayout is inside a ViewPager.
This ViewPager is working without FragmentManager (its Adapter is a PagerAdapter).
How do I know its the LinearLayout obfuscating the children and not the ViewPager obfuscating everything? because when playing around the LinearLayout attributes, the changes become visible, this includes the actual ripple effect of the LinearLayout itself (not its children) when touched outside a children (I've given some room for this to be possible).
In case you want to see the ViewPager:
<com.example.flyhigh.custom_artifacts.MyViewPager
android:id="#+id/main_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
>
I'm planning to add a Drag and Drop functionality to each children but I'm afraid that this symptom will reach that functionality also.
I'm 100% sure the solution relies on overriding a Method in the MyLayout class but which one?
I appear to have had some concepts wrong about the order of how views overlap.
It seems that the order will always be that the last nested object is the one on top of everything else (how could I've thought it otherwise...), maybe I was confused by the way the code is written (in a nested way)...
Anyways...
The problem wasn't that the parent ViewGroup was obfuscating its more inner children, BUT the other way around:
It's always the children that obfuscate its parent.
To reprise my initial question, I thought my outer layout (a custom LinearLayout) was preventing the ripple effect from its list items (a ViewGroup also).
So, the solution, without using the android:foreground= that seems to be a popular one, is the next one:
This inside the ViewGroup you want highlighted with ripple effect (my list item):
<LinearLayout
...
android:clickable="true"
android:focusable="true"
android:background="?android:attr/selectableItemBackground"
android:addStatesFromChildren="true" // this is the important one, but without the other attrs is useless
>
//obfuscating children should have the according:
<TextView
...
android:clickable="true"
android:focusable="true"
...
/>
<TextView
...
android:clickable="true"
android:focusable="true"
...
/>
<TextView
...
android:clickable="true"
android:focusable="true"
...
/>
Etc...etc...etc...
</LinearLayout>

Android: Box that fills entire screen with content below

I've been trying to figure out how to place a container that fills the entire screen vertically inside a ScrollView that has content below.
Something like this:
[ BOX THAT FILLS ENTIRE SCREEN ]
[ Some other stuff / requires scroll to view ]
This is the content I would like below the box:
http://myupload.dk/handleupload/ce34836ostt
I've tried everything - it seems like i can't position stuff absolute to the container that fills the entire screen. I can make a box that fills the entire screen, but everything i adds below it squeezes the box together.
It works when I add a size to the box, like this:
<com.github.ksoichiro.android.observablescrollview.ObservableScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|top"
android:minWidth="25px"
android:minHeight="0px"
android:paddingTop="0dp"
android:fillViewport="true"
android:id="#+id/scroll">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- THIS BOX I WOULD LIKE TO FIT THE ENTIRE SCREEN -->
<android.support.v4.view.ViewPager
android:layout_width="match_parent"
android:layout_height="100dp"
android:id="#+id/cardsPager" />
<!-- THIS CONTENT SHOULD BE BELOW THE BOX ABOVE -->
<fragment android:name="bonnier.hvadsynes.fragments.DetailsFragment"
android:id="#+id/details_fragment"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</com.github.ksoichiro.android.observablescrollview.ObservableScrollView>
Really appreciate your help!
Simon
My idea is to set the height of the screen-filling-box programmatically.
When and Where: depends on what you use e.g.in onResume there surely are better places but for now it works.
How: add an OnGlobalLayoutListener to your viewtreeobserver and then copy your scrollviews height to 'the box'.
<!--Whatever the whole things height should be, e.g. match_parent or 100dp or or or-->
<ScrollView
android:background="#0f0"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scrollview">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- THIS BOX I WOULD LIKE TO FIT THE ENTIRE SCREEN -->
<!-- Any height works as we overwrite it anyway-->
<View
android:id="#+id/makebig"
android:background="#f00"
android:layout_width="match_parent"
android:layout_height="300dp" />
<!-- THIS CONTENT SHOULD BE BELOW THE BOX ABOVE -->
<View
android:background="#0ff"
android:id="#+id/details_fragment"
android:layout_width="match_parent"
android:layout_height="100dp"/>
</LinearLayout>
</ScrollView>
In your Activity or wherever: e.g. in onResume()
final View makebig = findViewById(R.id.makebig);
final View scrollview = findViewById(R.id.scrollview);
final ViewTreeObserver viewTreeObserver = scrollview.getViewTreeObserver();
viewTreeObserver.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
//only do this all once
scrollview.getViewTreeObserver().removeOnGlobalLayoutListener(this);
makebig.getLayoutParams().height = scrollview.getHeight();
}
});
You can do it this way:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/layout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#A5CB3A"
android:orientation="vertical" >
</LinearLayout>
<LinearLayout
android:id="#+id/layout2"
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="vertical" >
<!-- here goes your textview -->
</LinearLayout>
</LinearLayout>
And in the onCreate method of the Activity
Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size);
int screenHeight = size.y;
LinearLayout layout = (LinearLayout)findViewById(R.id.layout1);
ViewGroup.LayoutParams params = layout.getLayoutParams();
params.height = screenHeight; //here you can substract the height of the top bar, if your app has one
layout.setLayoutParams(params);
You are just setting the height of the screen to the inner layout.

Cannot center view in RelativeLayout

I am trying to center a view within a parent RelativeLayout:
private void addLoadingAnimation() {
RelativeLayout mainView = (RelativeLayout) findViewById(R.id.medical_supply_tile_activity);
mainView.removeAllViews();
GifView gifView = new GifView(this);
RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
layoutParams.addRule(RelativeLayout.CENTER_IN_PARENT);
gifView.setId(R.id.loading_gif);
mainView.setBackgroundColor(getResources().getColor(android.R.color.background_light));
mainView.addView(gifView,layoutParams);
}
I have also tried adding layoutParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0); before the CENTER_IN_PARENT line. Nothing seems to be working. It is always aligned to the bottom right.
Here is my parent view:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/background_light"
tools:context=".MedecineTileActivity"
android:id="#id/medical_supply_tile_activity"
android:layout_gravity="left">
</RelativeLayout>
The gravity=left is because when the loading animation is removed, I insert fragments and want them to go to the left.
I have achieved this with TextViews and ImageViews by aligning the child View's right to the parent's right and also aligning the child View's left to the parent's left.
<TextView
style="#style/NewTextM2"
android:id="#+id/soft_dialog_info_item_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignBottom="#+id/<Parent>"
android:layout_alignLeft="#+id/<Parent>"
android:layout_alignRight="#+id/<Parent>"
android:gravity="center"
android:tag="item_text"
android:text="#string/text"
android:maxLines="1" />
This will align the view to the bottom center of the parent.

Android - How to set the right on click event to a button that was made programmatically

I've made the next layout xml code which I call it layout_one -
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:id="#+id/layout_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="50dp"
android:layout_marginTop="50dp"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="100dp"
android:text="Testing 1"
android:textSize="20sp" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/info_layout"
android:visibility="gone">
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
As you can see there are two layout in it - one with textview and button and one with textview.
The layout with the only textview - is gone, And i want by a click on the button, from the visible layout, it will be shown.
Now at the activity i wrote the next code -
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test_layout);
ScrollView sv = new ScrollView(this);
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
sv.addView(ll);
LinearLayout newView0 = (LinearLayout)View.inflate(this, R.layout.layout_one, null);
LinearLayout newView1 = (LinearLayout)View.inflate(this, R.layout.layout_one, null);
LinearLayout newView2 = (LinearLayout)View.inflate(this, R.layout.layout_one, null);
LinearLayout newView3 = (LinearLayout)View.inflate(this, R.layout.layout_one, null);
ll.addView(newView0);
ll.addView(newView1);
ll.addView(newView2);
ll.addView(newView3);
setContentView(sv);
newView1.findViewById(R.id.layout_one).setBackgroundColor(0xff0000ff);
TextView tv3 = (TextView) newView3.findViewById(R.id.textView1);
tv3.setText("Suprise Suprise");
infoLay = (View) findViewById(R.id.info_layout);
ll.addView(newView3);
}
Now there is something I don't understad - How could I set an on click listener to the button that will know which layout to show?
Tanks for any kind of help
Actually, there are three linear layouts, which seems redundant.
You can set the onclick listener by checking if textview of each layout is empty or not.
First off, have a look at this http://developer.android.com/training/improving-layouts/reusing-layouts.html. Use that <include> tag instead of dynamically creating all these layouts in your Activity class. Define your ScrollView, and all your LinearLayouts in your test_layout.xml file.
Then, in your Activity, all you have to do is get a reference to those views that's you've defined using findViewById.
Once you've got that working we can address your question as follows:
setContentView(R.layout.test_layout);
LinearLayout layout1 = (LinearLayout)findViewById(R.id.layout1);
LinearLayout layout2 = (LinearLayout)findViewById(R.id.layout2);
LinearLayout layout3 = (LinearLayout)findViewById(R.id.layout3);
OnClickListener listener = new OnClickListener() {
#Override
public void onClick(View view) {
((View)view.getParent().getParent()).findViewById(R.id.textView2).setVisibility(View.VISIBLE);
}
});
layout1.findViewById(R.id.button1).setOnClickListener(listener);
layout2.findViewById(R.id.button1).setOnClickListener(listener);
layout3.findViewById(R.id.button1).setOnClickListener(listener);
I'd also add that it seems like you're trying to create a list here, in which case you should use a ListView instead of many LinearLayouts.

Android. Add controls programmatically

I have layout with controls:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/contact_phones_layout_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/contact_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:inputType="phone" />
<Spinner
android:id="#+id/contact_phone_type"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
And I want to inflate it into another layout on the fragment. Quantity of these controls depends on values in a array. Array contains controls objects. So every time onCreateView rises I filled the layout from the array:
private void addLine(PhoneLine line) {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = line.getParent();
((ViewGroup) view.getParent()).removeView(view);
layout.addView(view, layout.getChildCount() - 1);
setButtonVisible(false);
}
if line is null controls are created this way:
private void addLine() {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = inflater.inflate(R.layout.contact_phone_line, layout, false);
EditText phoneEditText = (EditText) view.findViewById(R.id.contact_phone);
Spinner phoneType = (Spinner) view.findViewById(R.id.contact_phone_type);
phoneLines.add(new PhoneLine(phoneEditText, phoneType, view));
layout.addView(view, layout.getChildCount() - 1);
}
But after that I get same values in all EditText/Spinner controls, values equal to last element in the array. What can be wrong? May be there is more pure way to add controls dynamically?
I fixed it by adding controls when onResume rises, not onCreateView. But I still don't understand why onCreateView changes all values.

Categories

Resources