Replacing a Fragment: Previous view remains Active in the background? - android

I am working on an application where I have to change the view of one tab when tab is changed. I am doing it following way using OnTabChangedListener. I am able to replace view. But when I change my tab to some other tab, previous view remain tapable in the background? Why is it so? In front, new view is populated but If I tap anywhere on the screen where previous view's controls were, the functionality is executed. I am not getting where may the problem lie. Please help me out. Thanks!
public class FragmentTabs extends FragmentActivity implements OnTabChangeListener {
private FragmentTabHost mTabHost;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.fragment_tabs);
mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
mTabHost.setOnTabChangedListener(this);
if (Database.getSharedObject(getApplicationContext()).getAppSettings().getListView() == 1) {
mTabHost.addTab(mTabHost.newTabSpec("home").setIndicator("", getResources().getDrawable(R.drawable.home_tab)), HomeFragment.class, null);
} else {
mTabHost.addTab(mTabHost.newTabSpec("home").setIndicator("", getResources().getDrawable(R.drawable.home_tab)), CalendarViewFragment.class, null);
}
mTabHost.addTab(mTabHost.newTabSpec("groups").setIndicator("", getResources().getDrawable(R.drawable.groups_tab)), GroupFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("templates").setIndicator("", getResources().getDrawable(R.drawable.templates_tab)), TemplateFragment.class, null);
mTabHost.addTab(mTabHost.newTabSpec("settings").setIndicator("", getResource
mTabHost.getTabWidget().getChildAt(0).getLayoutParams().height = height;
mTabHost.getTabWidget().getChildAt(1).getLayoutParams().height = height;
mTabHost.getTabWidget().getChildAt(2).getLayoutParams().height = height;
mTabHost.getTabWidget().getChildAt(3).getLayoutParams().height = height;
// Database.getSharedObject(getApplicationContext());
mTabHost.setCurrentTab(0);
}
#Override
protected void onResume() {
super.onResume();
}
#Override
public void onTabChanged(String arg0) {
mTabHost.getCurrentTabView().invalidate();
if (mTabHost.getCurrentTab() == 0) {
if (Database.getSharedObject(getApplicationContext()).getAppSettings().getListView() == 1) {
HomeFragment homeFragment = new HomeFragment();
mTabHost.getCurrentTabView().invalidate();
getSupportFragmentManager().beginTransaction().replace(R.id.realtabcontent, homeFragment).addToBackStack(null).commit();
} else {
CalendarViewFragment calendarViewFragment = new CalendarViewFragment();
mTabHost.getCurrentTabView().invalidate();
getSupportFragmentManager().beginTransaction().replace(R.id.realtabcontent, calendarViewFragment).addToBackStack(null).commit();
}
}
}
}

Add android:clickable="true" to your new tab's root layout. It will prevent click events to be passed through the layouts behind.

Related

Android 8 nullpointer exception with function call in fragment (only on certain devices!!!)

