What I am trying to achieve is to have a fragment that on tablet it shows as a DialogFragment, while on smartphone it would be shown as a regular fragment. I am aware there is already a similar post, but I am not able to make that work - apply the style to fragment.
To show things top down, MainActivity.java:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.action_next) {
decideToNext();
return true;
}
return super.onOptionsItemSelected(item);
}
private void decideToNext() {
String device = getString(R.string.device);
if ("normal".equalsIgnoreCase(device)) {
Intent intent = new Intent(this, DetailedActivity.class);
startActivity(intent);
} else if ("large".equalsIgnoreCase(device)) {
Log.d("SOME_TAG", "Yes, I am seeing this line on tablet only");
DetailedFragment fragment = DetailedFragment.newInstance();
getSupportFragmentManager().beginTransaction().add(fragment, "MAGIC_TAG").commit();
}
}
}
DetailedActivity is nothing much:
public class DetailedActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detailed_activity);
}
}
its layout:
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root_container"
android:name="com.myapps.sampleandroid.DetailedFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and the interesting DetailedFragment:
public class DetailedFragment extends Fragment {
public static DetailedFragment newInstance() {
return new DetailedFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Context contextThemeWrapper = new ContextThemeWrapper(getActivity(), R.style.MyDialogTheme);
LayoutInflater localInflater = inflater.cloneInContext(contextThemeWrapper);
return localInflater.inflate(R.layout.detailed_fragment, container, false);
}
}
... and its layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Regular Text" />
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button Dummy" />
</LinearLayout>
In onCreateView I've tried to set the custom styling but it doesn't seem to work for tablet.
Styling
res/values/styles.xml contains:
<resources>
<style name="AppTheme" parent="#style/Theme.AppCompat.Light.DarkActionBar">
</style>
<style name="MyDialogTheme" />
</resources>
while res/values-large/styles.xml:
<resources>
<!-- Is there anything I should add here? -->
<style name="MyDialogTheme" parent="#android:style/Theme.Dialog"/>
</resources>
I've nested MyDialogTheme from Theme.Dialog, but it doesn't seem to help.
On smartphone when tapping on "NEXT" action bar menu item I am seeing the detailed activity (snapshot from an SG2):
while tapping on the same "NEXT" menu item from the tablet it doesn't do anything (except viewing the message on Logcat: Yes, I am seeing this line).
What should I add more in styles.xml or in code in order to see DetailedFragment as a dialog for tablet?
EDIT
I've tried the solution Little Child proposed (to have a DialogFragment contain my initial fragment and show it). So, I've added a WrapperDetailedFragment:
public class WraperDetailedFragment extends DialogFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.wrap_detailed_fragment, container, false);
}
}
its layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root_container_dialog"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/wrapped_fragment_id"
android:layout_width="match_parent"
android:layout_height="match_parent"
class="com.myapps.sampleandroid.DetailedFragment" />
</LinearLayout>
The code from MainActivity changes to:
private void decideToNext() {
String device = getString(R.string.device);
if ("normal".equalsIgnoreCase(device)) {
Intent intent = new Intent(this, DetailedActivity.class);
startActivity(intent);
} else if ("large".equalsIgnoreCase(device)) {
Log.d("SOME_TAG", "Yes, I am seeing this line ...");
WraperDetailedFragment fragment = new WraperDetailedFragment();
fragment.show(getSupportFragmentManager(), "MAGICAL_TAG");
}
}
but when I'm trying to add this DialogFragment I am getting the following crash:
android.view.InflateException: Binary XML file line #8: Error inflating class fragment
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at com.myapps.sampleandroid.WraperDetailedFragment.onCreateView(WraperDetailedFragment.java:12)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:1478)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1082)
at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:304)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:676)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:746)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
at com.myapps.sampleandroid.WraperDetailedFragment.onCreateView(WraperDetailedFragment.java:12)
at android.support.v4.app.Fragment.performCreateView(Fragment.java:1478)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:927)
at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:682)
at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1460)
at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:440)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IllegalArgumentException: Binary XML file line #8: Duplicate id 0x7f050047, tag null, or parent id 0x0 with another fragment for com.myapps.sampleandroid.DetailedFragment
at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:290)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:676)
... 28 more
This issue can be easily fixed if instead of trying to make a Fragment look like a DialogFragment I would look from the opposite angle: make a DialogFragment look like a Fragment - after all, a DialogFragment is a Fragment!
The key of this fix is to call or not DialogFragment.setShowsDialog();
So changing the DetailedFragment to:
public class DetailedFragment extends DialogFragment {
private static final String ARG_SHOW_AS_DIALOG = "DetailedFragment.ARG_SHOW_AS_DIALOG";
public static DetailedFragment newInstance(boolean showAsDialog) {
DetailedFragment fragment = new DetailedFragment();
Bundle args = new Bundle();
args.putBoolean(ARG_SHOW_AS_DIALOG, showAsDialog);
fragment.setArguments(args);
return fragment;
}
public static DetailedFragment newInstance() {
return newInstance(true);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Bundle args = getArguments();
if (args != null) {
setShowsDialog(args.getBoolean(ARG_SHOW_AS_DIALOG, true));
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.detailed_fragment, container, false);
}
}
its layout remains as it was, DetailedActivity changes to:
public class DetailedActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.detailed_activity);
if (savedInstanceState == null) {
DetailedFragment fragment = DetailedFragment.newInstance(false);
getSupportFragmentManager().beginTransaction().add(R.id.root_layout_details, fragment, "Some_tag").commit();
}
}
}
its layout as well:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/root_layout_details"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and the caller activity does only:
private void decideToNext() {
String device = getString(R.string.device);
if ("normal".equalsIgnoreCase(device)) {
Intent intent = new Intent(this, DetailedActivity.class);
startActivity(intent);
} else if ("large".equalsIgnoreCase(device)) {
DetailedFragment fragment = DetailedFragment.newInstance();
fragment.show(getSupportFragmentManager(), "Tablet_specific");
}
}
To make a DialogFragment show as a regular Fragment, call add() or replace() using a resource ID for the fragment container, e.g
beginTransaction().add(R.id.fragment_container, fragment)
But to make the DialogFragment display as a dialog, call add(fragment, "Fragment Tag"). Behind the scenes this results in a call to add(resId, fragment), setting the resource ID for the fragment container to 0, which causes the DialogFragment to set its showAsDialog option to true.
As a consequence you can use a dialog fragment as a dialog or a regular fragment - depending on your needs - without needing to create any special logic to do it.
OK, I have been there and done that. To make a DialogFragment you need a layout defined in XML. Say, for this you have LinearLayout as root. In this LinearLayout you can add a fragment class="...." and when you display your DialogFragment, it will be the same Fragment you displayed side by side on the tablet now displayed in a DialogFragment.
<LinearLayout 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:orientation="vertical">
<fragment class="com.example.tqafragments.FeedFragment" android:id="#+id/feedFragment" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_weight="1"/>
</LinearLayout>
Like so. And inflate this in your DialogFragment
Related
My Android app crashes when resuming after being killed by OS due to low memory.
So I have built the smallest possible app to reproduce the bug.
My app is composed of a MainActivity, which contains 1 page in a viewPager2, which contains a fragment.
This fragment is added dynamically by replacing "structure_placeholder" (in the real app, the type of fragment depends on some parameters)
MainActivity.java:
public class MainActivity extends AppCompatActivity
{
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this);
ViewPager2 viewPage = findViewById(R.id.viewpager);
viewPage.setAdapter(sectionsPagerAdapter);
}
/***************************************************************************/
class SectionsPagerAdapter extends FragmentStateAdapter
{
private SectionsPagerAdapter(FragmentActivity fa)
{
super(fa);
}
#NonNull #Override
public Fragment createFragment(int position)
{
return new MyPageFragment();
}
#Override
public int getItemCount()
{
return 1;
}
}
}
activity_main.xml:
<LinearLayout
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=".MainActivity">
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
MyPageFragment.java:
public class MyPageFragment extends Fragment
{
public MyPageFragment()
{ /* Required empty public constructor*/}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.my_page_fragment, container, false);
}
#Override
public void onStart()
{
super.onStart();
Fragment listFragment = SubFragment.newInstance();
FragmentTransaction ft = requireActivity().getSupportFragmentManager().beginTransaction();
ft.replace(R.id.structure_placeholder, listFragment);
ft.commit();
}
}
my_page_fragment.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:context=".MyPageFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_blank_fragment" />
<FrameLayout
android:id="#+id/structure_placeholder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
tools:layout_width="100dp"
tools:layout_height="150dp"
tools:background="#color/design_default_color_secondary"
>
</FrameLayout>
</LinearLayout>
SubFragment.java:
public class SubFragment extends Fragment
{
public SubFragment()
{ /* Required empty public constructor*/ }
public static SubFragment newInstance()
{
SubFragment fragment = new SubFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public View onCreateView(#NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.sub_fragment, container, false);
}
}
sub_fragment.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/line1" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/line2" />
</LinearLayout>
Here is the error log when resuming the app after it has been killed:
2021-11-19 15:46:55.739 8298-8298/com.delrocq.mytestapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.delrocq.mytestapp, PID: 8298
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.delrocq.mytestapp/com.delrocq.mytestapp.MainActivity}: java.lang.IllegalArgumentException: No view found for id 0x7f0801db (com.delrocq.mytestapp:id/structure_placeholder) for fragment SubFragment{9eb5496} (ada1c808-c885-49ed-9cd3-f4de1855c6af id=0x7f0801db)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2957)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3032)
at android.app.ActivityThread.-wrap11(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1696)
at android.os.Handler.dispatchMessage(Handler.java:105)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Caused by: java.lang.IllegalArgumentException: No view found for id 0x7f0801db (com.delrocq.mytestapp:id/structure_placeholder) for fragment SubFragment{9eb5496} (ada1c808-c885-49ed-9cd3-f4de1855c6af id=0x7f0801db)
I tried to put some logs to understand the lifecycle of the activity/fragments, but it did not help.
What am I doing wrong?
I finally found the solution after hours of searching and many wrong tracks.
Inside a fragment, getChildFragmentManager() shall be used instead of getSupportFragmentManager().
It is so easy when you know it :)
This question already has answers here:
android.content.ActivityNotFoundException:
(24 answers)
Closed 4 years ago.
i just want to open SkipActivity from MondayFragment, and fail in error. The swipe between fragments is work, but when i click on skip button move to another Activity- the app is crushed(closed):
All relevant code is attached, please help:
public class MondayFragment extends Fragment {
final String LOG_TAG="myLogs";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.fragment_monday, container, false);
TextView txt=(TextView) v.findViewById(R.id.skip);
txt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
((TextView)getActivity().findViewById(R.id.skip)).setText("Access from Monday Fragment");
//Start your activity here
Intent i = new Intent(getActivity(),SkipActivity.class);
startActivity(i);
}
});
return v;
}
}
This is java Activity class :
public class SkipActivity extends AppCompatActivity {
#Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.after_skip_scr);
}
}
This is MainActivity class - i think there is nothing to change here:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Set the content of the activity to use the activity_main.xml layout file
setContentView(R.layout.activity_main);
// Find the view pager that will allow the user to swipe between fragments
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
// Create an adapter that knows which fragment should be shown on each page
SimpleFragmentPagerAdapter adapter = new SimpleFragmentPagerAdapter(getSupportFragmentManager());
// Set the adapter onto the view pager
viewPager.setAdapter(adapter);
}
}
Below is res-layout files: The first one is for monday fragment
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:id="#+id/monday_main"
android:background="#a7cbeb">
<TextView
android:id="#+id/welcome_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_centerInParent="true"
android:textColor="#191970"
android:text="Welcome to Circles"
android:textSize="29sp"
/>
<TextView
android:id="#+id/skip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="54px"
android:textStyle="italic"
android:layout_alignParentEnd="true"
android:layout_alignParentTop="true"
android:layout_marginRight="25dp"
android:layout_marginTop="25dp"
android:text="skip"
android:textColor="#ffffff"
android:onClick="openSkipActivity"
/>
</RelativeLayout>
And this one is for main activity:
<LinearLayout 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:orientation="vertical"
tools:context="MainActivity">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
you can call activity by :
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(getActivity(), Contact_Developer.class);
getActivity().startActivity(intent);
}
});
why are you take this line
((TextView)getActivity().findViewById(R.id.skip)).setText("Access from Monday Fragment");
and share logcat error log.
or try this
decalare this
Context context;
txt.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(context, SkipActivity.class);
context.startActivity(intent);
}
});
Your app is crashing on click event because your view is null the reason is you are trying to find out the view by using activity.findViewById which will obvious return null.
((TextView)getActivity().findViewById(R.id.skip)).setText("Access from Monday Fragment");
Change above line with this
((TextView)v.findViewById(R.id.skip)).setText("Access from Monday Fragment");
After all you are launching activity from onClick event so doesn't make sense to update the text of skip textview. but if still you want to do that, no problem.
Just replace the above line of code, it should fix your issue.
As a suggestion in case you want to update the skip textview text in on click event or any other place, create a class level instance say TextView skipTextView and initialize in onCreateView then simply use skipTextView.setTex("<text>")" all over the place where ever required.
#Dima , Try this :
public class MondayFragment extends Fragment implements OnClickListener
{
final String LOG_TAG="myLogs";
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v=inflater.inflate(R.layout.fragment_monday, container, false);
// Views
TextView txt=(TextView) v.findViewById(R.id.skip);
txt.setOnClickListener(this);
return v;
}
// onClick Method
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.skip:
((TextView)getActivity().findViewById(R.id.skip)).setText("Access from Monday Fragment");
Intent i = new Intent(getActivity(),SkipActivity.class);
startActivity(i);
break;
default:
break;
}
}
}
tnx for answers!
The problem was - i didn't declare the SkipActivity in manifest file.
At the moment i did it- the app is running and i can swipe between fragments and launch another Activity from fragment.
tnx a lot!
I've been searching the error for about 2 hours on google , i can't figure out what it's wrong . It just force closes when I tap the button "butonCap", I didn't work with fragments until now
ERROR:
java.lang.IllegalStateException: Could not find method butonCap(View) in a parent or ancestor Context for android:onClick attribute defined on view class android.support.v7.widget.AppCompatButton with id 'cap'
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.resolveMethod(AppCompatViewInflater.java:423)
at android.support.v7.app.AppCompatViewInflater$DeclaredOnClickListener.onClick(AppCompatViewInflater.java:380)
at android.view.View.performClick(View.java:6897)
at android.widget.TextView.performClick(TextView.java:12693)
at android.view.View$PerformClick.run(View.java:26101)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6944)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
Butoane.java - i just made another java file because i can't write any code in the fragment java file
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_fata);
final Button butonCap = (Button) findViewById(R.id.cap);
butonCap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent butonCap = new Intent(Butoanele.this, capul.class);
Butoanele.this.startActivity(butonCap);
}
});
}
}
fragment_fata.xml - this is just one of the 2 fragments from navigation view
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".FragmentFata"
android:background="#drawable/fata">
<!-- TODO: Update blank fragment layout -->
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/cap"
android:layout_width="wrap_content"
android:layout_height="65dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="4dp"
android:background="#drawable/button_outline"
android:paddingBottom="30dp"
android:onClick="butonCap"
android:text="Cap"
android:textSize="18sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
You class should extends Fragment and inflate layout in onCreateView() callback.
Call getActivity().findViewById in onViewCreated() callback.
watch this guide
In fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_fata, container, false);
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
if (getView() == null) {
return;
}
final Button butonCap = (Button) getView().findViewById(R.id.cap);
butonCap.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent intent = new Intent(getView().getContext(), capul.class);
startActivity(intent);
}
});
}
I'm trying to inflate a simple PopupMenu for rename/delete options when a RecylerView item is longClicked. For some reason i'm getting an XML inflate error when I call mPopup.show() after loading my xml file into the inflater.
I use similar logic elsewhere in my app to make a PopupMenu and it works fine. I've even tried loading the working PopupMenu from an unrelated part of the app into this inflater and I see the same android.view.InflateException: Binary XML file line #17: Failed to resolve attribute at index 1 error in logcat, so maybe the XML file isn't the problem?
How can I get this PopupMenu to inflate and show itself?
Fatal Exception Logcat
05-31 23:02:27.421 19597-20019/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.foo, PID: 19597
android.view.InflateException: Binary XML file line #17: Failed to resolve attribute at index 1: TypedValue{t=0x2/d=0x7f01005d a=-1}
Caused by: java.lang.UnsupportedOperationException: Failed to resolve attribute at index 1: TypedValue{t=0x2/d=0x7f01005d a=-1}
at android.content.res.TypedArray.getLayoutDimension(TypedArray.java:761)
at android.view.ViewGroup$LayoutParams.setBaseAttributes(ViewGroup.java:7060)
at android.view.ViewGroup$MarginLayoutParams.<init>(ViewGroup.java:7241)
at android.widget.FrameLayout$LayoutParams.<init>(FrameLayout.java:438)
at android.widget.FrameLayout.generateLayoutParams(FrameLayout.java:370)
at android.widget.FrameLayout.generateLayoutParams(FrameLayout.java:369)
at android.view.LayoutInflater.inflate(LayoutInflater.java:505)
at android.view.LayoutInflater.inflate(LayoutInflater.java:426)
at android.support.v7.view.menu.MenuAdapter.getView(MenuAdapter.java:93)
at android.support.v7.view.menu.MenuPopup.measureIndividualMenuWidth(MenuPopup.java:160)
at android.support.v7.view.menu.StandardMenuPopup.tryShow(StandardMenuPopup.java:153)
at android.support.v7.view.menu.StandardMenuPopup.show(StandardMenuPopup.java:187)
at android.support.v7.view.menu.MenuPopupHelper.showPopup(MenuPopupHelper.java:290)
at android.support.v7.view.menu.MenuPopupHelper.tryShow(MenuPopupHelper.java:175)
at android.support.v7.view.menu.MenuPopupHelper.show(MenuPopupHelper.java:141)
at android.support.v7.widget.PopupMenu.show(PopupMenu.java:233)
at com.example.foo.FragmentChordMenu.showChordOptionsMenu(FragmentChordMenu.java:132)
at com.example.foo.CustomChordAdapter$ChordViewHolder$2.onLongClick(CustomChordAdapter.java:138)
at android.view.View.performLongClickInternal(View.java:5687)
at android.view.View.performLongClick(View.java:5645)
at android.view.View.performLongClick(View.java:5663)
at android.view.View$CheckForLongPress.run(View.java:22234)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6077)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:865)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:755)
FragmentActivity
public class FragmentChordMenu extends Fragment implements CustomChordAdapter.onItemClickListener {
private static RecyclerView mCustomChordList;
private static CustomChordAdapter mRecyclerViewAdapter;
private static Context mContext;
private FloatingActionButton mFAB;
private View mPopupView;
private PopupWindow mCustomChordMenu;
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mRecyclerViewAdapter = new CustomChordAdapter(this);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
mContext = getActivity().getApplicationContext(); //stores application context for later use in fragment without risk
//of detachment
View v = inflater.inflate(R.layout.menu_fragment_chord, container, false);
LayoutInflater layoutInflater = (LayoutInflater)getActivity().getBaseContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
...
mFAB = (FloatingActionButton) v.findViewById(R.id.addChord);
mFAB.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mCustomChordMenu.showAtLocation(mPopupView, Gravity.CENTER, 10, 10);
mCustomChordList = (RecyclerView) mPopupView.findViewById(R.id.rv_userChords);
LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity());
mCustomChordList.setLayoutManager(layoutManager);
mCustomChordList.setAdapter(mRecyclerViewAdapter);
}
});
return v;
}
public static void showChordOptionsMenu(final int position){
View anchorView = mCustomChordList.findViewHolderForAdapterPosition(position).itemView;
PopupMenu mPopup = new PopupMenu(mContext, anchorView);
mPopup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
#Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()){
case R.id.delete:
mRecyclerViewAdapter.deleteChord(position);
return true;
case R.id.rename:
Log.d("FragmentChordMenu: ", "Rename clicked");
}
return true;
}
});
MenuInflater popupInflater = mPopup.getMenuInflater();
popupInflater.inflate(R.menu.popup_delete_chord, mPopup.getMenu());
mPopup.show(); //ERROR HERE
}
...
}
PopupMenu XML
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<item
android:id="#+id/rename"
android:title="#string/rename"/>
<item
android:id="#+id/delete"
android:title="#string/delete"/>
</menu>
FragmentActivity 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:android.support.design="http://schemas.android.com/tools"
android:id="#+id/chordMenu"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:id="#+id/scrollview"
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:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/chordButtons"
android:orientation="vertical" >
</LinearLayout>
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_margin="16dp"
android:clickable="true"
app:fabSize="mini"
android:id="#+id/addChord"
app:borderWidth="0dp"
app:useCompatPadding="false"
android:src="#drawable/ic_add_black_24dp"/>
</LinearLayout>
</ScrollView>
</RelativeLayout>
The cause of the immediate issue here - the InflateException - was using the application Context for an appcompat-v7 PopupMenu.
mContext = getActivity().getApplicationContext();
...
PopupMenu mPopup = new PopupMenu(mContext, anchorView);
That Context won't have the correct theme resources set for a v7 widget, leading to the InflateException. The Activity does have the appropriate theme, though, and using that instead solves that particular issue.
mContext = getActivity();
After fixing that, however, a WindowManager$BadTokenException came up due to the PopupMenu being passed an anchor View from a PopupWindow. Popups must be anchored to a View in a top-level Window, and the Popup* classes are basically simple Views, thus the Exception.
A simple solution for this is to replace the PopupWindow with a Dialog, which does have a Window. For example:
AlertDialog dlg = new AlertDialog.Builder(getActivity()).setView(mPopupView).show();
Lastly, I would suggest that you modify your setup to remove the need for the static members in your Fragment class. Those static fields are likely to cause memory leaks, and your IDE may very well be warning you of that now. A listener interface similar to the one you have in CustomChordAdapter would suffice.
I decided to switch from multiple activities to one activity which switches between fragments however the application now crashes.
Here is the activity I am adding the fragment to
public class MainActivity extends SherlockFragmentActivity{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyFragment fragment = new MyFragment();
fragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment).commit();
}
Here is the fragment its an observer and has functionality but to save space ill just show the creation
public class MyFragment extends SherlockFragment implements Observer{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.my_fragment, container, false);
}
Heres my_fragment.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/my_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" >
... HAS SOME TEXT VIEWS HERE!
</RelativeLayout>
The crash I get is
02-15 16:17:41.079: E/AndroidRuntime(18668): FATAL EXCEPTION: main
02-15 16:17:41.079: E/AndroidRuntime(18668): java.lang.RuntimeException:
Unable to start activity ComponentInfo{com.example.myapp/com.example.myapp.MainActivity}:
java.lang.IllegalArgumentException:
No view found for id 0x7f040036 for fragment MyFragmentt{41a05910 #0 id=0x7f040036}
Can anyone help me out here? I can't really figure out what is causing this. I know if I comment out getSupportFragmentManager() in main activity (the top code block in this post) it will run just not draw anything in my fragment.
UPDATE
The frame_container which I'm not sure where to place
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
So the way you can use Fragments in your applications are two.
First way is if you declare the Fragment in your xml file like this :
<fragment android:name="com.example.news.ArticleReaderFragment"
android:id="#+id/viewer"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
The second way is if you add / replace your Fragments dynamically to your container which in the most examples is FrameLayout. Here is how you can do that :
In your main FragmentActivity :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myFragmentContainer);
}
and in your xml myFragmentContainer.xml is where you place your fragment_container and it looks like :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
and you are adding and replacing your Fragments like this :
if (findViewById(R.id.fragment_container) != null) {
if (savedInstanceState != null) {
return;
}
// Create an instance of ExampleFragment
HeadlinesFragment firstFragment = new HeadlinesFragment();
// if there are any extras
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
and for the next Fragment which you want to show just do :
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, secondFragment).commit();