WearableListView 的 onLayout 错误

onLayout error with WearableListView

我正在尝试按照 this example 创建 WearableListView。我在 BoxInsetLayout 文件上遇到问题,因为在尝试呈现布局时出现 onLayout() 错误。这是堆栈跟踪:

java.lang.ClassCastException: com.android.layoutlib.bridge.android.support.Adapter$ViewHolder cannot be cast to android.support.wearable.view.WearableListView$ViewHolder
    at android.support.wearable.view.WearableListView.getChildViewHolder(WearableListView.java:521)
    at android.support.wearable.view.WearableListView$LayoutManager.notifyChildrenAboutProximity(WearableListView.java:762)
    at android.support.wearable.view.WearableListView$LayoutManager.performLayoutChildren(WearableListView.java:883)
    at android.support.wearable.view.WearableListView$LayoutManager.onLayoutChildren(WearableListView.java:854)
    at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2118)
    at android.support.v7.widget.RecyclerView.onLayout_Original(RecyclerView.java:2415)
    at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java)
    at android.view.View.layout(View.java:16630)
    at android.view.ViewGroup.layout(ViewGroup.java:5437)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
    at android.view.View.layout(View.java:16630)
    at android.view.ViewGroup.layout(ViewGroup.java:5437)
    at android.support.wearable.view.BoxInsetLayout.onLayout_Original(BoxInsetLayout.java:318)
    at android.support.wearable.view.BoxInsetLayout.onLayout(BoxInsetLayout.java)
    at android.view.View.layout(View.java:16630)
    at android.view.ViewGroup.layout(ViewGroup.java:5437)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:336)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:273)
    at android.view.View.layout(View.java:16630)
    at android.view.ViewGroup.layout(ViewGroup.java:5437)
    at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1079)
    at android.view.View.layout(View.java:16630)
    at android.view.ViewGroup.layout(ViewGroup.java:5437)
    at com.android.layoutlib.bridge.impl.RenderSessionImpl.render(RenderSessionImpl.java:376)
    at com.android.layoutlib.bridge.Bridge.createSession(Bridge.java:428)
    at com.android.ide.common.rendering.LayoutLibrary.createSession(LayoutLibrary.java:350)
    at com.android.tools.idea.rendering.RenderTask.compute(RenderTask.java:510)
    at com.android.tools.idea.rendering.RenderTask.compute(RenderTask.java:498)
    at com.intellij.openapi.application.impl.ApplicationImpl.runReadAction(ApplicationImpl.java:888)
    at com.android.tools.idea.rendering.RenderTask.createRenderSession(RenderTask.java:498)
    at com.android.tools.idea.rendering.RenderTask.access0(RenderTask.java:72)
    at com.android.tools.idea.rendering.RenderTask.call(RenderTask.java:610)
    at com.android.tools.idea.rendering.RenderTask.call(RenderTask.java:607)
    at com.android.tools.idea.rendering.RenderService.runRenderAction(RenderService.java:362)
    at com.android.tools.idea.rendering.RenderTask.render(RenderTask.java:607)
    at com.android.tools.idea.rendering.RenderTask.render(RenderTask.java:629)
    at com.intellij.android.designer.designSurface.AndroidDesignerEditorPanel.run(AndroidDesignerEditorPanel.java:480)
    at com.intellij.util.ui.update.MergingUpdateQueue.execute(MergingUpdateQueue.java:320)
    at com.intellij.util.ui.update.MergingUpdateQueue.execute(MergingUpdateQueue.java:310)
    at com.intellij.util.ui.update.MergingUpdateQueue.run(MergingUpdateQueue.java:254)
    at com.intellij.util.ui.update.MergingUpdateQueue.flush(MergingUpdateQueue.java:269)
    at com.intellij.util.ui.update.MergingUpdateQueue.flush(MergingUpdateQueue.java:227)
    at com.intellij.util.ui.update.MergingUpdateQueue.run(MergingUpdateQueue.java:217)
    at com.intellij.util.concurrency.QueueProcessor.runSafely(QueueProcessor.java:238)
    at com.intellij.util.Alarm$Request.run(Alarm.java:351)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