I currently see a very strange nullpointer-exception that I can not explain myself.
I have an Activity which hosts two fragments.
The context-menu is evaluated in the host-activity and then calls a public function in one of the fragments.
While this works well for several phones with android 7 I usually get a nullpointer-exception on my Samsung S7 with Android 8.
The nullpointer-exception occurs when I try to access any UI-elements of the fragment within this function call!
I already checked that the fragment instances are valid, and they are ok. They are fully initialized and added on the onCreate of the host.
Whenever I trigger the function from inside the fragment it is ok, but not if I call the same function from the context menu of the hosting activity!
At first it looks like a timing problem, because sometimes it works, although relativ seldom.
What is the reason of this behaviour and how can I get over this strange error?
Thanks
Andreas
public class EpaperFragmentHost extends AppCompatActivity
{
private CustomViewPager mViewPager;
private Toolbar toolbar;
private EpaperPicture_Fragment EpaperPictureFrag;
private int Picture_Fragment_Position = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setRequestedOrientation(
ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.activity_connectfragmenthost);
toolbar = findViewById(R.id.toolbar);
toolbar.setTitle( String.format( Locale.GERMAN,
getString(R.string.Connectingto_STRING) , mDeviceName) );
setSupportActionBar(toolbar);
mSectionsPagerAdapter = new Connection_fragment_adapter(
getSupportFragmentManager() );
EpaperPictureFrag = EpaperPicture_Fragment.newInstance( );
mSectionsPagerAdapter.setFragment( Picture_Fragment_Position,
EpaperPictureFrag );
mViewPager = findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.epapermenu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch(id){
case R.id.menuitem_savepicture:
// This call will fail on some phones, but works on others!
// And not because EpaperPictureFrag would be null, but the
// myDrawView inside this instance is null!
EpaperPictureFrag.takeScreenshot( true);
break;
default:
break;
}
boolean result = super.onOptionsItemSelected(item);
return result;
}
}
and this is the fragment:
public class EpaperPicture_Fragment extends Fragment
{
public DrawView myDrawView;
public EpaperPicture_Fragment() {
// Required empty public constructor
}
public static EpaperPicture_Fragment newInstance( ) {
EpaperPicture_Fragment fragment = new EpaperPicture_Fragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_epaper_picture, container,
false);
btn_Clear = v.findViewById(R.id.clear_btn);
btn_addText = v.findViewById(R.id.addtext_btn);
btn_Transmit = v.findViewById(R.id.transmit_btn);
myDrawView = v.findViewById(R.id.epaper);
btn_Clear.setOnClickListener( this );
btn_addText.setOnClickListener(this);
btn_Transmit.setOnClickListener( this );
return v;
}
#Override
public void onClick(View v) {
switch( v.getId() ){
case R.id.transmit_btn:
// This call works!
takeScreenshot( true );
break;
default:
break;
}
}
public File takeScreenshot(boolean showToast) {
// THIS IS THE PROBLEMATIC SECTION!
// Why can myDrawView be null, if the fragment exists?
myDrawView.setDrawingCacheEnabled(true);
Bitmap cachedBitmap = myDrawView.getDrawingCache();
Bitmap copyBitmap = cachedBitmap.copy(Bitmap.Config.RGB_565, true);
myDrawView.destroyDrawingCache();
// ...
}
}
Ok, the solution is:
I changed the check for storage access in the base-class to a new mode.
And this check caused each activity which was inherited by the baseclass to re-create after checking the access rights!
I found it by generating all overwrite methods of the base class and made a log-output in each. It was a terrible destroy and create-sequence.
Unfortunately I changed my test-phone around the same time...

Android: NullPointerException when communicating between fragments

Android newbie here. My app crashes when trying to send text from one fragment to another. It says:
java.lang.NullPointerException
at com.example.hang.camera.Camera.sendResults(Camera.java:57)
at com.example.hang.camera.PageOneFragment.sendMethod(PageOneFragment.java:320)
at com.example.hang.camera.PageOneFragment$1.onClick(PageOneFragment.java:104)
Here is my code:
// Activity that implements the interface from fragment A
Camera.java:
public class Camera extends ActionBarActivity implements PageOneFragment.OnComputeResultListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabHost.setup();
mViewPager = (CustomViewPager)findViewById(R.id.pager);
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
mTabsAdapter.addTab(mTabHost.newTabSpec("one").setIndicator("One"), PageOneFragment.class, null);
mTabsAdapter.addTab(mTabHost.newTabSpec("two").setIndicator("Two"), PageTwoFragment.class, null);
if (savedInstanceState != null)
{
mTabHost.setCurrentTabByTag(savedInstanceState.getString("tab"));
}
mViewPager.setPagingEnabled(false);
}
#Override
public void sendResults(String text){
PageTwoFragment fragmentTwo = (PageTwoFragment)getSupportFragmentManager().findFragmentById(R.id.second_fragment);
fragmentTwo.updateText(text);
}
// fragment A:
PageOneFragment.java
OnComputeResultListener mCallback;
public interface OnComputeResultListener{
public void sendResults(String text);
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try{
mCallback = (OnComputeResultListener) activity;
} catch(ClassCastException e){
throw new ClassCastException(activity.toString()
+ " must implement OnComputeResultListener");
}
}
public void sendMethod(){
mCallback.sendResults("the results");
}
// in onCreateView, I have this onclicklistener, and I call "sendMethod" here
lengthButton.setOnClickListener(
new Button.OnClickListener(){
public void onClick(View v){
double length ;
line_x = imageview.x_getter();
line_y = imageview.y_getter();
length = Math.sqrt(Math.pow((line_x[0]-line_x[1]),2) + Math.pow((line_y[0]-line_y[1]),2));
line_length.setText("Length of the line is: " + String.format("%.2f",length) + "dp");
sendMethod();
}
}
);
// fragment B
PageTwoFragment.java
private TextView resultText = null;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.pagetwo_fragment, container, false);
resultText = (TextView) view.findViewById(R.id.resultText);
return view;
}
public void updateText(String text){
resultText.setText(text);
}
I finally solved the problem. In my camera.java where I implemented "sendResults" method of the interface, I changed the parameter of "findFragmentById" to the id of the viewPager instead of the id of the fragment's xml file.
However, from Android developer doc, for findFragmentById, it does say:
Finds a fragment that was identified by the given id either when inflated from XML or as the container ID when added in a transaction.
So, I don't know why the xml id didn't work.
You can save your Fragment as an object in your Activity class.
In the activity class before doing the fragment transaction, do the following:
PageTwoFragment fragmentTwo = new PageTwoFragment();
// do the transaction:
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.container, fragmentTwo);
ft.commit();
then later in code, just call
fragmentTwo.updateText(...);

