Unable to use variable defined inside if statement - java

I'm trying to do something like this in android:
// If the parent is a viewpager
if (parentIsViewPager)
{
// Retrieve the view's layout informations specific to viewpagers
ViewPager.MarginLayoutParams marginLayoutParams = (ViewPager.MarginLayoutParams) getLayoutParams();
}
// If the parent is not a viewpager (mainly a viewgroup)
else
{
// Retrieve the view's layout informations for a viewgroup
ViewGroup.MarginLayoutParams marginLayoutParams = getLayoutParams();
}
Log.i("Test","marginLayoutParams.leftMargin = " + marginLayoutParams.leftMargin);
Unfortunately, the IDE (Android Studio 1.5.1) tells me it cannot find declaration for layoutParams in the last line above... But I declared it in the if statements!
I guess there is something about scope in here but as the following code in my project is really big, I cannot duplicate it in each if statement.
So how can I achieve this or something similar?
EDIT:
As I guessed, and this was confirmed in the comments, this is a matter of scope. Ok, got it.
Thanks for your help guys.

A variable declaration is limited to it's scope.
In this case the if-else statement.
You could do it like this:
// Declare in correct scope and define later...
ViewGroup.LayoutParams layoutParams;
// If the parent is a viewpager
if (parentIsViewPager)
{
// Retrieve the view's layout informations specific to viewpagers
layoutParams = (ViewGroup.LayoutParams) getLayoutParams();
}
// If the parent is not a viewpager (mainly a viewgroup)
else
{
// Retrieve the view's layout informations for a viewgroup
layoutParams = (ViewGroup.LayoutParams) getLayoutParams();
}
Log.i("Test","layoutParams.width = " + layoutParams.width);

This compilation error has nothing to do with android or the IDE, is just the following:
if (parentIsViewPager)
{
// Retrieve the view's layout informations specific to viewpagers
ViewPager.LayoutParams layoutParams = (ViewPager.LayoutParams) getLayoutParams();
}
in this condition, the "Variable" layoutParams has a Scope: only inside the if condition,
the second part:
else
{
// Retrieve the view's layout informations for a viewgroup
ViewGroup.LayoutParams layoutParams = getLayoutParams();
}
the same criteria applies..
the error is because you are trying to use an object out of its scope...
layoutParams is not avaliable anymore outside that if condition

Is simply duplicating the Log statement on both sides of the if-else not an option or is layoutParams referenced else where?
// If the parent is a viewpager
if (parentIsViewPager)
{
// Retrieve the view's layout informations specific to viewpagers
ViewPager.LayoutParams layoutParams = (ViewPager.LayoutParams) getLayoutParams();
Log.i("Test","layoutParams.width = " + layoutParams.width);
}
// If the parent is not a viewpager (mainly a viewgroup)
else
{
// Retrieve the view's layout informations for a viewgroup
ViewGroup.LayoutParams layoutParams = getLayoutParams();
Log.i("Test","layoutParams.width = " + layoutParams.width);
}

Related

How to take a list of identical view components

Is it possible to somehow take an array, for example, a TextView, which are in the current Activity, or is it necessary to take each view?
You can get all the views of your xml programmatically as below:
bind your ViewGroup / Parent layout
Then the child View are accessible in if condition
val container = findViewById(R.id.container) as ViewGroup
for (i in 0 until container.childCount) {
val v = container.getChildAt(i)
if (v is Button) {
// You will get Button here
}
else if(v is TextView){
// You will get textView here
}
}

How to set the align parent right attribute of TextViews in relative layout programmatically

