Android 1.6 & Fragment & Tabhost - java

I'm working on upgrading an Android application (1.6 compatibility) which uses a TabHost to show 3 different tabs with nested activities.
At the time I used the ActivityGroup trick to show nested activities in a tab but I'm very unhappy with this method since it's a real pain to handle some features.
I heard about the Fragments API compatibility package for 1.6 and a Fragment looks perfect for what I want to do (show nested views / features within a tab with transition effects and stuff) but I can't make it work with a TabHost (It was meant to work with an Action Bar but it's not available in the compatibility package).
Did any of you guys found a way to create such a structure in your applications?
My error here is :
ERROR/AndroidRuntime(955): Caused by: java.lang.RuntimeException: Unable
to start activity
ComponentInfo{com.XXX}:
java.lang.IllegalArgumentException: No
view found for id 0x1020011 for
fragment MyFragment
CODE
main.xml
<TabHost xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/tabhost"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:padding="5dp"
android:layout_weight="1" />
<TabWidget
android:id="#android:id/tabs"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0" />
</LinearLayout>
</TabHost>
MainActivity.java
public class MainActivity extends TabActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Resources res = getResources();
final TabHost tabs = getTabHost();
TabHost.TabSpec spec;
Intent i;
i = new Intent(this, MyActivity.class);
spec = tabs.newTabSpec("MyActivity").setIndicator("MyActivity",res.getDrawable(R.drawable.tab)).setContent(i);
tabs.addTab(spec);
}
}
MyActivity.class
public class MyActivity extends FragmentActivity {
private static String TAG = "MyActivity";
private static FragmentManager fragmentManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ListeResultatFragment fragment = MyFragment.newInstance();
fragmentTransaction.add(android.R.id.tabcontent, fragment, "MyFragment");
fragmentTransaction.commit();
}
}
MyFragment.java
public class MyFragment extends Fragment {
public static MyFragment newInstance() {
MyFragment instance = new MyFragment();
return instance;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment, container, false);
}
}

The problem is that your MyActivity tries to reach out to the enclosing MainActivity by using android.R.id.tabcontent as a container id. This is not possible. Instead, MyActivity needs to have its own layout (e.g. a FrameLayout) which can be used as the parent for the Fragment. In this layout, there must exist a view that can be referenced by id.
Let's say you have a layout called activity_layout.xml which contains a FrameLayout with the id 'framelayout'. You can then modify the onCreate method in MyActivity to something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View parent = getLayoutInflater().inflate(R.layout.activity_layout, null);
setContentView(parent);
fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment fragment = MyFragment.newInstance();
fragmentTransaction.add(R.id.framelayout, fragment, "MyFragment");
fragmentTransaction.commit();
}
In other words, MyActivity needs to be able to work on its own. Try to make it work first, and then embed MyActivity in the MainActivity containing the TabHost.

Why don't you create your own tabbar. It's verry easy to build. Just add a LinearLayout with some buttons in it and set the onClickListener to switch fragments by using the FragmentManager. The Fragment manager can be obtained from a FragmentActivity.
FragmentManager fragmentManager = getSupportFragmentManager();
In the onClick handler you just do a transaction to the switch to the correct fragments.

Related

Can't call Fragment method in parent Activity

I simply want to call a method from a fragment in my MainActivity(parent).
But as soon as I try to call the method I get an NullPointerException.
Attempt to invoke virtual method 'void
com.example.fragmenttest.TestFragment.testMethod()' on a null object
reference
Here is what I do in the onCreate of the MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.containerView, new TestFragment()).commit();
TestFragment fragment = (TestFragment) fragmentManager.findFragmentById(R.id.testfragment);
fragment.testMethod();
}
and here is the fragment:
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View rootView=inflater.inflate(R.layout.activity_fragment,container,false);
return rootView;
}
public void testMethod(){
Toast.makeText(getContext(), "Test", Toast.LENGTH_LONG).show();
}
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/containerView">
</FrameLayout>
</RelativeLayout>
and activity_fragment.xml:
<?xml version="1.0" encoding="utf-8"?>
<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"
tools:context="com.example.fragmenttest.MainActivity"
android:id="#+id/testfragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="50dp"
android:text="Fragment" />
</RelativeLayout>
Of course this is not my real project, I just created a new one to simplify my issue.
In my real project I want to call a method from my fragment as soon as the onRewardedVideoCompleted method gets called, which is in my MainActivity.
How do I call the method from my fragment without getting a null pointer exception and without using an interface? (Using an interface for this small problem seems unnecessary)
Thanks
commit() is asynchronous. This is why your project is crashing upon launch. Instead of using commit(), use commitNow(). Also, instead of using new TestFragment(), create a variable so you can call its methods.
TestFragment testFragment= new TestFragment();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.containerView, testFragment).commitNow();
testFragment.testMethod();
Fragment transactions are asynchronous (unless you use executePendingTransactions()). Your transaction has likely not completed yet. You use runOnCommit on FragmentTransaction (in the support library) to execute code after it is done.
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
TestFragment yourFragment = newe TestFragment();
fragmentTransaction.replace(R.id.containerView, yourFragment).commit();
yourFragment.testMethod();
First of all, You must pass yourFragment to fragmentTransaction, then you can call any methods you want.

