圆形的碎片在仿真器/手表上变成黑色的佩戴手表

我的Android Wear圆形手表上有一个黑色的屏幕(正方形)。 在Android Studio布局devise中,两种布局都显示正常。

项目testing:

https://bitbucket.org/powder366/motoblack

我使用下面的代码:

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); ... 

布局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> 

通过以下方式调用它:

 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; } ... 

这是从以下方面调用:

 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); } }); } ... 

有没有人看到这个,或者可以给我一个关于如何解决它的提示?

更新:

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> 

额外信息:

 public class ScrollableGridViewPager extends GridViewPager { ... 

在这里输入图像说明在这里输入图像说明

Solutions Collecting From Web of "圆形的碎片在仿真器/手表上变成黑色的佩戴手表"

问题并不是那么微不足道,所以我希望我已经描述得很清楚了。

理由#1:

这个问题的真正原因是在round设备上调用 onLayoutInflated(WatchViewStub)方法两次

rect_activity.xml被充满时,它被第一次调用:

 WearActivity$1.onLayoutInflated(WatchViewStub) line: 26 WatchViewStub.inflate() line: 133 WatchViewStub.onMeasure(int, int) line: 141 WatchViewStub(View).measure(int, int) line: 16648 

但在此之后,当调度onApplyWindowInsets(WindowInsets)时,第二次对此方法的调用是使用新增的round_activity.xml完成的:

 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 

我不知道这个问题是否只出现在模拟器上或真实的设备上,但我认为这应该被报告为可穿戴支持库( WatchViewStub组件中的WatchViewStub )。 这种行为没有logging在任何地方。

理由#2(#1的视觉效果):

上面的问题听起来像它应该做什么都没有…新的布局是膨胀的(所以旧的被删除),新的MenuAdapter将被创build(旧的将不再使用,所以谁在乎),这个新创buildMenuAdapter应该在新的GridViewPager设置为适配器。 一切都应该“重置”,所以它应该仍然可以正常工作(除了性能问题,因为布局是膨胀两次而不是一次)。

不完全…因为这是GridViewPager棘手的部分。

我不知道GridViewPagerFragmentGridPagerAdapter的确切源代码,但它似乎与ViewPagerFragmentPagerAdapter代码类似。 FragmentPagerAdapter将新创build的片段添加到FragmentManager ,该特殊tagViewPager id和给定子片段的位置组成:

 122 private static String More ...makeFragmentName(int viewId, int index) { 123 return "android:switcher:" + viewId + ":" + index; 124 } 

一旦片段被添加到FragmentManager (在当前的Activity ),将来可以通过这个tag名称来引用FragmentManager 。 真正的问题是由onLayoutInflated(WatchViewStub)方法被调用两次引起的。 第一个片段是在第一个MenuAdapter创build的, MenuAdapter添加到ID为R.id.pager (来自rect_activity.xml )的容器中。 然后round_activity.xml被膨胀,第二次调用onLayoutInflated(WatchViewStub)被调用 – 在新的MenuAdapter创build新的片段,并添加到新充气的GridViewPager ,使用相同的ID: R.id.pager 。 所以第一个片段和第二个片段想要在FragmentManager具有完全相同的tag

在标准的FragmentPagerAdapter如果带有给定tag片段已经存在于FragmentManager它将再次附加到它的上一个父级。 你可以在这里看到源代码:

 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 } 

所以可能的情况是类似的,第一个片段被重用,它被添加到第一个GridViewPager的ID为R.id.pager – 但这个家长不在那里,所以片段不能附着在任何地方(和显示)。 这是在一个Activity只能有一个给定id的ViewPager类父对象的实现。

对于另一个实验,我为这两个GridViewPagers设置了不同的ID。 一个带有rect_activity.xml id为R.id.pager1 ,另一个在round_activity.xml有`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); } } }); 

在第一遍中,只有gridViewPager1被find,只有它的适配器被设置。 类似于第二遍 – 仅用于gridViewPager2适配器已设置。 因为GridViewPagers有不同的ID,所以在片段tags没有碰撞,所以最终结果如预期。
但这只是为了certificate上述的理论:)

在这里输入图像说明

解:

WatchViewStub用于为rectround屏幕提供不同的布局。 我可以看到,你使用两个形状完全相同的布局。 你可以摆脱WatchViewStub ,只是这样做:

 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); } } 

这将使您的示例完美工作,而无需使用WatchViewStub

我有一个普通的ViewPager相同的问题。 MaciejCiemięga的实验性修补程序使用单独的id为不同的布局为我工作。 我向Google发布了一个错误报告,并链接到这个问题和答案。 我会评论答案,但我还没有足够的声誉留下评论。

OK,所以Android的生命周期已经消失,完全…截至目前创build并没有真正创造任何东西…它必须等待WatchViewStub.onMeasure(…)将被调用(这被称为长在onResume完成后),只有它膨胀正确的观点(或先是矩形视图,然后是圆形视图,这是双重调用的原因)…这可能会导致大量的问题。

我真的很喜欢一致性(不太确定为什么),所以我试图确保onViewInflated对于矩形和圆形设备只处理一次,不pipe它被调用多less次,这里是我的解决scheme:

 class MyActivity extends Activity { private class WearableUI_Helper implements OnLayoutInflatedListener { public Runnable processView = new Runnable() { @Override public void run() { processView(stub); postRender(); } }; private WatchViewStub stub; private Handler h = new Handler(Looper.getMainLooper()); public void postRender() { if (stub != null) render(); } @Override public void onLayoutInflated(WatchViewStub stub) { Log.w("Step", "onLayoutInflated"); this.stub = stub; h.removeCallbacks(processView); h.postAtFrontOfQueue(processView); } } private void render() { Log.w("Step", "Render"); // do here all the rendering you need... set images and text and all // and never call this method directly because it is only initialized // long after the onResume is called. } WearableUI_Helper uiHelper = new WearableUI_Helper(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.w("Step", "onCreate"); setContentView(R.layout.layout__app_notification_action); WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub); stub.setOnLayoutInflatedListener(uiHelper); } private void processView(WatchViewStub stub) { Log.w("Step", "processView"); // here you should find all you views and assign them to your activity fields. } @Override protected void onResume() { super.onResume(); uiHelper.postRender(); // if you call render() here it will crash!! } } 

我已经testing了这个圆形和矩形模拟器,并得到了预期的结果。 我没有在碎片中尝试这个,我也没有看到这个应该会失败的原因。

我希望我已经救了你一些挫折…