I have a relative layout which I am creating programmatically:
RelativeLayout layout = new RelativeLayout( this );
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
Now I have two TextViews which I want to add in this relative layout. But the problem is both TextViews are being shown on the left of the RelativeLayout overlapping on each other.
textViewContainer.addView(textView1);
textViewContainer.addView(textView2);
Now I want to know how can I programmatically set the the
`android:layout_alignParentRight="true"`
or
`android:layout_toLeftOf="#id/textView"`
attribute of TextViews as we do in the xml?
You can access any LayoutParams from code using View.getLayoutParams. You just have to be very aware of what LayoutParams your accessing. This is normally achieved by checking the containing ViewGroup if it has a LayoutParams inner child then that's the one you should use. In your case it's RelativeLayout.LayoutParams. You'll be using
RelativeLayout.LayoutParams#addRule(int verb) and
RelativeLayout.LayoutParams#addRule(int verb, int anchor)
You can get to it via code:
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)textView.getLayoutParams();
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
params.addRule(RelativeLayout.LEFT_OF, R.id.id_to_be_left_of);
textView.setLayoutParams(params); //causes layout updat
Use the method addrule
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
You need to align LayoutParams to each view:
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
Then add appropriate rule to each view:
params.addRule(RelativeLayout.ALIGN_PARENT_LEFT, RelativeLayout.TRUE);
to the first one and
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
to the second one.
then apply it to the view:
view.setLayoutParam(params)

Android - How to get reference to view in a custom layout?

I have a ListView that gets its layout for each item from "rowlayout".
I want to do something to one of the buttons in the rowlayout in my main activity, how do i get a reference to it findviewbyid doesn't work.
I guess i'm essentially not understanding a general concept of how to get a reference to a view in a custom layout- unless getting a view from a ListView is different. Can anyone help? thanks.
ListView is a ViewGroup so you can just iterate over its children:
int n = getChildCount();
for (int i = 0; i < n; i++) {
doSomethingToView(getChildAt(i));
}
However, you must be careful. ListView is pretty special and only has a subset of children that you might expect. It only holds (roughly) references to children that are currently visible and reuses them as they disappear.
What you might consider is changing underlying adapter instead and than notifying the ListView that its adapter changed, so it needs to redraw children. Or alternatively, you can make the child of ListView directly listen for events that are supposed to change it and then adjust itself.
If you are inflating a custom layout in the getView method of your adapter, you can get a view like this
LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.your_custom_layout, parent, false);
TextView tv = (TextView) rowView.findViewById(R.id.your_tv_id);
You can use partial refresh principle if you implement ListView in the way of ViewHolder, just like this:
private void refreshPartially(int position){
int firstVisiblePosition = listview.getFirstVisiblePosition();
int lastVisiblePosition = listview.getLastVisiblePosition();
if(position>=firstVisiblePosition && position<=lastVisiblePosition){
View view = listview.getChildAt(position - firstVisiblePosition);
if(view.getTag() instanceof ViewHolder){
ViewHolder vh = (ViewHolder)view.getTag();
//holder.play.setBackgroundResource(resId);//Do something here.
...
}
}
}
Possibly you can use the answer from this android - listview get item view by position to get the child view at a position then call findViewById() on that child view.

The specified child already has a parent. You must call removeView() on the child's parent first (Android)