Android: Cant replace fragment inside activity with another fragment?

I'm new to android development and having some issues with replacing a fragment with another fragment.
In my MainFragment.java, the first thing I do in onCreate is check for internet connectivity, and if there is not, I replace that fragment with the InternetCheckFragment. However, in InternetCheckFragment.java, when I try to inflate my check_internet.xml, the app closes, but there is no error.
activity_main.xml:
<fragment 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"
android:id="#+id/main_browse_fragment"
android:name="com.ui.MainFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.ui.MainActivity"
tools:deviceIds="tv"
tools:ignore="MergeRootFrame" />
MainActivity.java:
public class MainActivity extends Activity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
MainFragment.java:
public class MainFragment extends VerticalGridFragment
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (isConnectedToInternet() == true)
{
// DO stuff
}
else
{
showInternetError();
}
}
public void showInternetError()
{
getFragmentManager().beginTransaction().replace(R.id.show_internet_error , new InternetCheckFragment())
.commit();
}
....
}
InternetCheckFragment.java:
public class InternetCheckFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
// Defines the xml file for the fragment
return inflater.inflate(R.layout.check_internet, parent, false);
}
}
check_internet.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"
android:id="#+id/show_internet_error"
android:background="#color/black_transparent">
<Button android:id="#+id/btn_retry"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="RETRY"
android:layout_centerInParent="true"/>
</RelativeLayout>
However, I'm getting the error No view found for id.
I'm sure I'm not setting something correctly? Can someone point me in the right direction?
Thanks!
You are inflating a Fragment in a Fragment using the Fragment's FragmentManager.
This FragmentManager cannot handle this.
Try using the SupportFragmentManager with AppCompatActivity.getSupportFragmentManager() and the support Fragments from android.support.v4.app.Fragment
Ugh!..it was a stupid mistake, I replaced R.id.show_internet_error within getFragmentManager().beginTransaction().replace(R.id.show_internet_error , new InternetCheckFragment()).commit(); with R.id.main_browse_fragment and now it works.

Default fragment on Activity Launch

I'm trying to have a fragment loaded as the default view for the activity, but I just get a blank screen and huge memory leaks.
Activity file onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
if (savedInstanceState == null) {
FragmentManager fragmentManager = getFragmentManager();
int fragmentTransaction = fragmentManager.beginTransaction()
.add(R.id.login_screen_fragment, new FragmentLogin())
.commit();
}
}
The XML for the activity:
<FrameLayout 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"
tools:context=".LoginActivity">
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/login_screen_fragment"
class="com.test.project.FragmentLogin"
/>
<fragment
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/forgotten_password_fragment"
class="com.test.project.FragmentForgottenPassword"
/>
</FrameLayout>
And the Fragment class (relevant parts):
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.fragment_login, container, false);
final Activity activity = getActivity();
... code ...
return view;
}
I followed instructions from a tutorial someone suggested earlier from a different question, but I must be doing something horribly wrong. Any ideas?
xml file
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/changeFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoginActivity">
</FrameLayout>
your activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
if (savedInstanceState == null) {
FragmentLogin f1= new FragmentLogin();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.changeFragment, f1);
fragmentTransaction.commit();
}
}
I'm new to Android and I ran into the same problem. Here's how I fixed mine.
So in your onCreate method, use getSupportFragmentManager() from the FragmentManager class then declare FragmentTransaction separately. See below. I did this for my case and it worked.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
if (savedInstanceState == null) {
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.login_screen_fragment, new FragmentLogin());
fragmentTransaction.commit();
}
Hope this helps solve your problem as well.
Sincerely,
sylvery
Just replace the fragment element with FrameLayout, then you can see your fragment.
If you wanted to add the Fragment programatically, then remove the <Fragment> in your Framelayout and add some ID unto it.
It should look like this:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/login_screen_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".LoginActivity" />

How do I make duplicate fragments get their individual layoutid?

