I am implementing a ViewPagerIndicator that works as expected in vertical orientation. When I test in horizontal orientation, either by switching at run-time (physically turning the phone) or when the app starts in horizontal mode (phone was horizontal before the app started) I get a blank white space where the ViewPagerIndicator should be. I have a layout/main.xml and layout-land/main.xml with a few buttons that are displaying as expected (stacked vertically in vertical mode, as a grid in horizontal mode). When I press the "edit" button, I launch the edit activity:
public class ATEditActivity extends Activity {
static final int OFFSCREEN_PAGE_LIMIT = 5;
ViewPager pager = null;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.edit);
int iPage = 2; // set the page based on caller's intentions
Bundle bndlExtras = getIntent().getExtras();
if (bndlExtras != null) {
iPage = bndlExtras.getInt("PAGE");
}
ATViewPagerAdapter adapter = new ATViewPagerAdapter(this);
pager = (ViewPager) findViewById(R.id.awesomepager);
pager.setOffscreenPageLimit(OFFSCREEN_PAGE_LIMIT);
pager.setAdapter(adapter);
TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.TitlePageIndicator);
indicator.setViewPager(pager);
indicator.setCurrentItem(iPage);
}
}
edit.xml:
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#FFFFFF"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginLeft="0dp"
android:layout_marginRight="0dp">
<EditText
android:id="#+id/etName"
android:inputType="textAutoComplete"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/edit_text"
android:layout_margin="4dp"
android:hint="#string/name" />
<com.viewpagerindicator.TitlePageIndicator
android:id="#+id/TitlePageIndicator"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
style="#style/Widget.MyTitlepageIndicator" />
<android.support.v4.view.ViewPager
android:id="#+id/awesomepager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Any thoughts on why the ViewPagerIndicator would not display in horizontal mode? Is more information needed?
You probably need to override onConfigurationChanged() see reference as your app needs to retrieve the resources again and draw everything.
After three or four days of trying everything I could imagine, I finally decided there must have been something really messed up in my project so I created a new project that only had the minimal amount of code necessary to get the ViewPagerIndicator working. The new project is now working with both orientations "out-of-the-box". I have no idea what caused my old project to go haywire with only the horizontal orientation but I will get the new project on track and up to speed in short order now that it is working as expected.
Related
I came across a frustrating glitch in my app.
I have a FragmentTabHost in my Activity.
This tab host is hosting 5 fragments, each of these getting rendered correctly.
Now, the trick is that when I rotate the screen, sometimes the fragment is not being rendered.
The tab widget is still being rendered and is clickable, but the view corresponding to that fragment is not displayed.
Here is an example below.
Note that I put a red background on my realtabcontent FrameLayout:
Everything is working fine
Rotating the screen: randomly the screen becomes partially empty
Switching to other tabs executes the OnCreate/OnCreateView of the relevant fragment, but nothing is displayed
Switching back to the first screen does not even show the button of the Fragment anymore
Note that:
- the first time I rotate, there is still a button showing up (it belongs to the fragment that should be rendered).
- once the bug is triggered, I can switch to other tabs. The other tabs are loaded correctly (notice the action bar changing), but nothing is rendered in the FrameLayout
- If I go back to the first tab, the button that was still being displayed is not even rendered anymore
Notice also a detail: the reddot of the tab disappears, even though the code that set the reddot is correctly executed. It's as if the tab host is not responding to the drawing requests.
Here is the code in charge of displaying the reddot:
public void showReddotForTab(int tabIndx, Boolean showFlag){
try {
if (tabIndx < mTabHost.getTabWidget().getTabCount()) {
RelativeLayout tabRootView = (RelativeLayout) mTabHost.getTabWidget().getChildTabViewAt(tabIndx);
WhovaNotificationBadge notifBadge = tabRootView.findViewById(R.id.notif_badge);
if (showFlag) {
notifBadge.setVisibility(View.VISIBLE);
notifBadge.setLabel(null);
}
else {
notifBadge.setVisibility(View.GONE);
}
}
}
catch(Exception e){
e.printStackTrace();
}
}
Here is the layout of the activity containing the FragmentTabHost
<?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">
<androidx.fragment.app.FragmentTabHost
android:id="#android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<FrameLayout
android:id="#android:id/tabcontent"
android:layout_width="0dp"
android:layout_height="0dp" />
<FrameLayout
android:id="#+id/realtabcontent"
android:background="#color/red"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="2px"
android:background="#color/separate_gray"/>
<TabWidget
android:id="#android:id/tabs"
android:orientation="horizontal"
android:background="#color/new_whova_tab_bg"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="6dp"
android:layout_weight="0"/>
</LinearLayout>
</androidx.fragment.app.FragmentTabHost>
<TextView
android:id="#+id/text_network"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="40dp"
android:layout_alignParentTop="true"
android:background="#color/orange"
android:text="#string/network_off_indicator_msg"
android:visibility="gone"
android:textColor="#color/white"
android:gravity="center"/>
</RelativeLayout>
Here is the code setting up the tabhost
mTabHost = findViewById(android.R.id.tabhost);
mTabHost.setOnTabChangedListener(tabId -> {
...
})
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
mTabHost.getTabWidget().setDividerDrawable(null);
//! simplified below, but the code is simlar to it
//! spec is basically TabHost.TabSpec spec = mTabHost.newTabSpec("someID").setImageResource(...).setIndicator(...).setContent(tag -> findViewById(R.id.realtabcontent));
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
mTabHost.addTab(spec, FragmentXXXX.class, fragmentBundleXXXX);
Found how to solve it, but I have no idea what is the reason.
My actionbar is a toolbar containing an AppCompatSpinner.
The spinner is only used for the tab showed in the pictures and has setOnItemSelectedListener set.
When rotating the screen, the listener is called (not sure why, because the initial selection is done before setting the listener and not selection occurred afterwhile.
Either way, sometimes when this listener is called, the data loaded from DB is still processing in the background. The weird thing being that once it is done, we refresh the UI but it stays blank and the other tabs are affected.
I suspect that there is something wrong in the way the listener is called and it blocks the processing of other UI events, but not sure.
When I set the listener within a post, the problem disappear
spinner.post(() -> {
spinner.setOnItemSelectedListener(...)
})
Having a layout which has horizontally side by side A and B parts when in landscape mode. Let's say A take 1/3 of the screen and B take other 2/3.
When rotate what is wanted is that the A keeps its original width but is changed to overlay on top of the B and B changes to have width of full screen underneath the A.
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<FrameLayout
android:id="#+id/left_part"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.3" />
<FrameLayout
android:id="#+id/right_part"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.7" />
</LinearLayout>
The part A, and B are holders for different fragments, which has it's adapetr with cursor etc. and could have a few stacked up in backstack. So when rotate would prefer to not re start the activity so that the context is maintained, but just some how to rearrange the layout dynamically.
Not sure if it is doable. Any suggestion?
Thanks!
Handling the Configuration Change Yourself
Your activity should be using saved instance state to persist the data from the old layout and restore it into the new layout.
It's theoretically possible to define a single layout that has sub groups from the different orientations, populate them both, then show / hide those as the rotation occurs using the method mentioned above for handling it yourself, but you are better off sticking Android's stock mechanism to handle this.
seems it should work with dynamically changing the layout, tried following shows the expected behavior.
Please comment if seeing any issue with this approach or having better solution. Thanks!
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<FrameLayout
android:id="#+id/right_pane"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentRight="true"
android:layout_alignParentLeft="false"
android:layout_toRightOf="#+id/left_part" />
<FrameLayout
android:id="#+id/left_part"
android:layout_width="#dimen/left_part_width"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"/>
</RelativeLayout>
void doChangeMode() {
RelativeLayout.LayoutParams leftLp = (RelativeLayout.LayoutParams) leftPart.getLayoutParams();
RelativeLayout.LayoutParams rightLp = (RelativeLayout.LayoutParams) rightPart.getLayoutParams();
if (mode == mode_side_by_side) {
mode = mode_overlap;
rightLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
} else {
mode = mode_side_by_side;
rightLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT, 0);
//api 17 above
//rightLp.removeRule(RelativeLayout.ALIGN_PARENT_LEFT);
}
Handler delayHandler = new Handler();
delayHandler.post(new Runnable() {
#Override
public void run() {
rootView.requestLayout();
}
});
}
I'm trying to remove a progress indicator after loading my data in a fragment involving a ListView. Here is my completion handler:
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
dataSource = (ArrayList<Map<String, Object>>) task.getResult();
PostAdapter adapter = new PostAdapter(getActivity(), dataSource);
ListView list = (ListView) rootView.findViewById(R.id.listView);
list.setAdapter(adapter);
View indicator = getActivity().findViewById(R.id.indicator);
RelativeLayout layout = (RelativeLayout) getActivity().findViewById(R.id.layout);
layout.removeView(indicator);
}
});
The last 3 lines of code is the relevant part. Everything is called correctly, nothing is null etc. in debug everything works perfectly. The adapter also works correctly, populating my list, but the indicator is still on the screen. I've also tried setting it's visibility to GONE or HIDDEN but they also don't seem to hide it either. I've seen Android - Can't hide progress bar but it's answers involve setEmptyView() which I'm not using anyway. I am using the same fragment (of course, a different instance) in another tab, and it works correctly.
Here is my layout file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="0dp"
android:background="#ffffffff"
android:id="#+id/layout">
<ListView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/listView"
android:layout_margin="0dp"
android:padding="0dp" />
<ProgressBar
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/indicator"
android:indeterminate="true"
android:layout_centerInParent="true" />
</RelativeLayout>
What am I doing wrong?
I've found out the problem (thanks to all the commenters). I was calling activity's methods to find and remove the view. The problem is that, I have multiple instances of the same fragment in the same activity, under different tabs. I've used my fragment's root view to find and remove the indicator, instead of the activity, and it worked.
Change the codes of last three lines in onClick(...) as:
View indicator = (ProgressBar)rootView.findViewById(R.id.indicator);
and use:
indicator.setVisibility(ProgressBar.GONE);
or codes:
RelativeLayout layout = (RelativeLayout) rootView.findViewById(R.id.layout);
layout.removeView(indicator);
I am just learning about android development, and I am having some issues with getting this to work.
I have an activity that uses a relatively layout. I need it to have 2 buttons along the bottom, and then right above the bottoms, I want my custom view to take up the rest of the space.
viewer.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/viewerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<sketchViewer.AnimationPanelView
android:id="#+id/animationView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_above="#+id/homeFromViewerButton"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" />
<Button
android:id="#+id/homeFromViewerButton"
android:layout_width="640dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:text="Replay" />
<Button
android:id="#+id/replayButton"
android:layout_width="640dp"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:text="Home" />
</RelativeLayout>
The issue I am having is I that when I run my program, I need to pass a number of parameters into my custom view constructor so that my custom view decides what it should draw. So after creating an instance of my custom view (AnimationPanelView), I am not sure how I set this object into the space I provided for the view.
This is my activity class:
Viewer.java
public class Viewer extends Activity {
AnimationPanelView animationPanelView;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewer);
animationPanelView = new AnimationPanelView(this, true /*, more parameters here */);
animationPanelView.setBackgroundColor(Color.GREEN);
RelativeLayout v = (RelativeLayout) findViewById(R.id.viewerLayout);
v.addView(animationPanelView);
}
Right now, with my v.addView command, the view takes up the entire page, covering up the buttons at the bottom. Can anyone shed some light on this? I feel like I am close, but I've been playing around with it for a while, and I just seem stuck.
Check out the implementing a custom view section here. You need to override onLayout and onMeasure so you can tell your container how big you are.
You are adding another custom view to your layout instead you should use
animationPanelView = (AnimationPanelView) findViewById(R.id.animationView);
animationPanelView.setBackgroundColor(Color.GREEN);
The goal is to display two Fragments (Frag A, Frag B) on the screen in a different layout depending on the screen orientation (portrait or landscape).
When the device is in Portrait mode, only one of Frag A and Frag B is displayed at a time using swiping to switch between UIs. ViewPager works perfectly for this.
When the device is in Landscape mode, Frag A is displayed on the left of the screen and Frag B is displayed on the right. A simple LinearLayout works perfectly for this.
The problem arises when trying to get both working in the same application. It seems like the ViewPager class must be referred to in the Java source code (in the onCreate method). This means that a UI element is defined in both the XML and the source code... which seems like a problem when the device is in Landscape mode and there should not be ViewPager object but one is referred to in the source code. Am I approaching this correctly? If one layout is using ViewPager, does every other layout need a ViewPager?
layout/activity_mymain.xml
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MyMainActivity" >
<!--
This title strip will display the currently visible page title, as well as the page
titles for adjacent pages.
-->
<android.support.v4.view.PagerTitleStrip
android:id="#+id/pager_title_strip"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="top"
android:background="#33b5e5"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textColor="#fff" />
</android.support.v4.view.ViewPager>
layout-land/activity_mymain.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="com.example.ActionFragment"
android:id="#+id/action_fragment"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="match_parent" />
<fragment android:name="com.example.SummaryFragment"
android:id="#+id/summary_fragment"
android:layout_weight="2"
android:layout_width="0dp"
android:layout_height="match_parent" />
</LinearLayout>
From MyMainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_topmain);
// Create the adapter that will return a fragment for each of the three
// primary sections of the app.
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
}
ViewPager is used to instantiate View objects at runtime, so it needs to be defined in the code.
What I was trying to do is very similar to the different tablet/handset layouts described in the Fragments Guide, where they recommend using separate Activities.
(If anyone knows of a way to handle swiping entirely in XML, that would be a much better solution)