Specified child already has parent - java

I'm currently passing data between two dialogfragments. Whenever the dialog fragment 2 had been done. It will pass the data from to dialog fragment 1. Here the method for dialog fragment 1, it implements an interface that created in dialog fragment 2.
Dialog Fragment 1
#Override
public void updateContributor(ArrayList<View> imageView) {
System.out.println("Contributors received from contributer's fragment: " + imageView);
for ( View child : imageView)
{
if (child instanceof ImageView) {
ImageView childImageView = (ImageView) child;
ll_contributor_list.addView(childImageView);
}
}
}
Dialog Fragment 2
MusicRecorderFragment fragment = (MusicRecorderFragment) getFragmentManager().findFragmentByTag("record_fragment");
ArrayList<View> views = getAllChildren(ll);
/*setArguments(args);*/
fragment.updateContributor(views);
getDialog().dismiss();
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.

You are getting this exception because of your views are already added in some layout and you are trying to add it again to the contributor list which I guess it's a layout. (ll_contributor_list.addView(childImageView);)
Try to just collect the data and recreate your view on the other fragment instead of trying to add an already added view to another layout.

Related

How to create a dynamic UI with Kotlin in Android Studio

create widget at runtime as texts and buttons, with kotlin language
For example, when clicking a button, a new text is created
The answer for XML-based UI:
To add a view programmatically on Android, you can use the addView method of a ViewGroup. Here are the basic steps:
Get a reference to the parent ViewGroup where you want to add the new view. You can do this by calling findViewById on the parent Activity or Fragment.
Create a new instance of the View you want to add to the parent ViewGroup. You can do this by calling the constructor of the View class that matches the type of View you want to create.
Set any necessary properties on the new View, such as its layout parameters or content.
Add the new View to the parent ViewGroup by calling the addView method on the parent ViewGroup and passing in the new View.
Here's an example of how to add a TextView to a LinearLayout programmatically:
val linearLayout = findViewById(R.id.linear_layout) // get a reference to the parent LinearLayout
val textView = TextView(this) // create a new TextView in Activity
// or TextView(requireContext) in Fragment
textView.text = "Hello, world!" // set the text of the TextView
linearLayout.addView(textView) // add the TextView to the LinearLayout
lets assume you have a ViewGroup such as LinearLayout and lets name it group
and you have a button.
Based on this assumption. I would do the following
mButton.setOnClickListener{
group.addView(TextView(context)
.also{
it.text = "your text"
})
}
of course you will have to work on positioning it too

Change selected item's layout in ListView (Custom Adapter)