这是我的 xml 文件,其中包含一个 WearableListView:

<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/grey"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        app:layout_box="left|bottom|right">

        <android.support.wearable.view.WearableListView
            android:id="@+id/wearable_list"
            android:layout_height="match_parent"
            android:layout_width="match_parent">
        </android.support.wearable.view.WearableListView>

    </FrameLayout>
</android.support.wearable.view.BoxInsetLayout>

此外,在我的 DevicesListActivity.java 上,我实现了接口 WearableListView.ClickListener,但是当我尝试通过 id 获取布局时,我得到一个空的 WearableListView 对象(我认为发生这种情况是因为那个错误我之前说过,但我不确定):

public class DevicesListActivity extends Activity implements WearableListView.ClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_devices_list);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {

            }
        });

        WearableListView listView = (WearableListView) findViewById(R.id.wearable_list);
        if (listView == null) {
            Log.d("DevicesListActivity", "ListView is null");
        } else {
            listView.setAdapter(new WearableListViewAdapter("Prueba", "00:00", this));
            listView.setClickListener(this);
        }

    }

    @Override
    public void onClick(WearableListView.ViewHolder viewHolder) {
        Toast.makeText(this, "Clic realizado", Toast.LENGTH_LONG);
    }

    @Override
    public void onTopEmptyRegionClick() {

    }
}

有什么帮助吗?

提前致谢。

我不认为你的第二个错误来自 onlayout() 错误问题,因为我有相同的错误问题,而且我的可穿戴列表视图工作完美。

它可能来自 WatchViewStub,因为我在没有它的情况下尝试成功,让 xml 包含您的 R.id.watch_view_stub。

会很有用

希望对您有所帮助。

我找到了解决 onLayout() 问题的方法,但我不知道这是否是件好事:https://code.google.com/p/android/issues/detail?id=186918

创建这个 class :

public class WearableListViewFixEditor extends WearableListView {
    public WearableListViewFixEditor(Context context) {
        super(context);
    }

    public WearableListViewFixEditor(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public WearableListViewFixEditor(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    /**
     * Override this method to implement the choice for the intellij android designer
     */
    @Override
    public ViewHolder getChildViewHolder(View child) {
        if (!isInEditMode()) {
            return super.getChildViewHolder(child);
        } else {
            /**
             * Override this with an empty body to avoid an error in intellij android designer
             */
            return new WearableListView.ViewHolder(new View(getContext())) {
                @Override
                protected void onCenterProximity(boolean isCentralItem, boolean animate) {

                }
            };
        }
    }
}

之后修改您的 xml 文件如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/grey"
    android:layout_height="match_parent"
    android:layout_width="match_parent">

    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        app:layout_box="left|bottom|right">

        <your.package.path.WearableListViewFixEditor
            android:id="@+id/wearable_list"
            android:layout_height="match_parent"
            android:layout_width="match_parent">
        </your.package.path.WearableListViewFixEditor>

    </FrameLayout>
</android.support.wearable.view.BoxInsetLayout>

我设法用 this example 之后的空 WearableListView 对象解决了我的问题,但我忘记了 post 之后的答案。我所做的是将我的 WearableListView 布局移动到文件 round_activity_devices_list.xml(因为我的设备是圆形的,但我认为如果我添加我的布局,这也应该适用于矩形设备至 rect_activity_devices_list.xml):

<?xml version="1.0" encoding="utf-8"?>
<android.support.wearable.view.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/grey"
    tools:context=".DevicesListActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="25dp"
        android:textSize="17dp"
        android:text="Select a device"
        android:id="@+id/listTitle"/>

    <android.support.wearable.view.WearableListView
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/wearable_list"
        app:layout_box="all" />

</android.support.wearable.view.BoxInsetLayout>

这些是 java 类 我现在有:

WearableListViewAdapter.java

public class WearableListViewAdapter extends WearableListView.Adapter {

    private final String TAG = "WearableListViewAdapter";

    private String[] deviceName;
    private String deviceAddress = "Not being used.";
    private final Context context;
    private final LayoutInflater inflater;

    public WearableListViewAdapter(Context context, String[] devices) {
//        Log.d(TAG, "WearableListViewAdapter constructor accessed.");
        this.deviceName = devices;
//        this.deviceAddress = device;
        this.context = context;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public void onBindViewHolder(WearableListView.ViewHolder holder, int position) {
        ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
        TextView deviceNameText = itemViewHolder.deviceNameText;
        TextView deviceAddressText = itemViewHolder.deviceAddressText;
        deviceNameText.setText(deviceName[position]);
        deviceAddressText.setText(deviceAddress);

        holder.itemView.setTag(position);
        Log.d(TAG, "onBindViewHolder");
    }

    @Override
    public int getItemCount() {
        return deviceName.length;
    }

    @Override
    public WearableListView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new ItemViewHolder(inflater.inflate(R.layout.list_item, null));
    }

    public static class ItemViewHolder extends WearableListView.ViewHolder {
        private TextView deviceNameText;
        private TextView deviceAddressText;

        public ItemViewHolder (View itemView) {
            super(itemView);
            deviceNameText = (TextView) itemView.findViewById(R.id.deviceNameText);
            deviceAddressText = (TextView) itemView.findViewById(R.id.deviceAddressText);
        }

    }

}

WearableListItemLayout.java

public class WearableListItemLayout extends LinearLayout
        implements WearableListView.OnCenterProximityListener {

    private ImageView deviceImg;
    private TextView deviceName;
    private TextView deviceAddress;

    public WearableListItemLayout(Context context) {
        this(context, null);
    }

    public WearableListItemLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }


    public WearableListItemLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    // Get references to the icon and text in the item layout definition
    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        // These are defined in the layout file for list items
        // (see next section)
        deviceImg = (ImageView) findViewById(R.id.deviceImg);
        deviceName = (TextView) findViewById(R.id.deviceNameText);
        deviceAddress = (TextView) findViewById(R.id.deviceAddressText);
    }

