Im getting a black screen on my Android Wear round watch (on square it works). Both layouts show up OK in the Android Studio layout design.
Project to test:
https://bitbucket.org/powder366/motoblack
Im using the following code:
public class MyFragment extends Fragment {
...
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View contentView = inflater.inflate(R.layout.my_fragment, container, false);
...
Layout my_fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/my_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:keepScreenOn="true"
tools:context=".WearFragment">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/text1"
android:text="Hi1"
android:layout_gravity="center_horizontal"
style="@style/UnitsStyle"/>
<TextView
android:id="@+id/text2"
android:text="Hi2"
android:layout_gravity="center_horizontal"
style="@style/BaseStyle"/>
<TextView
android:id="@+id/text3"
android:text="Hi3"
android:layout_gravity="center_horizontal"
style="@style/UnitsStyle"/>
</LinearLayout>
</LinearLayout>
Calling it from:
public class MenuAdapter extends FragmentGridPagerAdapter
...
public MenuAdapter(Context context, FragmentManager fm, Handler handler) {
super(fm);
mContext = context;
mHandler = handler;
myFragment = new MyFragment();
}
...
@Override
public Fragment getFragment(int rowNum, int colNum) {
Log.d(TAG, String.format("getFragment(%d, %d)", rowNum, colNum));
if(colNum == 0) {
return myFragment;
}
if(colNum == 1) {
final SecondFragment switchAction = SecondFragment.newInstance(mHandler);
return switchAction;
}
return null;
}
...
Which is called from:
public class WearActivity extends Activity {
...
private GridViewPager gridViewPager;
private MenuAdapter menuAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "Create(Wear)");
super.onCreate(savedInstanceState);
setContentView(R.layout.wear_activity);
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
menuAdapter = new MenuAdapter(WearActivity.this, getFragmentManager(), mHandler);
gridViewPager = (GridViewPager) findViewById(R.id.pager);
gridViewPager.setAdapter(menuAdapter);
}
});
}
...
Have anyone seen this or can give me a hint on how to solve it?
Update:
wear_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.WatchViewStub
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/watch_view_stub"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:rectLayout="@layout/rect_activity"
app:roundLayout="@layout/round_activity"
tools:context=".WearActivity"
tools:deviceIds="wear">
</android.support.wearable.view.WatchViewStub>
rect_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<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:layout_gravity="center"
tools:deviceIds="wear_square">
<com.xyz.my.ScrollableGridViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"/>
</FrameLayout>
round_activity.xml
<?xml version="1.0" encoding="utf-8"?>
<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:layout_gravity="center"
tools:deviceIds="wear_round">
<com.xyz.my.ScrollableGridViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:keepScreenOn="true"/>
</FrameLayout>
Extra info:
public class ScrollableGridViewPager extends GridViewPager {
...
The problem is not so trivial, so I hope that I've described it pretty clearly.
The real reason of this issue is that the onLayoutInflated(WatchViewStub)
method is called twice on the round device.
It is invoked for the first time when the rect_activity.xml
is inflated:
WearActivity$1.onLayoutInflated(WatchViewStub) line: 26
WatchViewStub.inflate() line: 133
WatchViewStub.onMeasure(int, int) line: 141
WatchViewStub(View).measure(int, int) line: 16648
But right after that the second call to this method is done with newly inflated round_activity.xml
when onApplyWindowInsets(WindowInsets)
is dispatched:
WearActivity$1.onLayoutInflated(WatchViewStub) line: 26
WatchViewStub.inflate() line: 133
WatchViewStub.onApplyWindowInsets(WindowInsets) line: 112
WatchViewStub(View).dispatchApplyWindowInsets(WindowInsets) line: 6051
WatchViewStub(ViewGroup).dispatchApplyWindowInsets(WindowInsets) line: 5435
SwipeDismissLayout(ViewGroup).dispatchApplyWindowInsets(WindowInsets) line: 5439
PhoneWindow$DecorView(ViewGroup).dispatchApplyWindowInsets(WindowInsets) line: 5439
ViewRootImpl.dispatchApplyInsets(View) line: 1170
I don't know whether this issue occurs only on emulator or on real device also, but I think this should be reported as a bug in wearable-support-lib (in WatchViewStub
component to be precise). Such behavior is not documented anywhere.
The issue above sounds like it should do pretty nothing... New layout is inflated (so the old one is removed), new MenuAdapter
will be created (the old one will not be used any more so who cares), and this newly created MenuAdapter
should be set as an adapter in new GridViewPager
. Everything should "reset" so it should still works fine (apart that the performance issue due to the fact that layout is inflated two times instead of just once).
Not exactly... because here is the tricky part with GridViewPager
.
I don't know the exact source codes of GridViewPager
and FragmentGridPagerAdapter
but it seems to be similar to ViewPager
and FragmentPagerAdapter
code. FragmentPagerAdapter
adds newly created fragment to the FragmentManager
with special tag
that is composed with id
of the ViewPager
and position of given child fragment:
122 private static String More ...makeFragmentName(int viewId, int index) {
123 return "android:switcher:" + viewId + ":" + index;
124 }
Once a fragment is added to the FragmentManager
(in current Activity
) it can be referenced by this tag
name in future.
The real problem is caused by the fact that onLayoutInflated(WatchViewStub)
method is called twice. The first fragment is created in first MenuAdapter
and added to the container with id R.id.pager
(from the rect_activity.xml
). Then round_activity.xml
is inflated and second call to onLayoutInflated(WatchViewStub)
is invoked - new fragment is created in new MenuAdapter
and added to newly inflated GridViewPager
with the same id: R.id.pager
. So the first fragment and second one want to have exactly the same tag
in FragmentManager
.
In standard FragmentPagerAdapter
if fragment with given tag
is already present in FragmentManager
it is attached to it's previous parent again. You can see source codes here:
50 @Override
51 public Object More ...instantiateItem(View container, int position) {
52 if (mCurTransaction == null) {
53 mCurTransaction = mFragmentManager.beginTransaction();
54 }
55
56 // Do we already have this fragment?
57 String name = makeFragmentName(container.getId(), position);
58 Fragment fragment = mFragmentManager.findFragmentByTag(name);
59 if (fragment != null) {
60 if (DEBUG) Log.v(TAG, "Attaching item #" + position + ": f=" + fragment);
61 mCurTransaction.attach(fragment);
62 } else {
63 fragment = getItem(position);
64 if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
65 mCurTransaction.add(container.getId(), fragment,
66 makeFragmentName(container.getId(), position));
67 }
68 if (fragment != mCurrentPrimaryItem) {
69 fragment.setMenuVisibility(false);
70 }
71
72 return fragment;
73 }
So probably the situation is similar and the first fragment is reused, which was added to the first GridViewPager
with id R.id.pager
- but this parent is not there any more so the fragment cannot be attached anywhere (and be displayed). This is the implementation thing that there can be only one ViewPager
-like parent with given id in one Activity
.
For one more experiment I've set different ids for both GridViewPagers
. One with rect_activity.xml
has id R.id.pager1
and another one in round_activity.xml
has `R.id.pager2'.
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
menuAdapter = new MenuAdapter(WearActivity.this, getFragmentManager());
GridViewPager gridViewPager1 = (GridViewPager) findViewById(R.id.pager1);
if(gridViewPager1!=null) { // rect_activity
gridViewPager1.setAdapter(menuAdapter);
}
GridViewPager gridViewPager2 = (GridViewPager) findViewById(R.id.pager2);
if(gridViewPager2!=null) { // round_activity
gridViewPager2.setAdapter(menuAdapter);
}
}
});
In first pass only gridViewPager1
is found and only its adapter is set. Similar in the second pass - only adapter for gridViewPager2
is set.
Because GridViewPagers
have different ids there is no collision in fragment tags
, so the end result is as expected.
But this is only to prove the theory described above:)
WatchViewStub
is used to provide different layouts for rect
and round
screens. I can see that you use exactly the same layouts for both shapes. You can get rid of the WatchViewStub
and just do like this:
public class WearActivity extends Activity {
private GridViewPager gridViewPager;
private MenuAdapter menuAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.rect_activity);
menuAdapter = new MenuAdapter(WearActivity.this, getFragmentManager());
gridViewPager = (GridViewPager) findViewById(R.id.pager);
gridViewPager.setAdapter(menuAdapter);
}
}
this will make your example work perfectly without the use of WatchViewStub
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With