Is it possible to change layout of selected item in ListView?
I made simple app with ListView using Custom Adapter. It's working great, but I would like to change the layout of selected item to different layout.
In practice, the clicked item would expand it's height and some new buttons would appear. Once user deselects the items, it would go back to classic layout.
This is my Custom Adapter code
public class ListAdapter extends ArrayAdapter<Polozka> {
// context mem variable
private Context mContext;
// Konstruktory
public ListAdapter(Context context,int textViewResourceId){
super(context,textViewResourceId);
}
public ListAdapter(Context context, int resource, List<Polozka> items){
super(context,resource,items);
this.mContext=context;
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
View v = convertView;
if (v == null){
LayoutInflater vi;
vi = LayoutInflater.from(getContext());
v = vi.inflate(R.layout.polozkanakupniseznam,null);
}
final Polozka p = getItem(position);
if (p != null){
final TextView tt1 = (TextView) v.findViewById(R.id.seznam_nazevPolozky);
TextView tt2 = (TextView) v.findViewById(R.id.seznam_pocetPolozky);
CheckBox cb1 = (CheckBox) v.findViewById(R.id.checkBox_koupeno);
Button btnPridat = (Button) v.findViewById(R.id.buttonPridat);
Button btnOdebrat = (Button) v.findViewById(R.id.buttonOdebrat);
if (tt1 != null){
tt1.setText(p.getNazev());
}
if (tt2 != null){
tt2.setText(Integer.toString(p.getPocet()));
}
return v;
}}
This is how I set adapter to my listview
lv = (ListView) findViewById(R.id.lv);
adapter = new ListAdapter(this,R.layout.polozkaseznam,list);
lv.setAdapter(adapter);
I figured out how to change layout of every item by creating new custom adapter and asigning it to the ListView, but that's sadly not what I need.
Thank you very much!
The getView() method of the adapter is used to create the original view. In there you can use the view's setOnTouchListener() method to add a View.OnTouchListener implementation. The onTouch() handler of that class receives the selected view.
A useful member to add to the OnTouchListener implementation is a GestureDetector; this is created with an OnGestureListener implementation to which you can pass the MotionEvent received in the OnTouchListener's onTouch() callback. This listener is then used to distinguish various events (single tap, double tap, etc); GestureDetector.SimpleOnGestureListener is good implementation to use with a number of callbacks handling such events you can override.
At some point (e.g., in the SimpleGestureListener's onDown() callback, which fires when an item is first touched), you can modify the layout of the view handed to the OnTouchListener. To change it back, you could either use a handler for the "on up" phase of a gesture, or, if you wish the item to remain as is until something else is selected, hold a reference to the last selected view and change it when another one is selected.
In case this last point is not clear, the idea is that the SimpleGestureListener is a member object of the view's OnTouchListener instance, therefore it can access the view belonging to the enclosing OnTouchListener.

Fragment View's state is broken by FragmentManager

I wanted to retain views through fragment replace operation and noticed a strange bug:
After fragment's View is destroyed through replace operation, it still has a parent, however, the parent doesn't have the view among its children.
private View view;
#Override
public View onCreateView(...) {
if (view == null) {
view = // ... inflate view
} else {
// at this point, view has mParent, but can't be removed
// because the parent does not have it among its children
}
return view;
}
#Override
public void onDestroyView() {
// I could remove the view from its parent here manually,
// however, it will cause fragment's content to disappear before
// the replace animation ends
}
Notice how the view has a reference to its parent through mParent, but the parent has zero mChildrenCount.
This causes fragment transaction to crash with an exception when trying to reuse the view:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first. I find this behavior to be exceptionally wrong since it leaves the fragment's view in an unusable state.
If someone has faced this issue, please share your workarounds. I have come up with a temporary dirty reflective solution:
#Override
public View onCreateView(...) {
if (view == null) {
view = // ... inflate view
} else {
// clear parent reference (because SDK fails to do it)
Field f = View.class.getDeclaredField("mParent");
f.setAccessible(true);
f.set(view, null);
}
return view;
}

How to change dynamically inflated textview from after proceeding an action on other fragment?

So, what my problem is that in one fragment(w/i a viewpager, I'll call this Fragment A) I click on this dynamically created button that adds a new fragment(I'll call this Fragment B) in a framelayout which allows me to use PayPal service. On PayPal Activity result, Fragment B communicates with the main Activity via a communicator(an interface class) to call Fragment A to change that text. But I'm getting a null pointer exeception crash.
To be specific:
what I did was that I made a global TextView variable that is initialized on click. I did this b/c I have a list of other things that are dynamically inflated and to avoid the TextView from being initialized with wrong layout I initialized it on click.
bidChange.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
eventListChangeKey = keyVal;
eventListChangeIdx = eventListIdx;
eventBiddingChangeIdx = finalI;
priceToChage = (TextView) biddersLayout.findViewById(R.id.single_list_bidder_bid_price);
Bundle bundle = new Bundle();
bundle.putInt("auctionID", auctionId);
bundle.putInt("dateID", dateId);
bundle.putInt("FromWhere", 2);
Fragment fragment = new Fragment_Home_ItemInfo_Bid();
fragment.setArguments(bundle);
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.add(R.id.container_mainScreen, fragment, "itemInfo_bid")
.addToBackStack(null)
.setTransitionStyle(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
.commit();
}
});
In the main activity
public void changeBidderPrice(String s) {
Fragment fragment = viewPagerAdapter.getItem(1);
((Fragment_List) fragment).changePrice(s);
}
is what I do
back in Fragment A
public void changePrice(String val) {
priceToChage.setText(val);
dataMap.get(eventListChangeKey).get(eventListChangeIdx).getBidList().get(eventBiddingChangeIdx).setPrice(val);
}
I've thought this over an over but I just can't figure this out. I've searched for similar cases in StackOverflow but I wasn't able to get a help.
Would the problem be the way I initialize that TextView? or is it the way I'm calling Fragment A from the main activity?
for fragments onViewCreated() is called after onCreateView() and ensures that the fragment's root view is non-null. Any view setup should happen here. E.g., view lookups, attaching listeners.
source : codepath
for activities onCreate()

Adding methods after fragments

I have a fragment that is a "timer" that I can add anywhere. In the fragment I change a textView programatically, and it runs beautifully. My problem is when it comes to using a view from the layout inflated by the constructor(? Not sure if that's the right terminology) in another method below it.
public class Timer_fragment extends android.support.v4.app.Fragment {
int testpins;
String testedpin;
TextView text;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.timer_frag, container, false);
TextView text = (TextView) v.findViewById(R.id.pwd_status);
text.setText("Setting text in fragment not main");
/* set the TextView's text, click listeners, etc. */
updateStatus();
return v;
}
All of that code works with no errors but when I try to add this method:
private void updateStatus() {
TextView text = (TextView) findViewById(R.id.pwd_status);
testPin();
text.setText(testedpin);
}
I get a red line under findViewById saying The method findViewById(int) is undefined for the type Timer_fragment.
I thought about inflating the view in all of my methods and not returning them, but surely that would affect performance somehow right?
Just tried inflating the layout before using the view but I get an error on the word inflater and container saying that they can't be resolved.
Am I going about this correctly?
You already have a member variable in the scope of your Fragment called text. Don't re-declare it in your methods, just assign it.
text = (TextView) v.findViewById(R.id.pwd_status);
and
private void upateStatus() {
testPin();
text.setText(testedpin);
}
The method 'findViewById' is provided by the activity. While this class extends Fragment, you will not have access to activity related method calls unless you provide the activity to the fragment. Check out: http://developer.android.com/reference/android/app/Activity.html#findViewById(int)
Basically, either pass in the instance of the activity to the Timer_fragment:
private final Activity _activity;
Timer_fragment(Activity activity)
{
_activity = activity;
}
...
private void updateStatus()
{
TextView text = (TextView) _activity.findViewById(R.id.pwd_status);
testPin();
text.setText(testedpin);
}
Or set the text of the view from within whichever activity is being used, and not from within the timer class.
Just replace findViewById with getActivity().findViewById.
findViewById method is defined inside the Activity class. Fragments aren’t activites. But the fragment can get a reference to the Activity that added it to a screen using the method getActivity.

Categories