I have to switch between two layouts frequently. The error is happening in the layout posted below.
When my layout is called the first time, there doesn't occur any error and everything's fine. When I then call a different layout (a blank one) and afterwards call my layout a second time, it throws the following error:
> FATAL EXCEPTION: main
> java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
My layout-code looks like this:
tv = new TextView(getApplicationContext()); // are initialized somewhere else
et = new EditText(getApplicationContext()); // in the code
private void ConsoleWindow(){
runOnUiThread(new Runnable(){
#Override
public void run(){
// MY LAYOUT:
setContentView(R.layout.activity_console);
// LINEAR LAYOUT
LinearLayout layout=new LinearLayout(getApplicationContext());
layout.setOrientation(LinearLayout.VERTICAL);
setContentView(layout);
// TEXTVIEW
layout.addView(tv); // <========== ERROR IN THIS LINE DURING 2ND RUN
// EDITTEXT
et.setHint("Enter Command");
layout.addView(et);
}
}
}
I know this question has been asked before, but it didn't help in my case.
The error message says what You should do.
// TEXTVIEW
if(tv.getParent() != null) {
((ViewGroup)tv.getParent()).removeView(tv); // <- fix
}
layout.addView(tv); // <========== ERROR IN THIS LINE DURING 2ND RUN
// EDITTEXT
simply pass the argument
attachtoroot = false
View view = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, false);
I came here on searching the error with my recyclerview but the solution didn't work (obviously). I have written the cause and the solution for it in case of recyclerview. Hope it helps someone.
The error is caused if in the onCreateViewHolder() the following method is followed:
layoutInflater = LayoutInflater.from(context);
return new VH(layoutInflater.inflate(R.layout.single_row, parent));
Instead it should be
return new VH(layoutInflater.inflate(R.layout.single_row, null));
I got this message while trying to commit a fragment using attach to root to true instead of false, like so:
return inflater.inflate(R.layout.fragment_profile, container, true)
After doing:
return inflater.inflate(R.layout.fragment_profile, container, false)
It worked.
You must first remove the child view from its parent.
If your project is in Kotlin, your solution will look slightly different than Java. Kotlin simplifies casting with as?, returning null if left side is null or cast fails.
(childView.parent as? ViewGroup)?.removeView(childView)
newParent.addView(childView)
Kotlin Extension Solution
If you need to do this more than once, add this extension to make your code more readable.
childView.removeSelf()
fun View?.removeSelf() {
this ?: return
val parentView = parent as? ViewGroup ?: return
parentView.removeView(this)
}
It will safely do nothing if this View is null, parent view is null, or parent view is not a ViewGroup
frameLayout.addView(bannerAdView); <----- if you get error on this line the do like below..
if (bannerAdView.getParent() != null)
((ViewGroup) bannerAdView.getParent()).removeView(bannerAdView);
frameLayout.addView(bannerAdView); <------ now added view
If other solution is not working like:
View view = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, false);
check for what are you returning from onCreateView of fragment is it single view or viewgroup? in my case I had viewpager on root of xml of fragment and I was returning viewpager, when i added viewgroup in layout i didnt updated that i have to return viewgroup now, not viewpager(view).
My error was define the view like this:
view = inflater.inflate(R.layout.qr_fragment, container);
It was missing:
view = inflater.inflate(R.layout.qr_fragment, container, false);
In my case it happens when i want add view by parent to other view
View root = inflater.inflate(R.layout.single, null);
LinearLayout lyt = root.findViewById(R.id.lytRoot);
lytAll.addView(lyt); // -> crash
you must add parent view like this
View root = inflater.inflate(R.layout.single, null);
LinearLayout lyt = root.findViewById(R.id.lytRoot);
lytAll.addView(root);
Simplified in KOTLIN
viewToRemove?.apply {
if (parent != null) {
(parent as ViewGroup).removeView(this)
}
}
In my case, I had id named as "root" for constraint layout, which was conflicting the existing parent root id.
Try to change the id.
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/root" //<--It should not named as root.
android:layout_width="match_parent"
android:layout_height="match_parent"
</androidx.constraintlayout.widget.ConstraintLayout>
In my case the problem was caused by the fact that I was inflating parent View with <merge> layout. In this case, addView() caused the crash.
View to_add = inflater.inflate(R.layout.child_layout_to_merge, parent_layout, true);
// parent_layout.addView(to_add); // THIS CAUSED THE CRASH
Removing addView() helped to solve the problem.
The code below solved it for me:
#Override
public void onDestroyView() {
if (getView() != null) {
ViewGroup parent = (ViewGroup) getView().getParent();
parent.removeAllViews();
}
super.onDestroyView();
}
Note: The error was from my fragment class and by overriding the onDestroy method like this, I could solve it.
My problem is related to many of the other answers, but a little bit different reason for needing to make the change... I was trying to convert an Activity to a Fragment. So I moved the inflate code from onCreate to onCreateView, but I forgot to convert from setContentView to the inflate method, and the same IllegalStateException brought me to this page.
I changed this:
binding = DataBindingUtil.setContentView(requireActivity(), R.layout.my_fragment)
to this:
binding = DataBindingUtil.inflate(inflater, R.layout.my_fragment, container, false)
That solved the problem.
You just need to pass attachToRoot parameter false.
mBinding = FragmentCategoryBinding.inflate(inflater, container, false)
If you're using ViewBinding, make sure you're referring to the right binding!
I had this issue when I was trying to inflate a custom dialog from within an activity:
AlertDialog.Builder builder = new AlertDialog.Builder(this);
final AlertBinding alertBinding = AlertBinding.inflate(LayoutInflater.from(this), null, false);
builder.setView(binding.getRoot()); // <--- I was using binding (which is my Activity's binding), instead of alertBinding.
This is how I do my custom dialog
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
android.view.View views = getLayoutInflater().inflate(layout_file, null, false);
builder.setView(views);
dialog = builder.create();
dialog.show();
I changed it into this and its works for me, I hope this helps
Dialog dialog = new Dialog(MainActivity.this);
dialog.setContentView(layout_file);
dialog.show();
check if you already added the view
if (textView.getParent() == null)
layout.addView(textView);
if(tv!= null){
((ViewGroup)tv.getParent()).removeView(tv); // <- fix
}
I was facing the same error, and look what I was doing. My bad, I was trying to add the same view NativeAdView to the multiple FrameLayouts, resolved by creating a separate view NativeAdView for each FrameLayout, Thanks
In my case I was accidentally returning a child view from within Layout.onCreateView() as shown below:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v= inflater.inflate(R.layout.activity_deliveries, container, false);
RecyclerView rv = (RecyclerView) v.findViewById(R.id.deliver_list);
return rv; // <- here's the issue
}
The solution was to return the parent view (v) instead of the child view (rv).
I found another fix:
if (mView.getParent() == null) {
myDialog = new Dialog(MainActivity.this);
myDialog.setContentView(mView);
createAlgorithmDialog();
} else {
createAlgorithmDialog();
}
Here i just have an if statement check if the view had a parent and if it didn't Create the new dialog, set the contentView and show the dialog in my "createAlgorithmDialog()" method.
This also sets the positive and negative buttons (ok and cancel buttons) up with onClickListeners.
In my case, I had an adapter which worked with a recyclerView, the items that were being passed to the adapter were items with their own views.
What was required was just a LinearLayout to act as a container for every item passed, so what I was doing was to grab the item in the specified position inside onBindViewHolder then add it to the LinearLayout, which was then displayed.
Checking the basics in docs,
When an item scrolls off the screen, RecyclerView doesn't destroy its
view
Therefore, with my items, when I scroll towards a direction, then change towards the opposite direction - fast, the racently displayed items have not been destroyed, meaning, the items are still associated with the LinearLayout container, then on my end, I'm trying to attach to another container, which ends up with a child having a parent already.
My solution was to check if the specified item has a parent, if it has, I assign it to a variable, then call parentVar.removeView(item), then assign the new parent.
Here's the sample code(Problematic Adapter):
override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) {
holder.linearLayoutContainer.removeAllViewsInLayout()
val questionWidget: QuestionWidget =
dataSource[position]
questionWidget.setValueChangedListener(this)
holder.linearLayoutContainer.addView(questionWidget)/*addView throws error once in a while*/
}
inner class QuestionWidgetViewHolder(mView: View) :
RecyclerView.ViewHolder(mView) {
val linearLayoutContainer: LinearLayout =
mView.findViewById(R.id.custom_question_widget_container)
}
Content of the R.id.custom_question_widget_container:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/custom_question_widget_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:orientation="vertical"
android:padding="10dp" />
So, the questionWidget seems to have been retaining the parent for almost 4 steps outside visibility, and when I scroll to the opposite direction fast, I would encounter it still with its parent, then I'm attempting to add it to another container.
Here's the fix - option 1:
override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) {
holder.linearLayoutContainer.removeAllViewsInLayout()
val questionWidget: QuestionWidget =
dataSource[position]
questionWidget.setValueChangedListener(this)
val initialWidgetParent : ViewParent? = questionWidget.parent
//attempt to detach from previous parent if it actually has one
(initialWidgetParent as? ViewGroup)?.removeView(questionWidget)
holder.linearLayoutContainer.addView(questionWidget)
}
Another better solution - option 2:
override fun onBindViewHolder(holder: QuestionWidgetViewHolder, position: Int) {
holder.linearLayoutContainer.removeAllViewsInLayout()
val questionWidget: QuestionWidget =
dataSource[position]
questionWidget.setValueChangedListener(this)
val initialWidgetParent : ViewParent? = questionWidget.parent
//if it's in a parent container already, just ignore adding it to a view, it's already visible
if(initialWidgetParent == null) {
holder.linearLayoutContainer.addView(questionWidget)
}
}
Actually, it's much of asking the child if it has a parent before adding it to a parent.
I tried all the things that you guys suggested, with no luck.
But, I managed to fix it by moving all my binding initializations from onCreate to onCreateView.
onCreate(){
binding = ScreenTicketsBinding.inflate(layoutInflater)
}
MOVE TO
onCreateView(...){
binding = ScreenTicketsBinding.inflate(layoutInflater)
}
You can use this methode to check if a view has children or not .
public static boolean hasChildren(ViewGroup viewGroup) {
return viewGroup.getChildCount() > 0;
}
My case was different the child view already had a parent view i am adding the child view inside parent view to different parent. example code below
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="#dimen/lineGap"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="#color/black1"
android:layout_gravity="center"
android:gravity="center"
/>
</LinearLayout>
And i was inflating this view and adding to another LinearLayout, then i removed the LinaarLayout from the above layout and its started working
below code fixed the issue:
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#color/black1" />
It happened with me when I was using Databinding for Activity and Fragments.
For fragment - in onCreateView we can inflate the layout in traditional way using inflater.
and in onViewCreated method, binding object can be updated as
binding = DataBindingUtil.getBinding<FragmentReceiverBinding>(view) as FragmentReceiverBinding
It solved my issue
In my case, I was doing this (wrong):
...
TextView content = new TextView(context);
for (Quote quote : favQuotes) {
content.setText(quote.content);
...
instead of (good):
...
for (Quote quote : favQuotes) {
TextView content = new TextView(context);
content.setText(quote.content);
...
If you are working with MaterialAlertDialog, this worked for me:
(yourChildView.parent as? ViewGroup)?.removeView(yourChildView)
If in your XML you have layout with id "root" It`s problem, just change id name

view.getParent() does not return a LinearLayout

I'm trying to make my first Android application, and I want to drag and drop coins into boxes. The onDrag method I found was this:
#Override
public boolean onDrag(View v, DragEvent e) { // onDrag(waarnaartoe, event)
if (e.getAction()==DragEvent.ACTION_DROP) {
// user interface
View view = (View) e.getLocalState();
ViewGroup from = (ViewGroup) view.getParent();
from.removeView(view);
FrameLayout to = (FrameLayout) v;
to.addView(view);
view.setVisibility(View.VISIBLE);
}
return true;
}
The boxes are LinearLayouts, so I changed the original code i.e.
from
ViewGroup from = (ViewGroup) view.getParent(); (which works fine)
to
LinearLayout from = (LinearLayout) view.getParent();
However, the app crashes if I try to drop something onto a box. The reason I want to get the LinearLayout is that I made a Map which connects LinearLayouts to "boxes" (which are theoretical objects with certain properties).
Could anyone explain to me why I cannot replace ViewGroup by LinearLayout?
The function which I copied was not applicable to my program. The line
FrameLayout from = (FrameLayout) e.getLocalState();
works perfectly fine, so e.getLocalstate already gave the container I wanted. Therefore the parent obtained by view.getParent() was the GridLayout.

Categories