Research has been fruitless because all other references to this error seem to be reliably repeatable; I'm unable to consistently repeat this error:
08-22 17:32:25.216 22699-22699/com.smbds.punisher11 E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.IllegalArgumentException: width and height must be > 0
at android.graphics.Bitmap.createBitmap(Bitmap.java:1023)
at android.graphics.Bitmap.createBitmap(Bitmap.java:1002)
at android.graphics.Bitmap.createBitmap(Bitmap.java:985)
at com.github.amlcurran.showcaseview.ShowcaseView.updateBitmap(ShowcaseView.java:171)
at com.github.amlcurran.showcaseview.ShowcaseView.onGlobalLayout(ShowcaseView.java:354)
at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:655)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2054)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1190)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:4860)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:766)
at android.view.Choreographer.doCallbacks(Choreographer.java:575)
at android.view.Choreographer.doFrame(Choreographer.java:542)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:751)
at android.os.Handler.handleCallback(Handler.java:725)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:5751)
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:1083)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:850)
at dalvik.system.NativeStart.main(Native Method)
As I stated above, this error does not occur all the time. It seems to happen about (but not exactly) 2-3 times, then 2-3 runs go through without a problem.
Here is my resources file:
<?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:id="#+id/container_fragment_person_list"
android:tag="PersonListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.PersonListFragment">
<TextView
android:id="#+id/textView_add_guardian_placeholder"
android:layout_centerInParent="true"
android:layout_width="1dp"
android:layout_height="1dp" />
<TextView
android:id="#+id/textView_intro_placeholder"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:minWidth="1dp"
android:minHeight="1dp"
android:layout_width="1dp"
android:layout_height="1dp" />
<ListView
android:visibility="gone"
android:id="#id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:orientation="vertical"
android:id="#id/android:empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center" >
<TextView
android:text="#string/no_people"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ImageButton
android:id="#+id/imageButton_empty_text"
android:src="#drawable/ic_man_silhouette_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:contentDescription="#string/generic_content_description"/>
<TextView
android:id="#+id/test"
android:text="#string/tap_icon_to_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</RelativeLayout>
And here is my java. Please notice that I even tried hard-coding a width and height for my ViewTarget, but still get inconsistent errors. I also tried creating placeholder TextViews in my resources file, but still no go.
public class PersonListFragment extends BaseListFragment {
private PersonAdapter personAdapter;
private PersonModel personModel;
private int personCount;
private TextView tvTargetIntro;
public static PersonListFragment newInstance(String tag, Long index) {
PersonListFragment fragment = new PersonListFragment();
Bundle args = new Bundle();
args.putString(TAG, tag);
args.putLong(INDEX, index);
fragment.setArguments(args);
return fragment;
}
public PersonListFragment() {
}
#SuppressWarnings("UnusedDeclaration")
public void onEventMainThread(People_FetchedEvent ignored) {
refreshList();
}
#SuppressWarnings("UnusedDeclaration")
public void onEventMainThread(Person_CreatedEvent ignored) {
String createType;
if(ignored.getId() == 0 | ignored.getPerson().getId() == null) {
createType = resources.getString(R.string.added);
} else {
createType = resources.getString(R.string.updated);
}
MessageManager.getInstance().makeToast(getActivity(), ignored.getPerson().getName(), " ", createType);
fm.popBackStack();
refreshList();
}
#SuppressWarnings("UnusedDeclaration")
public void onEventMainThread(Person_BuiltEvent ignored) {
makeCreatePersonBackgroundTask(ignored.getPerson());
}
#SuppressWarnings("UnusedDeclaration")
public void onEventMainThread(Person_DeletingEvent ignored) {
personModel.deletePersonByLocalId(ignored.getId());
MessageManager.getInstance().makeToast(getActivity(), PersonController.getInstance().getCurrentPerson().getName() + " " + resources.getString(R.string.removed));
refreshList();
FragmentManager fm = getChildFragmentManager();
fm.popBackStack();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
personModel = PersonModel.getInstance();
personAdapter = new PersonAdapter(getActivity().getLayoutInflater(), getActivity(), ListType.PROFILE_INFO);
setListAdapter(personAdapter);
personCount = PersonModel.getInstance().getPersonCount();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_person_list, container, false);
LinearLayout layoutEmptyList = (LinearLayout) view.findViewById(android.R.id.empty);
tvTargetIntro = (TextView) view.findViewById(R.id.textView_intro_placeholder);
ImageButton imageButtonEmptyText = (ImageButton) view.findViewById(R.id.imageButton_empty_text);
imageButtonEmptyText.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction ft;
ft = fm.beginTransaction();
ft.replace(R.id.container_fragment_person_list, PersonEditFragment.newInstance("PERSON_EDIT_FRAGMENT", (long) 0, new Person()), "PERSON_EDIT_FRAGMENT");
ft.addToBackStack(null);
ft.commit();
fm.executePendingTransactions();
}
});
switch(personCount) {
case 0:
break;
case 1:
break;
default:
}
return view;
}
private void makeShowCaseIntro() {
ViewTarget target1 = new ViewTarget(tvTargetIntro);
tvTargetIntro.setWidth(1);
tvTargetIntro.setHeight(1);
ShowcaseView sv = new ShowcaseView.Builder(getActivity())
.setTarget(target1)
.setContentTitle(resources.getString(R.string.showcase_intro_title))
.setContentText(resources.getString(R.string.showcase_intro_text))
.setStyle(R.style.CustomShowcaseTheme2)
.build();
sv.setButtonText(resources.getString(R.string.next));
sv.show();
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
makeShowCaseIntro();
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
PersonController.getInstance().setCurrentPerson(personAdapter.getItem(position));
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container_fragment_person_list, PersonDisplayFragment.newInstance("PERSON_DISPLAY_FRAGMENT", PersonController.getInstance().getLocalId(), PersonController.getInstance().getCurrentPerson()), "PERSON_DISPLAY_FRAGMENT");
ft.addToBackStack(null);
ft.commit();
fm.executePendingTransactions();
}
private void refreshList() {
new SimpleBackgroundTask<LazyList<Person>>(getActivity()) {
#Override
protected LazyList<Person> onRun() {
return PersonModel.getInstance().lazyLoadPeople();
}
#Override
protected void onSuccess(LazyList<Person> result) {
personAdapter.replaceLazyList(result);
}
}.execute();
}
private void makeCreatePersonBackgroundTask(Person person) {
jobManager.addJobInBackground(new CreatePersonJob(person));
fm.popBackStack();
}
#Override
public void onResume() {
super.onResume();
jobManager.addJobInBackground(new FetchPeopleJob());
if(dataDirty) {
refreshList();
dataDirty = false;
}
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
FragmentTransaction ft;
Person person = PersonController.getInstance().getCurrentPerson();
ft = fm.beginTransaction();
switch(item.getItemId()) {
case R.id.action_new:
ft.replace(R.id.container_fragment_person_list, PersonEditFragment.newInstance("PERSON_EDIT_FRAGMENT", (long) 0, new Person()), "PERSON_EDIT_FRAGMENT");
ft.addToBackStack(null);
ft.commit();
fm.executePendingTransactions();
return true;
case R.id.action_edit:
ft.replace(R.id.container_fragment_person_list, PersonEditFragment.newInstance("PERSON_EDIT_FRAGMENT", person.getId(), person), "PERSON_EDIT_FRAGMENT");
ft.addToBackStack(null);
ft.commit();
fm.executePendingTransactions();
return true;
case R.id.action_remove:
String title = MessageManager.getInstance().makeString(resources.getString(R.string.remove), " ", person.getName());
String message = MessageManager.getInstance().makeString(resources.getString(R.string.message_confirm_remove), " ", person.getName(), resources.getString(R.string.punctuation_question));
final InfoDialogFragment dialog = InfoDialogFragment.newInstance(title, message, DialogActionType.Delete, DialogActionTarget.Person, person.getId());
dialog.show(ft, DIALOG_TAG);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
I'm pretty sure this is some timing issue with the way android draws out objects, but I don't have the knowledge to know why they're drawn correctly on some runs but not on others.
Please don't zap this question unless you're sure that it has been asked elsewhere with respect to an intermittent problem :-)
Any help is appreciated.
This is a known issue : Github issues when updateBitmap() is called too early.
This issue comes from Bitmap.createBitmap() method either width or height equals to 0.
A workaround is to delay the ShowcaseView creation more:
someView.post(new Runnable() {
#Override
public void run() {
// display ShowcaseView here
}
});
You could also change the updateBitmap() method from the library to not create the bitmap if the view width or height is wrong.
Had the same problem, and posted my solution in another thread:
https://stackoverflow.com/a/25521608/1752670
I changed the ShowcaseView class slightly
go to com.github.amlcurran.showcaseview.ShowcaseView and change this method
private void updateBitmap() {
if ((bitmapBuffer == null || haveBoundsChanged()) && getMeasuredHeight() > 0 && getMeasuredWidth() > 0) {
if(bitmapBuffer != null)
bitmapBuffer.recycle();
int width = getMeasuredWidth();
int height = getMeasuredHeight();
if(width > 0 && height > 0)
bitmapBuffer = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
}
}
This was a bug in ShowcaseView, and was fixed starting in v5.4.2:
https://github.com/amlcurran/ShowcaseView/commit/c79c60dc798f081fb62e48c549c7bdbff8da51b9
So, you can just update your build.gradle to:
compile 'com.github.amlcurran.showcaseview:library:5.4.2'
Related
I have SettingActivity. It contains SettingFragment (with ListPreference) and another AudioSamplingSeekBarFragment(with my custom preference in it). When i choose some item in ListPreference, i recreate my AudioSamplingSeekBarFragment with chosen data from ListPreference. like this:
public class SettingsFragment extends PreferenceFragment {
private static Logger log = Logger.getLogger(SettingsFragment.class.getName());
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
ListPreference outputFormatPref = (ListPreference) findPreference(getResources().getString(R.string.key_encoder));
outputFormatPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
MySharedPreferences.setAudioEncoder(getActivity(), (String) newValue);
**embedSeekBarWithFormat((String) newValue);**
return true;
}
});
}
**public void embedSeekBarWithFormat(String format) {
try {
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
AudioSamplingSeekBarFragment fragment = (AudioSamplingSeekBarFragment) manager.findFragmentByTag(AudioSamplingSeekBarFragment.TAG);
if (manager.findFragmentByTag(AudioSamplingSeekBarFragment.TAG) != null) {
transaction.remove(fragment);
}
AudioSamplingSeekBarFragment newFragment = new AudioSamplingSeekBarFragment();
Bundle bundle = new Bundle();
bundle.putInt(AudioSamplingSeekBarFragment.STATE_FORMAT, Integer.parseInt(format));
newFragment.setArguments(bundle);
transaction.add(R.id.seekBar_container, newFragment, AudioSamplingSeekBarFragment.TAG);
transaction.commit();
} catch (Exception e) {
log.log(Level.SEVERE, "Exception: ", e);
}
}
}**
So when i quite the SettingActivity and go into again my AudioSamplingSeekBarFragment doesn't save state.
I put my fragment in onSaveInstanceState() in SettingsActivity as they say here: topic
public class SettingsActivity extends android.support.v7.app.ActionBarActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_preferences);
if (savedInstanceState != null) {
getFragmentManager()
.beginTransaction()
.add(R.id.seekBar_container, getFragmentManager().getFragment(savedInstanceState, AudioSamplingSeekBarFragment.TAG) , AudioSamplingSeekBarFragment.TAG)
.commit();
} else {
getFragmentManager()
.beginTransaction()
.add(R.id.seekBar_container, new AudioSamplingSeekBarFragment(), AudioSamplingSeekBarFragment.TAG)
.commit();
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getFragmentManager()
.putFragment(outState, AudioSamplingSeekBarFragment.TAG, getFragmentManager().findFragmentByTag(AudioSamplingSeekBarFragment.TAG));
}
And save specific data in onSaveInstanceState() in Fragment:
public class AudioSamplingSeekBarFragment extends Fragment {
public static final String TAG = "SEEK_BAR_FRAGMENT_TAG";
public static final String STATE_FORMAT = "format";
private int format;
private int seekBarInitVal;
**#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(STATE_FORMAT, format);
}**
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_audio_sampling_seekbar, container,false);
}
And i'm expecting to restore saved data here:
#Override
public void onViewCreated(final View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState != null) {
**format = savedInstanceState.getInt(STATE_FORMAT);**
} else if (getArguments() != null){
format = getArguments().getInt(STATE_FORMAT);
} else {
format = AAC;
}
SeekBar seekBar = (SeekBar)view.findViewById(R.id.sample_rate_seek_bar);
final TextView textProgress = (TextView) view.findViewById(R.id.progress);
switch (format) {
case AAC:
seekBarInitVal = 8000;
seekBar.setMax(40000);
break;
case AAC_ELD:
seekBarInitVal = 16000;
seekBar.setMax(32000);
break;
case AMR_NB:
seekBarInitVal = 8000;
seekBar.setEnabled(false);
break;
case AMR_WB:
seekBarInitVal = 16000;
seekBar.setEnabled(false);
break;
case HE_AAC:
seekBarInitVal = 8000;
seekBar.setMax(40000);
break;
}
textProgress.setText("" + seekBarInitVal + " Hz");
seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
i += seekBarInitVal;
textProgress.setText("" + i + " Hz");
}
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
#Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}
But it doesn't work.
Just in case layouts:
activity_preference:
<?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">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar"/>
<FrameLayout
android:id="#+id/preferences_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<FrameLayout
android:id="#+id/seekBar_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
fragment_audio_sampling_seekbar:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/seekBar_fragment">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#dbdbdd" />
<TextView
android:id="#+id/summary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="15dp"
android:layout_marginTop="14dp"
android:text="Set Audio Sampling Rate"
android:textAppearance="#style/TextAppearance.AppCompat" />
<SeekBar
android:id="#+id/sample_rate_seek_bar"
android:layout_width="303dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignStart="#+id/summary"
android:layout_marginTop="40dp" />
<TextView
android:id="#+id/progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignStart="#+id/summary"
android:layout_marginTop="66dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginTop="96dp"
android:background="#dbdbdd" />
</RelativeLayout>
As far as I was aware, SavedInstanceState only stores a Bundle of data. Only basic things such as Strings, Integers etc. and does not hold anything like Views.
I think you should really use a ViewModel as it can hold data such as custom classes and views and keep them during orientations changes and are only lost once the Activity is destroyed.
Android View Models
You can then code in a more "permanent" storage option say using SharedPreferences, files, sqlite when the Activity is destroyed. The persisted data can then be fetched and populate the ViewModel in the onAttatch method.
Maybe have a read of this to determine your requirements:
Android - Saving UI states.
I'm using HashMap of fragment's backstack. To save backstack and current fragment I use the code below:
public class MainActivity extends AppCompatActivity {
private HashMap<String, Stack<Fragment>> mStacks;
public static final String TAB_PROFILE = "tab_profile";
public static final String TAB_DASHBOARD = "tab_dashboard";
public static final String TAB_CHATS = "tab_chats";
public static final String TAB_SETTINGS = "tab_settings";
private String mCurrentTab;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupViews();
if (savedInstanceState != null) {
mCurrentTab = savedInstanceState.getString("currentTab");
mStacks = (HashMap<String, Stack<Fragment>>) savedInstanceState.getSerializable("stacks");
} else
selectedTab(TAB_DASHBOARD);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable("stacks", mStacks);
outState.putString("currentTab", mCurrentTab);
}
private void setupViews() {
mStacks = new HashMap<>();
mStacks.put(TAB_PROFILE, new Stack<>());
mStacks.put(TAB_DASHBOARD, new Stack<>());
mStacks.put(TAB_CHATS, new Stack<>());
mStacks.put(TAB_SETTINGS, new Stack<>());
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
bottomNavigationView.setSelectedItemId(R.id.action_dashboard);
BottomNavigationViewHelper.removeShiftMode(bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
switch (item.getItemId()) {
case R.id.action_profile:
selectedTab(TAB_PROFILE);
return true;
case R.id.action_dashboard:
selectedTab(TAB_DASHBOARD);
return true;
case R.id.action_chats:
selectedTab(TAB_CHATS);
return true;
case R.id.action_settings:
selectedTab(TAB_SETTINGS);
return true;
}
return true;
});
bottomNavigationView.setOnNavigationItemReselectedListener(item -> {
if (mStacks.get(mCurrentTab).size() != 1) {
mStacks.get(mCurrentTab).clear();
switch (item.getItemId()) {
case R.id.action_profile:
selectedTab(TAB_PROFILE);
break;
case R.id.action_dashboard:
selectedTab(TAB_DASHBOARD);
break;
case R.id.action_chats:
selectedTab(TAB_CHATS);
break;
case R.id.action_settings:
selectedTab(TAB_SETTINGS);
break;
}
}
});
}
private void selectedTab(String tabId) {
mCurrentTab = tabId;
if(mStacks.get(tabId).size() == 0){
if(tabId.equals(TAB_PROFILE)){
Fragment fragment = new ProfileFragment();
Bundle args = new Bundle();
args.putSerializable("user", Globals.getCurrentUser());
fragment.setArguments(args);
pushFragments(tabId, fragment,true);
} else if(tabId.equals(TAB_DASHBOARD)){
pushFragments(tabId, new DashboardFragment(),true);
}else if(tabId.equals(TAB_CHATS)){
pushFragments(tabId, new GroupsFragment(),true);
}else if(tabId.equals(TAB_SETTINGS)){
pushFragments(tabId, new SettingsFragment(),true);
}
}else {
pushFragments(tabId, mStacks.get(tabId).lastElement(),false);
}
}
public void pushFragments(String tag, Fragment fragment, boolean shouldAdd){
if(shouldAdd)
mStacks.get(tag).push(fragment);
FragmentManager manager = getFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}
public void popFragments(){
Fragment fragment = mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);
mStacks.get(mCurrentTab).pop();
FragmentManager manager = getFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}
#Override
public void onBackPressed() {
if(mStacks.get(mCurrentTab).size() == 1){
finish();
return;
}
popFragments();
}
}
Set new fragments using
((MainActivity)context).pushFragments(MainActivity.TAB_CHATS, fragment,true);
Layout
<?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"
android:orientation="vertical"
android:background="#color/background_material_light"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="#id/bottom_navigation"/>
<android.support.design.widget.BottomNavigationView
android:id="#+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
app:itemBackground="#color/waPrimary"
app:itemIconTint="#color/white"
app:itemTextColor="#color/white"
app:menu="#menu/menu_bottom_navigation" />
Everything works fine on screen rotation, but application crashes with exception on application hide.
java.lang.RuntimeException: Parcel: unable to marshal value %FragmentName%{c985244 #2 id=0x7f090051}
As I read, it happens when one of the objects I'm trying to pass is not Parceable, but have no idea how to fix this. Any thoughts?
UPD
After I made all of my fragments Serializable, new exception throws
java.lang.RuntimeException: Parcelable encountered IOException writing serializable object (name = %FragmentName%)
...
Caused by: java.io.NotSerializableException: android.support.v7.widget.RecyclerView
UPD2
Seems like a found a solution - transient property. Now I'm trying to make all non-serializeable objects transient.
UPD3
It helped, but I don't know is it efficient enough.
Here's my suggestion:
Your activity maintains a reference to the four fragments it wants for the bottom navigation toggling.
On toggling bottom navigation, you replace the current fragment in the activity fragment manager.
While on a given fragment, as you interact with the UI, you push things on to the fragment child fragment manager.
This way, each fragment maintains its own backstack automatically, you don't have to save any state, and it all Just Works™.
Some sample code that might help.
public class MainActivity extends AppCompatActivity {
private Fragment mProfileFragment;
private Fragment mDashboardFragment;
private Fragment mChatsFragment;
private Fragment mSettingsFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
// Init fragments
}
else {
// Find last active fragments in fragment manager
}
setupViews();
}
private void setupViews() {
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setSelectedItemId(R.id.action_dashboard);
BottomNavigationViewHelper.removeShiftMode(bottomNavigationView);
bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
Fragment fragment;
switch (item.getItemId()) {
case R.id.action_profile:
fragment = mProfileFragment;
break;
case R.id.action_dashboard:
fragment = mDashboardFragment;
break;
case R.id.action_chats:
fragment = mChatsFragment;
break;
case R.id.action_settings:
fragment = mSettingsFragment;
break;
}
// Replace the currently active fragment which will be
// managing its own backstack
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.frament_container, fragment)
.commit();
});
}
}
And one of your fragments would push stuff on its own stack like this:
public class ProfileFragment extends Fragment {
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.id.fragment_layout, container, false);
Button button = view.findViewById(R.id.some_button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Fragment someFragmentToPush = new SomeFragmentToPush();
// Use the child fragment manager to keep UI
// local to this fragment instance, adding to backstack
// for automatic popping on pressing back
getChildFragmentManager().beginTransaction()
.add(R.id.fragment_layout, someFragmentToPush)
.addToBackStack(null)
.commit();
}
});
return view;
}
}
Hope that helps!
Okay. I am stuck and having a headache... I am not sure how to access the other layout's view, since inflating does not work.
Here are my codes.
WriteRouteActivity.java
public class WriteRouteActivity extends AppCompatActivity {
private Toolbar tb;
private TextView txt_toolbar_title;
private Button btnSearchPlaces;
private LinearLayout parentLayout, placesCoverLayout;
private View popupView;
private ImageView imgShowPlaces;
private boolean isKeyBoardVisible;
private int keyboardHeight;
private EditText edtSearchPlaces;
private PopupWindow popupWindow;
//popupView
private TabLayout tabLayout;
private FrameLayout frameLayout;
//prework
private int minusVal;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_route);
initView();
}
private void initView() {
//for activity and native back button
tb = (Toolbar) findViewById(R.id.nav_toolbar);
setSupportActionBar(tb);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
txt_toolbar_title = (TextView) findViewById(R.id.txt_toolbar);
parentLayout = (LinearLayout) findViewById(R.id.layout_parent);
placesCoverLayout = (LinearLayout) findViewById(R.id.footer_for_places);
imgShowPlaces = (ImageView) findViewById(R.id.img_show_places);
edtSearchPlaces =(EditText) findViewById(R.id.edt_search_place);
btnSearchPlaces = (Button) findViewById(R.id.btn_search_place);
popupView = getLayoutInflater().inflate(R.layout.places_popup, null);
tabLayout = (TabLayout) popupView.findViewById(R.id.tab_layout);
frameLayout = (FrameLayout) popupView.findViewById(R.id.frame_layout);
doWorkForLayotus();
}
private void doWorkForLayotus(){
final float popUpheight = getResources().getDimension(R.dimen.keyboard_height);
changeKeyboardHeight((int) popUpheight);
enablePopUpView();
setTabLayout();
checkKeyboardHeight(parentLayout);
enableFooterView();
}
public void setCurrentTabFragment(int position) throws IllegalAccessException, InstantiationException {
String tag="";
Fragment fr = null;
Class frClass = null;
FragmentManager frManager = getSupportFragmentManager();
switch (position) {
case 0:
tag = "first";
//hide
if(frManager.findFragmentByTag("second")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("second")).commit();
}
if(frManager.findFragmentByTag("third")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("third")).commit();
}
if(frManager.findFragmentByTag("fourth")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("fourth")).commit();
}
//show
if(frManager.findFragmentByTag("first")!=null){
frManager.beginTransaction().show(frManager.findFragmentByTag("first")).commit();
}else{ //add
try {
frManager.beginTransaction().add(frameLayout.getId(), ((Fragment) Fragment_zasin.class.newInstance()), tag).commit();
}catch(Exception e){
Log.e("why", e.getMessage().toString());
}
}
break;
case 1:
tag = "second";
//hide
if(frManager.findFragmentByTag("first")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("first")).commit();
}
if(frManager.findFragmentByTag("third")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("third")).commit();
}
if(frManager.findFragmentByTag("fourth")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("fourth")).commit();
}
//show
if(frManager.findFragmentByTag("second")!=null){
frManager.beginTransaction().show(frManager.findFragmentByTag("second")).commit();
}else{ //add
frManager.beginTransaction().add(frameLayout.getId(), ((Fragment) Fragment_zasin.class.newInstance()), tag).commit();
}
break;
case 2:
tag = "third";
//hide
if(frManager.findFragmentByTag("first")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("first")).commit();
}
if(frManager.findFragmentByTag("second")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("second")).commit();
}
if(frManager.findFragmentByTag("fourth")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("fourth")).commit();
}
//show
if(frManager.findFragmentByTag("third")!=null){
frManager.beginTransaction().show(frManager.findFragmentByTag("third")).commit();
}else{ //add
frManager.beginTransaction().add(frameLayout.getId(), ((Fragment) Fragment_zasin.class.newInstance()), tag).commit();
}
break;
case 3:
tag = "fourth";
//hide
if(frManager.findFragmentByTag("first")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("first")).commit();
}
if(frManager.findFragmentByTag("second")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("second")).commit();
}
if(frManager.findFramentByTag("third")!=null){
frManager.beginTransaction().hide(frManager.findFragmentByTag("third")).commit();
}
//show
if(frManager.findFragmentByTag("fourth")!=null){
frManager.beginTransaction().show(frManager.findFragmentByTag("fourth")).commit();
}else{ //add
frManager.beginTransaction().add(R.id.frame_layout, ((Fragment) Fragment_zasin.class.newInstance()), tag).commit();
}
break;
}
//frManager.beginTransaction().replace(R.id.frame_container, fr, tag).setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
}
private void setTabLayout(){
tabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
#Override
public void onTabSelected(TabLayout.Tab tab) {
try {
setCurrentTabFragment(tab.getPosition());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
#Override
public void onTabUnselected(TabLayout.Tab tab) {
}
#Override
public void onTabReselected(TabLayout.Tab tab) {
}
});
}
private void enablePopUpView() {
// Creating a pop window for emoticons keyboard
popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT,
(int) keyboardHeight, false);
popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {
#Override
public void onDismiss() {
placesCoverLayout.setVisibility(LinearLayout.GONE);
}
});
}
int previousHeightDiffrence = 0;
private void checkKeyboardHeight(final View parentLayout) {
parentLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
parentLayout.getWindowVisibleDisplayFrame(r);
int screenHeight = parentLayout.getRootView()
.getHeight();
minusVal=screenHeight-r.bottom;
int heightDifference = screenHeight - (r.bottom+(minusVal));
if (previousHeightDiffrence - heightDifference > 50) {
popupWindow.dismiss();
}
previousHeightDiffrence = heightDifference;
if (heightDifference > 100) {
isKeyBoardVisible = true;
changeKeyboardHeight(heightDifference);
} else {
isKeyBoardVisible = false;
}
}
});
}
private void changeKeyboardHeight(int height) {
if (height > 100) {
keyboardHeight = height;
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, keyboardHeight);
placesCoverLayout.setLayoutParams(params);
}
}
private void enableFooterView() {
edtSearchPlaces.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (popupWindow.isShowing()) {
popupWindow.dismiss();
}
}
});
btnSearchPlaces.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
hideSoftKeyboard(WriteRouteActivity.this);
if(!popupWindow.isShowing()){
popupWindow.setHeight((int) (keyboardHeight));
if (isKeyBoardVisible) {
placesCoverLayout.setVisibility(LinearLayout.GONE);
} else {
placesCoverLayout.setVisibility(LinearLayout.VISIBLE);
}
popupWindow.setSoftInputMode(PopupWindow.INPUT_METHOD_NEEDED);
popupWindow.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
popupWindow.showAtLocation(parentLayout, Gravity.BOTTOM, 0, 0);
try {
setCurrentTabFragment(0);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
} else {
//popupWindow.dismiss();
}
}
});
}
#Override
protected void onDestroy() {
popupWindow.dismiss();
super.onDestroy();
}
public static void hideSoftKeyboard(Activity activity) {
InputMethodManager inputMethodManager =
(InputMethodManager) activity.getSystemService(
Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(
activity.getCurrentFocus().getWindowToken(), 0);
}
#Override
public void onBackPressed() {
if(popupWindow.isShowing()){
popupWindow.dismiss();
}else {
super.onBackPressed();
}
}
}
activity_write_wroute.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include layout="#layout/nav_toolbar" />
<fragment
android:id="#+id/google_map"
class="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<include
android:id="#+id/footer_layout"
layout="#layout/footer_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="#+id/footer_for_places"
android:layout_width="match_parent"
android:layout_height="#dimen/keyboard_height"
android:background="#android:color/transparent"
android:orientation="vertical"
android:visibility="gone" />
</LinearLayout>
Fragment_Zasin
public class Fragment_zasin extends Fragment {
public Fragment_zasin newInstance() {
Fragment_zasin fr = new Fragment_zasin();
return fr;
}
public Fragment_zasin() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_zasin, container, false);
return rootView;
}
}
places_popup.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/linear_layout_top"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="#+id/tab_layout"
android:layout_width="fill_parent"
android:layout_height="?attr/actionBarSize"
android:background="#ffffff"
app:tabGravity="fill"
app:tabIndicatorColor="#color/colorPrimary"
app:tabIndicatorHeight="4dp"
app:tabMode="fixed"
app:tabSelectedTextColor="#color/colorPrimary">
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/tab_pin_selector"
android:text="11">
</android.support.design.widget.TabItem>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/tab_mainroute_selector"
android:text="22" />
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/tab_talk_selector"
android:text="33" />
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:icon="#drawable/tab_my_selector"
android:text="44" />
</android.support.design.widget.TabLayout>
<FrameLayout
android:id="#+id/frame_layout"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1" />
</LinearLayout>
LOGCAT Message
FATAL EXCEPTION: main Process: suacuration.itgotravel, PID: 20131
java.lang.IllegalArgumentException: No view found for id 0x7f100173 for fragment Fragment_zasin{d4ac39c #0 id=0x7f100173 first}
Can somebody help this?
This error occurs because the fragment manager could not find the view on which it has to inflate the fragment.
The fragment transaction is linked to the activity so this error occurs as the frame layout is not a part of that main activity's xml.so it cant find where to add the fragment.fragment has to be added inside the activity.
what you have to do is provide the id of a view in your main activity.
for e.g your main view
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/mainView"//like this
android:orientation="vertical">
<include layout="#layout/nav_toolbar" />
<fragment
android:id="#+id/google_map"
class="com.google.android.gms.maps.MapFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<include
android:id="#+id/footer_layout"
layout="#layout/footer_edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:id="#+id/footer_for_places"
android:layout_width="match_parent"
android:layout_height="#dimen/keyboard_height"
android:background="#android:color/transparent"
android:orientation="vertical"
android:visibility="gone" />
</LinearLayout>
The view where you are trying to inflate the fragemnt must be inside activity.
Now when you try
frManager.beginTransaction().add(R.id.mainView, ((Fragment) Fragment_zasin.class.newInstance()), tag).commit();
the fragment will be loaded on this view of your activity.
Problem is you are trying to show a Fragment on a View which is not defined in your layout which you have defined in your onCreate() while setContentView(layoutId)
In your case you are inflating fragments in WriteRouteActivity where layout defined is activity_write_route and fragments are added on FrameLayout which is defined in places_popup.xml so define your framelayout in a view layout of Activity.
Small Description :
--------code------
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
-------layout define for activity-------
setContentView(R.layout.activity_write_route);
}
now where you are adding fragment on a FrameLayout(view)
frManager.beginTransaction().add(R.id.frame_layout, ((Fragment) Fragment_zasin.class.newInstance()), tag).commit();
here R.id.frame_layout should be define in your layout activity_write_route.
Okay i found an answer.
The problem was that fragment cannot be a child of a 'Dialog'.
Since i used popup dialog, it was unable to put fragments inside the dialog.
i solved by inflating views rather than using fragments in tablayout.
There could be other errors but what is this?
tb = (Toolbar) findViewById(R.id.nav_toolbar);
This is not a toolbar:
<include layout="#layout/nav_toolbar" />
Can we see inside this nav_toolbar layout?
Also, where is this: "R.layout.fragment_zasin". It's complaining about a Fragment so it would be worthwhile seeing if this is ok.
Anyway, as a general troubleshooting strategy, try commenting out all those lines in the initView() method and just add them in one at a time until it fails. Or maybe if you scroll down in the error logs further it will give you a hyperlink to the line that's causing it to fail.
Why do you have "frameLayout.getId()" on one line but "R.id.frame_layout" on another? Try to use the latter for all of the lines and see if that's it.
I was extending the wrong type of activity, extended AppCompact solved issue.
Got this error when starting new fragment from fragment, using ChildFragmentManager, and giving id of container from activity, in transaction replace method.
It happens that FragmentManager has access to activity containers, but ChildFragmentManager has access to fragment containers.
The solution was to use FragmentManager class instead of ChildFragmentManager.
You got this error because your view found for id 0x7f100173 into fragment Fragment_zasin so only solution is that:
1) check all id present to that particular layout.
2) if all id is present to that particular layout and still you facing same issue do one thing change name of id don't refactor change name and access this changed id name in your fragment or activity.
My fragment has a textview whose text contains links and each time the link is clicked I do a fragment transaction to add a new instance of the same fragment.
To explain further I have ActivityA which hosts fragmentA.
FragmentA fetches text from the web. The text contains links. When a link in fragmentA is clicked, a callback to ActivityA starts a new Instance of FragmentA
which fetches another text once more.
These are my codes:
ActivityA
public class ActivityA extends AppCompatActivity implements FragmentA.OnLinkClickedListener {
private final String TAG = "ActivityA";
Fragment mContent;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_rock);
String rock_id = getIntent().getStringExtra("rockId");
Log.d(TAG, rock_id);
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setDisplayHomeAsUpEnabled(true);
}
if (savedInstanceState == null) {
Bundle bundle = new Bundle();
Fragment fragment;
fragment = new FragmentA();
bundle.putString("rock_link", rock_id);
fragment.setArguments(bundle);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.anim.slide_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right, android.R.anim.slide_in_left);
ft.add(R.id.rock_frame, fragment);
ft.commit();
}
if (savedInstanceState != null) {
mContent = getSupportFragmentManager().getFragment(savedInstanceState, "mContent");
}
}
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (mContent != null) {
getSupportFragmentManager().putFragment(outState, "mContent", mContent);
}
}
#Override
public void OnLinkClicked(String link) {
Log.d(TAG, "The activity got the url and it is " + link);
Bundle bundle = new Bundle();
Fragment fragment;
fragment = new FragmentA();
bundle.putString("rock_link", link);
fragment.setArguments(bundle);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
//ft.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right, android.R.anim.slide_in_left, android.R.anim.slide_out_right);
ft.addToBackStack(null);
//ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.add(R.id.rock_frame, fragment);
ft.commit();
}
}
FragmentA
public class FragmentA extends Fragment {
OnLinkClickedListener mLinkClickedListener = sLinkCallbacks;
private AlertDialog internetDialog;
private AlertDialog sthWrongAlert;
private String url;
private String rockData;
private final String TAG = "FragmentA";
protected com.nostra13.universalimageloader.core.ImageLoader mImageLoader;
TextView rockContent;
private String rock_content;
public FragmentA() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView called");
url = getArguments().getString("rock_link");
Log.d(TAG, "Rock url is " + url);
view = inflater.inflate(R.layout.fragment_details, container, false);
showDialog();
sthWrongDialog();
rockContent = (TextView) view.findViewById(R.id.drock_content);
DisplayImageOptions defaultoptions = new DisplayImageOptions.Builder()
.cacheInMemory(true)
.cacheOnDisk(true)
.build();
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getActivity())
.defaultDisplayImageOptions(defaultoptions)
.writeDebugLogs()
.build();
mImageLoader = com.nostra13.universalimageloader.core.ImageLoader.getInstance();
mImageLoader.init(config);
if (savedInstanceState != null) {
rockData = savedInstanceState.getString("rockData");
if (rockData != null) {
parseHtml(rockData);
} else if (NetworkCheck.isAvailableAndConnected(getActivity())){
loadRock();
}
} else {
if (NetworkCheck.isAvailableAndConnected(getActivity())) {
//Calling method to load rocks
loadRock();
} else {
internetDialog.show();
}
}
return view;
}
public interface OnLinkClickedListener {
public void OnLinkClicked (String link);
}
private static OnLinkClickedListener sLinkCallbacks = new OnLinkClickedListener() {
#Override
public void OnLinkClicked(String link) {
}
};
private void showDialog() {
internetDialog = new AlertDialog.Builder(getActivity())
...
}
private void sthWrongDialog() {
sthWrongAlert = new AlertDialog.Builder(getActivity())
...
}
private void loadRock() {
Log.d(TAG, "loadRock called");
final ProgressBar progressBar;
progressBar = (ProgressBar) view.findViewById(R.id.progress_circle);
progressBar.setVisibility(View.VISIBLE);
Log.d(TAG, "You clicked rock id " + url);
StringRequest stringRequest = new StringRequest(url,
new Response.Listener<String>() {
// The usual codes for volley string request
//parseHtml() is called in onResponse
});
//Creating requestqueue
RequestQueue requestQueue = Volley.newRequestQueue(getActivity());
//Adding request queue
requestQueue.add(stringRequest);
}
private void parseHtml(String response) {
Log.d(TAG, "parsinghtml");
Document document = Jsoup.parse(response);
rock_content = document.select("div.entry-content").first().html();
rockContent.setVisibility(View.VISIBLE);
}
protected void makeLinkClickable(SpannableStringBuilder strBuilder, final URLSpan span)
{
int start = strBuilder.getSpanStart(span);
int end = strBuilder.getSpanEnd(span);
int flags = strBuilder.getSpanFlags(span);
ClickableSpan clickable = new ClickableSpan() {
public void onClick(View view) {
// Do something with span.getURL() to handle the link click...
Log.e(TAG, "on click" + span.getURL());
String clickedLink = span.getURL();
mLinkClickedListener.OnLinkClicked(clickedLink);
}
};
strBuilder.setSpan(clickable, start, end, flags);
strBuilder.removeSpan(span);
}
protected void setTextViewHTML(TextView text, String html) {
CharSequence sequence = Html.fromHtml(html, new UILImageGetter(rockContent, getActivity()), null);
SpannableStringBuilder strBuilder = new SpannableStringBuilder(sequence);
URLSpan[] urls = strBuilder.getSpans(0, sequence.length(), URLSpan.class);
for(URLSpan span : urls) {
makeLinkClickable(strBuilder, span);
}
text.setText(strBuilder);
text.setMovementMethod(LinkMovementMethod.getInstance());
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mLinkClickedListener = (OnLinkClickedListener) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnLinkClickedListener");
}
}
}
Layout for FragmentA
<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"
android:background="#android:color/white">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="5dp"
tools:context=".ActivityA">
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/progress_circle"
android:visibility="gone"
android:layout_centerInParent="true"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/dviewbottom"
android:hint="#string/main_body"
android:textColorLink="#color/textlink"
android:visibility="gone"
android:id="#+id/drock_content"
android:layout_margin="5dp"/>
</RelativeLayout>
</FrameLayout>
Layout for ActivityA
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scrollView"
android:fillViewport="true">
<FrameLayout
android:id="#+id/rock_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</ScrollView>
Like I wrote above, when I click on the link in rockContent inside FragmentA, ActivityA launches a new instance of FragmentA.
The problem is that this new Instance of FragmentA still has the height of the former Instance. And after the data has been loaded, there's is either an empty space at the bottom or the text is truncated depending on the size of the former instance.
To further explain, before the data is loaded into the new fragment, I can scroll up and down since the height is still that of the old instance of the fragment.
Please how do I correct this? i.e. to make each new instance of the fragment to have the height of the phone screen as I have set in the layout?
EDIT
If I set android:layout_width="wrap_content" in the FrameLayout of FragmentA layout, the problem is solved i.e. no extra space or truncating. The problem with this is that the Progressbar is not centered on the screen anymore. It's positioned just below the ActionBar.
I am using fragments,I have an edittext in fragment and I want to get value in main activity.
This is my fragment layout
<?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:orientation="vertical"
android:background="#878787" >
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="dfgdfgdf"
android:textSize="20dp"
android:layout_centerInParent="true"
android:id="#+id/user_name"/>
<EditText
android:id="#+id/message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
/>
<Button
android:text="Gönder"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:onClick="getFromUser"
android:layout_marginTop="40dp"
/>
</RelativeLayout>
I am loading fragment with this function:
public void startChat(JsonObject user) {
FrameLayout layout = (FrameLayout)findViewById(R.id.container);
layout.setVisibility(View.VISIBLE);
Bundle bundle = new Bundle();
bundle.putString("name", user.get("name").getAsString());
sendTo=user.get("username").getAsString();
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
ConversationFragment conv = new ConversationFragment();
conv.setArguments(bundle);
fragmentTransaction.add(R.id.container, conv);
fragmentTransaction.commit();
viewPager.setVisibility(View.GONE);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
actionBar.setDisplayHomeAsUpEnabled(true);
}
And this is my fragment class
public class ConversationFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String name = getArguments().getString("name");
View rootView = inflater.inflate(R.layout.fragment_conversation, container, false);
TextView username=(TextView)rootView.findViewById(R.id.user_name);
username.setText(name);
return rootView;
}
}
As you can see when press the button main activity runs "getFromUser" function.I want to get edittext value in this function.How can I do this ?
It's always the same procedure for these things. You can't access a fragment's views just like that. You need a callback method.
Add this code to ConversationFragment:
private OnGetFromUserClickListener mListener;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
mListener = (OnGetFromUserClickListener ) activity;
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement OnGetFromUserClickListener");
}
}
public void getFromUser(View v) {
if (mListener != null) {
EditText edit = (EditText)findViewById(R.id.message);
mListener.getFromUser(edit.getText().toString());
}
}
public interface OnGetFromUserClickListener {
void getFromUser(String message);
}
Make your MainActivity implement this interface. Replace getFromUser() inside MainActivity with:
public void getFromUser(String message) {
sendMessage(message);
}
Done.
Edit:
Actually, using the XML-onClick attribute is currently bugged (see onClick inside fragment called on Activity): It links to the activity instead of the fragment. You have to set the click listener programmatically to make sure the code won't break at some point in the future. So give the button an ID inside the XML (e.g. get_from_user) and add this code to onCreateView inside ConversationFragment:
v.findViewById(R.id.get_from_user).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (v.getId() == R.id.get_from_user) {
getFromUser(v);
}
}
});
Using this code vastly decouples the activity and the fragment from each other.
I resolved this problem.
public void getFromUser(View view) {
ConversationFragment fragment1 = (ConversationFragment)getSupportFragmentManager().findFragmentById(R.id.container);
View frag=fragment1.getView();
EditText editText1 =(EditText) frag.findViewById(R.id.message);
String message=editText1.getText().toString();
sendMessage(message);
}
Now I can get edittext value from fragment.