出于多种原因,这实际上可能对其他人有用。
如果您不得不使用只能通过其编程构造函数配置所需方式的视图(例如,您可以只包含 activity_main.xml 中的视图,但视图不是您需要的,除非您自己构建它,就像使用 OSMDroid 的 MapView 使用离线平铺地图一样)然后您就无法扩展该视图并实现包含 AttributeSet 的该视图的构造函数。AttributeSet 基本上是从该视图的 activity_main.xml xml 解析的结构。当您从 onCreate() 中调用 this.setContentView(R.layout.activity_main) 时,该构造函数将在 Activity 中自动调用。因此,任何自定义构造函数的内容都需要进入扩展视图的构造函数。
例如,我必须扩展 OSMDroid MapView,然后完全从超级实现我的离线地图瓦片源。注意你必须 super() 扩展构造函数的第一行,因为对象方法在继承的构造函数完成之前不可用,所以任何 super() 方法调用都必须是静态方法。
public class FieldMapView extends MapView {
public FieldMapView(Context context, AttributeSet attrs) throws Exception {
super(
context,
256,
new DefaultResourceProxyImpl(context),
FieldMapView.getOfflineMapProvider(context, MainActivity.mapTileArchiveFilename),
null,
attrs);
this.setUseDataConnection(false);
this.setBuiltInZoomControls(false);
this.setMultiTouchControls(true);
}
然后在我的activity_main.xml中我指向视图的扩展版本:(例如FieldMapView)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.test.FieldMapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="false"/>
所以现在我有我的扩展视图来处理任何程序化样式要求,我是如何让 SlidingDrawer 使用它的?我创建了一个以 0dip 高度视图作为句柄的 SlidingDrawer。然后我包含了一个 LinearLayout ,其中包含按钮,任何你想要的等等......
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/rootview"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<com.test.FieldMapView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/mapview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:clickable="false"/>
<SlidingDrawer
android:layout_width="wrap_content"
android:id="@+id/slidingDrawerActions"
android:content="@+id/action_content"
android:padding="10dip"
android:layout_height="75dip"
android:handle="@+id/handle2"
android:layout_alignBottom="@id/mapview"
android:orientation="vertical"
android:clickable="false">
<LinearLayout
android:layout_width="wrap_content"
android:id="@+id/action_content"
android:orientation="horizontal"
android:gravity="center"
android:padding="10dip"
android:background="#FF999999"
android:layout_height="wrap_content"
android:clickable="false">
<View
android:id="@id/handle2"
android:layout_width="0dip"
android:layout_height="0dip" />
<ImageButton
android:id="@+id/chatActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="2dip"
android:src="@drawable/ic_action_edit"
android:text="Chat">
</ImageButton>
</LinearLayout>
</SlidingDrawer>
</RelativeLayout>
在我的 MainActivity onCreate() 中,我只是找到 SlidingDrawer 视图并相应地分配任何侦听器。为了在 MapView 中长按 Marker 时打开抽屉(这现在变得更加特定于 OSMDroid),我自然有一个 OnItemGestureListener 来打开抽屉:
class NodeGestureListener implements OnItemGestureListener<NodeOverlayItem> {
@Override
public boolean onItemLongPress(int index, NodeOverlayItem node) {
if(slidingDrawerActions.isOpened() || slidingDrawerActions.isMoving()) {
return false;
}
slidingDrawerActions.animateOpen();
return false;
}
棘手的部分是我想通过单击 MapView 来关闭它(而不是通过触摸占用空间的关闭按钮),所以我必须分配 SlidingDrawer.OnDrawerOpenListener 和 OnDrawerCloseListener 类。他们只是简单地翻转一个布尔值,指示抽屉是打开还是关闭。然后,我为 MapView 设置了一个简单的 onClickListener,如果它基于 SlidingDrawer 侦听器设置的 isActionDrawerOpen 打开,则关闭抽屉。
public void onCreate(final Bundle savedInstanceState) {
...
this.mapView.setOnClickListener(new MapViewClickListener());
...
this.slidingDrawerActions = (SlidingDrawer)findViewById(R.id.slidingDrawerActions);
this.slidingDrawerActions.setOnDrawerOpenListener(new SlidingDrawerOpenListener());
this.slidingDrawerActions.setOnDrawerCloseListener(new SlidingDrawerCloseListener());
...
}
...
private boolean isActionDrawerOpen = false;
class SlidingDrawerOpenListener implements SlidingDrawer.OnDrawerOpenListener {
@Override
public void onDrawerOpened() {
isActionDrawerOpen = true;
}
}
class SlidingDrawerCloseListener implements SlidingDrawer.OnDrawerCloseListener {
@Override
public void onDrawerClosed() {
isActionDrawerOpen = false;
}
}
private boolean skippedMapViewClickListener = false;
class MapViewClickListener implements OnClickListener {
public void onClick(View view) {
if(isActionDrawerOpen) {
if(skippedMapViewClickListener) {
slidingDrawerActions.animateClose();
skippedMapViewClickListener = false;
} else {
skippedMapViewClickListener = true;
}
}
}
}
Note the skippedMapViewClickListener boolean. The problem I had was that the MapView OnClickListener would be called immediately after the SlidingDrawer listener when long pressing the Marker. Meaning the long press would be considered a MapView click, plus the long press itself would open the drawer before OnClickListener was called, so OnClickListener would always see the drawer as open, and would close it. What I did was effectively skip the 1st onClick this way, so the drawer would stay open until you clicked on the MapView. Seems to work great.
I hope this helps someone. There are like 4 problems I solved with this approach.