Android: ListView annoyingly scrolling to top when changing between tabs and back

I have a ListView inside of a Fragment attached to a CursorAdapter. The Fragment has setRetainInstance(true). Under the Fragment's onCreate() method, I instantiate the CursorAdapter (storing it in variable adapter). I then call listView.setAdapter(adapter) under my Fragment's onCreateView method. The Cursor in the CursorAdapter is loaded by a CursorLoader. Inside my LoaderCallbacks' onLoadFinished(), I call adapter.swapCursor(cursor).
In sum: Everything seems to be in place such that the ListView should not scroll to top when changing between tabs and back. But it still does! Could I be missing something?
Here's some code.
Fragment
public class Home extends Fragment{
...
private HomeFeedAdapter adapter; // HomeFeedAdapter extends CursorAdapter
private AutoQuery autoQuery; // AutoQuery extends LoaderCallbacks<Cursor>
... // (See inner class, at the end)
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
if(adapter == null)
adapter = new HomeFeedAdapter(getActivity(), null);
if(autoQuery == null)
autoQuery = new AutoQuery();
getLoaderManager().initLoader(LOADER_INITIAL, null, autoQuery);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Layout
View v = inflater.inflate(R.layout.home, container, false);
l = (ListView) v.findViewById(R.id.listview);
l.setAdapter(adapter);
return v;
}
private class AutoQuery implements LoaderCallbacks<Cursor>{
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
...
return new CursorLoader(getActivity(), uri,
null, null, null, null);
}
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
}
#Override
public void onLoaderReset(Loader<Cursor> loader) {
adapter.swapCursor(null);
}
}
}
Activity
public class MainActivity extends SherlockFragmentActivity {
...
private class TabsListener implements ActionBar.TabListener {
private Fragment fragment;
private String tag;
public TabsListener(Fragment fragment, String tag) {
this.fragment = fragment;
this.tag = tag;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
// Do nothing
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
ft.replace(R.id.fragment_container, fragment, tag);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.remove(fragment);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Layout
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(false);
getSupportActionBar().setDisplayShowTitleEnabled(false);
setContentView(R.layout.activity_main);
// Loads fragment
Fragment fHome, fActivity, fProfile;
if((fHome = getSupportFragmentManager().findFragmentByTag(HOME)) == null) fHome = new Home();
if((fActivity = getSupportFragmentManager().findFragmentByTag(ACTIVITY)) == null) fActivity = new FriendsList();
if((fProfile = getSupportFragmentManager().findFragmentByTag(PROFILE)) == null) fProfile = new Profile();
ActionBar.Tab tab;
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fHome,
HOME
));
getSupportActionBar().addTab(tab, false);
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fActivity,
ACTIVITY
));
getSupportActionBar().addTab(tab, false);
tab = getSupportActionBar().newTab();
tab.setTabListener(new TabsListener(
fProfile,
PROFILE
));
getSupportActionBar().addTab(tab, false);
...
}
}
With Greg Giacovelli's help in leading me in the right direction, I've found a solution to my problem.
I'll begin with a disclaimer that I don't quite understand how ListView positions are saved. My ListView instance is recreated every time that my Fragment's onCreateView() is called. This happens when the screen rotates, for example. And yet, in the specific case of screen rotations, even though onCreateView() is called and thus the ListView is reinstantiated, the ListView's state is nonetheless restored. So if the ListView is being recreated, something else must be telling it what its position previously was... and I don't know what that is. I think that it's attributable to the machinery of setRetainInstance(true).
But let's look at my main issue: Why did the ListView scroll to top between tab changes? As Greg suggested, it was because I was re-adding the Fragment with replace(), and thus destroying my Fragment and re-creating every time the user flipped to another tab and back.
My solution was to check if the tab was already added; if so, then not add it; else, add it. Then, when the user clicks out of a tab, I simply detach the fragment (not remove it), and attach a new one. This way the unselected tab's Fragment is still alive, though detached.
// Tabs listener
private class TabsListener implements ActionBar.TabListener {
private Fragment fragment;
private String tag;
public TabsListener(Fragment fragment, String tag) {
this.fragment = fragment;
this.tag = tag;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
if(fragment instanceof ScrollableToTop) ((ScrollableToTop) fragment).scrollToTop();
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if(!fragment.isAdded()){
ft.add(R.id.fragment_container, fragment, tag);
}
ft.attach(fragment);
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
ft.detach(fragment);
}
}