    @Override
    public void onCenterPosition(boolean animate) {
        deviceImg.animate().scaleX(1f).scaleY(1f).alpha(1);
        deviceName.animate().scaleX(1f).scaleY(1f).alpha(1);
        deviceAddress.animate().scaleX(1f).scaleY(1f).alpha(1);
    }

    @Override
    public void onNonCenterPosition(boolean animate) {
        deviceImg.animate().scaleX(0.8f).scaleY(0.8f).alpha(0.6f);
        deviceName.animate().scaleX(0.8f).scaleY(0.8f).alpha(0.6f);
        deviceAddress.animate().scaleX(1f).scaleY(1f).alpha(1);
    }
}

DevicesListActivity.java

public class DevicesListActivity extends Activity implements WearableListView.ClickListener {

    private final String TAG = "DevicesListActivity";
    private WearableListView listView;
    private TextView listTitleText;
    private Context context = this;
    private Messenger mainActivityMessenger;
    private String[] devicesName = { "SensorTag", "BH" };

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_devices_list);
        final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
        stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
            @Override
            public void onLayoutInflated(WatchViewStub stub) {
                listView = (WearableListView) stub.findViewById(R.id.wearable_list);
                listTitleText = (TextView) stub.findViewById(R.id.listTitle);
                Log.d(TAG, "listView created.");
                if (listView == null) {
                    Log.d("DevicesListActivity", "ListView is null");
                } else {
                    listView.setAdapter(new WearableListViewAdapter(context, devicesName));
                    listView.setClickListener((WearableListView.ClickListener) context);
                    mainActivityMessenger = getIntent()
                            .getParcelableExtra("mainactivitymessenger");
                }
            }
        });
    }
...

如您所见,父布局仍然是BoxInsetLayout,但是我删除了FrameLayout,因为我暂时不需要它。但是,onLayout() 的错误仍然存​​在,但实际上我不需要修复它,因为我的应用程序工作正常,而且它似乎只是一个渲染问题。无论如何,如果您认为此修复不正确或者您有解决 onLayout() 错误的方法,请告诉我。

谢谢。