I'm attempting to make an ultimate TicTacToe game, which requires running 9 instances of the same fragment at the same time. I house them Inside another fragment that'll control each. While performing other tasks.
I am trying to figure out a way to manage each fragment, have them talk to each other, and know who they are. (basically store its row and col and maybe allow the big layout run some methods) Is there a way to do that? I don't have much experience with fragment managers, but I'm not swapping anything out and It would seem like a lot of work for such a simple task.
Big Fragment
<LinearLayout
android:id="#+id/ll_Big_Row_0"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:baselineAligned="false" >
<fragment
android:id="#+id/board_0x0"
android:name="com.example.tictactectoe.Fragment_Small_Board"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:layout_weight="1" />
<fragment
android:id="#+id/board_0x1"
android:name="com.example.tictactectoe.Fragment_Small_Board"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:layout_weight="1" />
....
<LinearLayout/>
....
<LinearLayout/>
smallFragment.java
public class Fragment_Small_Board extends Fragment {
private int thisRow = 0;
private int thisCol = 0;
private ImageButton square0x0;
private ImageButton square0x1;
...
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.small_board, container, false);
setUpButtons(view);
buttonOnclickListeners();
return view;
}
public void setUpButtons(View view){
square0x0 = (ImageButton) view.findViewById(R.id.small_0_0);
square0x1 = (ImageButton) view.findViewById(R.id.small_0_1);
....
}
public void buttonOnclickListeners(){
square0x0.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
validateMove();
square0x0.setBackgroundResource(backgroundID);
square0x0.setImageResource(pictureID);
}
});
....
}
Don't put 9 fragments inside parent fragment because in your case, it doesn't look necessary. So what you can do is add these nine fragments inside your activity.and provide each fragment with communicator(interface) so that they can communicate via activity to each other.
To add fragments do something like this
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
fragment =new MyFragment();
ft.add(android.R.id.content,fragment,"myFragmentTag");
ft.commit();
and according to your functionalities keep on replacing fragments .
This link will give you brief idea about how to set up communicator inside fragment and extend it in your activity

Adding a New Fragment to a FrameLayout Yields IllegalStateException

I'm attempting to implement a Navigation Drawer within an app I'm trying to create. Currently, the content of each Activity is being displayed within a Fragment. Since I can't use a static <fragment /> within the layout, I opted to use a dummy <FrameLayout /> instead. Here is my layout:
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawerLayout_messages"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Main content view -->
<FrameLayout
android:id="#+id/messages_content"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!-- Drawer view -->
<ListView
android:id="#+id/messages_drawer_list"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:background="#F0F0F0"/>
</android.support.v4.widget.DrawerLayout>
Now, when trying to replace the <FrameLayout /> with a fragment, I guess this error:
java.lang.IllegalStateException: The specified child already has a
parent. You must call removeView() on the child's parent first.
This is the code I'm executing in order to add in the fragment:
public class MessagesActivity extends Activity {
public static final String TAG = Constants.PACKAGE + ".MessagesActivity";
private DrawerLayout drawerLayout;
private ActionBarDrawerToggle drawerToggle;
private ListView drawerItems;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "Starting activity: " + TAG);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messages);
drawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout_messages);
String[] drawerItemsList = getResources().getStringArray(R.array.drawer_items);
drawerItems = (ListView) findViewById(R.id.messages_drawer_list);
drawerItems.setAdapter(new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, drawerItemsList));
getActionBar().setDisplayHomeAsUpEnabled(true);
getActionBar().setHomeButtonEnabled(true);
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.drawable.ic_drawer, R.string.title_app, R.string.title_messages) {
public void onDrawerClosed(View view) {
getActionBar().setTitle(R.string.title_messages);
invalidateOptionsMenu();
}
public void onDrawerOpened(View view) {
getActionBar().setTitle(R.string.title_app);
invalidateOptionsMenu();
}
};
drawerLayout.setDrawerListener(drawerToggle);
if (savedInstanceState == null) {
Log.d(TAG, "Setting fragment");
setFragment(0);
}
Log.d(TAG, TAG + "'s layout is initialized. Now starting service connection");
}
public void setFragment(int position) {
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(R.id.messages_content, new MessagesFragment());
transaction.addToBackStack(null);
transaction.commit();
drawerItems.setItemChecked(position, true);
drawerLayout.closeDrawer(drawerItems);
}
}
That Activity file is basically pulled directly from Google's developer resources, yet I'm still getting an error message. Am I forgetting to do something?
EDIT: Added my MessagesFragment code for you all.
public class MessagesFragment extends Fragment {
public MessagesFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_messages, container);
}
}
return inflater.inflate(R.layout.fragment_messages, null);
Could you please try and use null instead of container.

Categories

Resources