Android Fixed Tabs on a Processing PApplet

I am developing an Android app based on Processing
I would like to show two Fixed Tabs with some content when Menu Button is pressed.
Tab1 would contain some settings and maybe a Help Button
Tab2 should have a ListView to show a list of presets.
Now I am trying to create two TextViews for sake of simplicity.
I am trying with the approach of the Ketai library for KetaiList
So there's an inner class that extends TabHost inside the PApplet class extended by my application:
public class MyProcessingApp extends PApplet {
public void setup() {
}
public void draw() {
}
public void keyPressed() {
if (key == CODED) {
if (keyCode == KeyEvent.KEYCODE_MENU) {
TabHost th = new GBTab(this);
}
}
}
public class GBTab extends TabHost {
private PApplet parent;
TabHost self;
TabWidget tab1, tab2;
LinearLayout layout;
public GBTab(PApplet _parent) {
super(_parent.getApplicationContext());
parent = _parent;
init();
}
public void init() {
println("GBTab init");
self = this;
layout = new LinearLayout(parent);
TabSpec settingsSpec = self.newTabSpec("SETTINGS").setContent(
new TabContentFactory() {
public View createTabContent(String tag) {
TextView tv = new TextView(parent);
tv.setText("SETTINGS!");
return tv;
}
}
)
.setIndicator("SETTINGS");
self.addTab(settingsSpec);
TabSpec presetsSpec = self.newTabSpec("PRESETS").setContent(
new TabContentFactory() {
public View createTabContent(String tag) {
TextView tv = new TextView(parent);
tv.setText("PRESETS!");
return tv;
}
}
)
.setIndicator("PRESETS");
self.addTab(presetsSpec);
self.setCurrentTab(0);
parent.runOnUiThread(new Runnable() {
public void run() {
parent.addContentView(self, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT));
}
}
);
}
}
}
This code gives a NullPointerException when adding a tab to the TabHost.
self.addTab(settingsSpec);
since self is null.
Is this a valid approach?
Thank you
How about the second answer (24 votes) in this? Essentially it says that since you are not using a tabactivity you need to call self.setup(); before adding any tabs

ActionBar + Tab Navigation(MapView + ListView) fragments

I'm wondering how can I work with a listfragment, and at the same time, using an static linearlayout with 4 filter buttons at the top of the list. Here is the problem:
First of all, I use an Action Bar + Tab Navigation from ActionBarSherlock :
QuestList extends FragmentMapActivity implements ActionBar.TabListener
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getTabs();
}
private void getTabs() {
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
ActionBar.Tab tab1 = getSupportActionBar().newTab();
tab1.setText("Geo");
tab1.setTabListener(this);
getSupportActionBar().addTab(tab1);
ActionBar.Tab tab2 = getSupportActionBar().newTab();
tab2.setText("No Geo");
tab2.setTabListener(this);
getSupportActionBar().addTab(tab2);
}
...
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (tab.getText().toString().compareTo("Geo") == 0) {
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, Map.getInstance()).commit();
}
else {
getSupportFragmentManager().beginTransaction().replace(android.R.id.content, List.newInstance(0)).commit();
}
}
I got some problems with the MapView, but I finally fixed all of them. Now the problem is that I want something like this:
Static LinearLayout + ListFragment
List extends ListFragment
...
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initializations();
getQuests();
if (gigHash.isEmpty()) {
TextView eList = new TextView(getActivity());
eList.setText(R.string.no_loc_gig_no_items);
eList.setWidth(Gravity.CENTER);
}
else {
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
mAdapter = new NoLocGigAdapter(getActivity(),R.layout.gig_noloc_row_land,gigList);
}
else {
mAdapter = new NoLocGigAdapter(getActivity(),R.layout.gig_noloc_row,gigList);
}
setListAdapter(mAdapter);
}
}
...
So I'm using my own adapter and I get all my items from a service.
The problem is, doing the transaction, I can only use a fragment, and I have to show the items dinamically with my own ArrayAdapter, so I don't know how to include a part of static code into that view...
Any suggestion? I think that handle with fragments it's a little weird!
PS: At first, I thought that I could call a fragment, and this, calling a static xml within the linearlayout and a ListFragment inside, but I think I can't do this, right?

Categories

Resources