i am trying to hide/show Button in fragment from Activity but it give me following exception.
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Home Activity
public class HomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
CategoryFragment frag=(CategoryFragment) activity.getSupportFragmentManager()
.findFragmentByTag("cat_frag");
Button newDesigns= (Button) frag.getView().findViewById(R.id.new_designs);
newDesigns.setVisibility(View.VISIBLE);
}
}
Category Fragment
public class CategoryFragment extends Fragment{
Button newDesigns;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_category, null);
newDesigns= (Button) v.findViewById(R.id.new_designs);
}
}
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#CCCCCC">
<TextView
android:id="#+id/list_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="#drawable/shape_logo_bg"
android:gravity="center"
android:padding="5dp"
android:textColor="#android:color/white"
android:textSize="18sp" />
<Button
android:id="#+id/new_designs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#id/list_name"
android:background="#color/edit_button_color"
android:padding="10dp"
android:text="#string/new_designs"
android:textColor="#color/btn_text_color"
android:layout_centerHorizontal="true"
android:layout_marginTop="5dp"
android:visibility="gone"
/>
</RelativeLayout>
Code is too large to be posted here. Thats why i have only posted the code where i am finding an issue.
I am able to get newDesigns BUTTON instance.What is shocking for me is if try to play with button instance (VISIBLE/GONE) it gives me above mentioned exception.
Help is appreciated.
You shouldn't play with a view of the Fragment while you are in a Activity directly. You will not know what the view's state will be and this can potentially lead to issues you can't even think of(beleive me i faced many). To interact with the activity's views, make an interface:
public interface AccessFragmentViews{
public void setVisibilityForButton(boolean bool);
//any other methods that you need
}
Now implement this inside the fragment class and override the method.
class YourFragment implements AccessFragmentViews{
.
.
public void serVisibilityForButton(boolean shouldHide){
if(shouldHide){
yourButton.setVisibility(View.GONE);
}else{
yourButton.setVisibility(View.VISIBLE);
}
}
}
Now you can interact safely with the views of the fragment within a activity using this interface. Make sure that the fragment is alive before doing so though ;) accessing child's view are prone to WindowLeakedExceptions and illegalstateexceptions
In the activity use it as follows:
you can get the fragment reference by either finding it by its tag or by using the reference you used to create the fragment
//NOTE: it is very very dangerous to do the accessing on a fragment views from an activity
//first the alive check then the logic
if(yourFragmentReference!=null){
((AccessFragmentViews)yourFragmentReference).setVisibilityForButton(true);// or false if you want to make it visible
}
Add a method in your fragment class
public void changeButtonVisibility(boolean visibility){
if(visibility){
newDesigns.setVisibility(View.VISIBLE);
}else{
newDesigns.setVisibilty(View.GONE);
}
}
and in your activity class
add this line
public class HomeActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
CategoryFragment frag=(CategoryFragment) activity.getSupportFragmentManager()
.findFragmentByTag("cat_frag");
frag.changeButtonVisibility(true);
}
}
Related
So I am still fairly new to working with Android Studio and everything in it. I have been stuck on trying to get fragments to communicate directly with each other. Here I'm simply just trying to set the TextView text element within one of my fragments. I have looked for hours and tried a lot, but I'm not sure what to do. Also, I am implementing my fragments through code in a FrameLayout.
Here is my fragment whose text value I'm trying to edit:
public class ReceivingFrag extends Fragment {
TextView sender;
public void updateText(String text) {
sender.setText(text);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frag_sender, container, false);
sender = (TextView) v.findViewById(R.id.sender);
return v;
}
}
I believe my root problem is that getView() and sender both return Null. I also understand that fragments are not technically views, but rather aid in the layout of views and ViewGroups. Any help is appreciated.
Not sure if it helps, but this is the method that calls the updateText() method within the ReceivingFrag class.
public void sendText(String text){
ReceivingFrag frag = new ReceivingFrag();
getSupportFragmentManager().beginTransaction().add(R.id.receiving_container, frag).commit();
getSupportFragmentManager().executePendingTransactions()
frag.updateText(text);
}
**Edit:
This is my MainActivity class that is calling and creating the Fragment:
public class MainActivity extends AppCompatActivity implements SendingFragment.TextClicked {
private static final String TAG = MainActivity.class.getSimpleName();
public final static String EXTRA_MESSAGE = "com.example.myfirstapp.MESSAGE";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] myStringArray = {"Hello", "Nice To See You", "Bye"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, myStringArray);
ListView listView = (ListView) findViewById(R.id.mobile_list);
listView.setAdapter(adapter);
sendText("Hello");
}
#Override
public void sendText(String text){
ReceivingFrag frag = new ReceivingFrag();
getSupportFragmentManager().beginTransaction().add(R.id.receiving_container, frag).commit();
getSupportFragmentManager().executePendingTransactions();
frag.updateText(text);
}}
**Edit 2:
This is the MainActivity layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/apk/tools"
xmlns:tools2="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<EditText android:id="#+id/edit_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="#string/edit_message"
android:layout_weight="1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="sendMessage"
android:text="#string/button_send"/>
</LinearLayout>
<ListView android:id="#+id/mobile_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="#+id/receiving_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></FrameLayout></LinearLayout>
And this is the layout for the Fragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/sender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/frag_sender"
android:background="#color/gray"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"/></LinearLayout>
Solution:
So as mentioned below, the runtime error was fixed by adding
#Override
protected void onResume() {
super.onResume();
sendText("hello");
}
to the MainActivity class. After reading from https://developer.android.com/guide/components/fragments.html#Lifecycle
I think the statement
"Once the activity reaches the resumed state, you can freely add and remove fragments to the activity. Thus, only while the activity is in the resumed state can the lifecycle of a fragment change independently."
best explains the situation and why the error initially occurred.
If you instead put the sendText() in your onResume() like this,
#Override
protected void onResume() {
super.onResume();
sendText("Hello");
}
It will not give you the Null Pointer Exception. The fragment is still null when you call on it from onCreate().
Change your Fragment to this:
public class ReceivingFrag extends Fragment {
private TextView sender;
public void updateText(String text) {
sender.setText(text);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frag_sender, container, false);
sender = (TextView) v.findViewById(R.id.sender);
return v;
}
}
and in your activity, before calling the updateText method, make sure the fragment transaction has executed by doing:
public void sendText(String text){
ReceivingFrag frag = new ReceivingFrag();
getSupportFragmentManager().beginTransaction().add(R.id.receiving_container, frag).commit();
getSupportFragmentManager().executePendingTransactions();
frag.updateText(text);
}
((Button)findViewById(2131230768)).setOnClickListener(new MainMenuActivity(this));
View.OnClickListener runGameListener = new MainMenuActivity(this);
I have implemented onclicklistener even though it gives error on mainmenuactivity
my class is
public class MainMenuActivity
extends Activity implements OnClickListener
please help me..
Assume you have layout sample_activity.
Here's the code for sample_activity.xml :
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="GO!!"
android:id="#+id/buttonGo"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="170dp" />
</RelativeLayout>
And MainActivity.class :
public class MainActivity extends ActionBarActivity {
Button goButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sample_activity);
goButton = (Button) findViewById(R.id.buttonGo);
goButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//....Do all the stuffs here
}
});
}
First, never search for views by their generated id value. Use the identifier name instead, e.g.
findViewById(R.id.your_button)
Second, new with an Activity is not correct. Don't instantiate activities yourself.
Since your activity implements View.OnClickListener and assuming the code is in the same activity, just use
setOnClickListener(this)
That is,
findViewById(R.id.your_button).setOnClickListener(this);
(setOnClickListener() is a View method so casting to Button is not needed here.)
Firstly check your id of button from xml is write or not? I think your id is wrong its giving error..
I have a button in android which i have defined in fragment_main.xml as -
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/love_button_text"
android:onClick="onLoveButtonClicked"
/>
the main_activity xml file is as follows -
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.newa.newapp2.MainActivity"
tools:ignore="MergeRootFrame" >
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100sp"
android:text="#string/a_button"
android:onClick="onLoveButtonClicked1"
/>
</FrameLayout>
Now as there is a onClick event defined i wanted to define in a separate class file as follows -
public class AndroidLove extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onLoveButtonClicked1(View view) {
TextView tv = (TextView) findViewById(R.id.anotherText);
tv.setVisibility(View.VISIBLE);
}
}
This compiles fine but at run time throws the error
"could not find the event listener named onLoveButtonClicked"
This however is working fine if i write the function in my MainActivity.java
Any ideas?
Thanks
Use an anonymous inner class in your fragment class.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
Button button = (Button)view.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//your code here
}
}
}
Lastly remove this from your xml:
android:onClick="onLoveButtonClicked"
Hello the Button is in your fragment_main.xml file
and you are setting content view in activity_main to AndroidLove activity.
i think you need to write onLoveButtonClicked() method inside Fragment not in activity.
Method name from xml and java file must same.
activity/fragment that set that xml file as content view it must be have same name method with view as params.
Thanx.
I am relatively new to Android. I am writing an app that uses a fragment in the main activity. This fragment also acts like a location listener and implements the onLocationChange method. This is because, based on location changes new text views are being added to this fragment.
The onCreateView method in the fragment has its scroll view created that is added to the main activity. In the same onCreateView method, I also capture the context and the linear layout tag that is present in the fragment's layout xml where I would like to insert the text views that get created in onLocationChange method.
The issue I am seeing is the context and the layout values are null in onLocationChanged method and the updateView method. Using the debugger I confirmed that they do have non-null values in the onCreateMethod only.
Here is the code snippet for the fragment class:
public class ResultsFragment extends Fragment implements LocationListener,OnCompleteListener {
private LoadPlaces loadPlaces;
HashMap<String, Double> map=new HashMap<String, Double>();
LinearLayout layout;
Context context;
Activity activity;
ScrollView view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view =(ScrollView)inflater.inflate(R.layout.scroll_view, container, false);
layout=(LinearLayout)view.findViewById(R.id.resultsView);
context=view.getContext();
return view;
}
#Override
public void onLocationChanged(final Location location) {
loadPlaces=new LoadPlaces(context); //context is null here
loadPlaces.setOnCompleteListener(this);
loadPlaces.execute(String.valueOf(location.getLatitude()),String.valueOf(location.getLongitude()));
}
private void updateResultView(){
String status=loadPlaces.getResultStatus();
if("OK".equals(status)){
for (Place p:loadPlaces.getPlacesResult()){
TextView txt=new TextView(context); //context is null here
txt.setText(p.name);
txt.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));
layout.addView(txt); //layout is null here
}
}
}
#Override
public void onComplete(String result) {
updateResultView();
}
here is the fragment's layout xml:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/results"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:scrollbars="vertical"
android:scrollbarAlwaysDrawVerticalTrack="true"
android:clickable="true"
android:background="#color/grey"
android:layout_marginTop="25dip">
<LinearLayout android:id="#+id/resultsView"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
</LinearLayout>
And this is the main activity's xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:padding="30dip"
android:layout_gravity="center"
android:background="#color/background">
<fragment android:name="com.app.xxx.ResultsFragment"
android:id="#+id/headlines_fragment"
android:layout_weight="1"
android:layout_width="fill_parent"
android:layout_height="0dp"/>
For your information, the activity that holds the fragment is never paused/killed. It is always visible while the location service runs in the background. So in this process, I don't think the fragment is getting destroyed.
Am i approaching this problem incorrectly? Is there a better way to access or save the fragment's context and use it later? Any tips will be helpful.
Thanks in advance.
Change to:
#Override
public void onLocationChanged(final Location location) {
if(getActivity()!=null){
loadPlaces=new LoadPlaces(getActivity()); //context is null here
loadPlaces.setOnCompleteListener(this);
loadPlaces.execute(String.valueOf(location.getLatitude()),String.valueOf(location.getLongitude()));
}
}
The problem is location change listener is called before onCreateView.
You can ignore all the onLocationChanged calls until your fragment is attached to Activity by using above function.
public void onAttach(android.app.Activity activity) {
super.onAttach(activity);
context = (Context)getActivity();
};
I have a simple Android app. The layout folder shows an activity_main.xml file and a fragment_main.xml file. In that fragment.xml file I have placed a button that I've named buttonTest.
<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:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context="com.snapvest.thunderbird.app.MainActivity$PlaceholderFragment">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TEST"
android:id="#+id/buttonTest"
android:layout_gravity="center_horizontal" />
</LinearLayout>
in the MainActivity.java file I'm trying to gain access to that buttonTest.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
// Get access to the buttons
final Button buttonTest = (Button)findViewById(R.id.buttonTest);
}
It compiles just fine. But when I run it the buttonTest variable comes back as a null pointer.
Why is it not finding the buttonTest object?
I think I found the solution. MainActivity.java sets up the activity_main.xml which then uses fragment_main.xml as its (initially) single, primary fragment. That's why we are supposed to actually put all of our UI for the activity primarily in fragment_main.xml (and actually put NOTHING in activity_main.xml).
Near the bottom of MainActivity.java there is a section that initializes the fragment_main.xml. And it is THERE that I need to gain access to the buttons and other objects of the UI for the fragment. Such as:
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Get access to the buttons
final Button buttonAvailableOptions = (Button)rootView.findViewById(R.id.buttonAvailableOptions);
return rootView;
}
}
Most of that, above, is automatically added by Android Studio. The part starting with "Get access to the button" is mine and is where you set up any access to or processing of the buttons and other UI objects.
The reason why findViewById() is failing to find the button is because setContentView() primes findViewById() to only look in the layout.xml file you gave to setContentView(), and your button is not located in R.layout.activity_main. If your button is located in the Fragment (which it appears to be) then you should be accessing and manipulating it from the Fragment, not the Activity. This ensures that your Activity and Fragment logic stay decoupled, allowing you to re-use the Fragment with different Activities.
You can use like this.
final Button buttonAvailableOptions = getActivity().findViewById(R.id.buttonAvailableOptions);