DrawerLayout 和 ListView 表示动作 - 如何在 View 上调用动作方法?
DrawerLayout with ListView representing actions - how to call action methods on a View?
我已经准备好 a simple test project at GitHub 我的问题了。
它基于 the well-known Navigation Drawer Example by Google - 由右侧抽屉扩展,在 ListView 中执行与音乐相关的操作:
动作都定义在res/values/strings.xml:
<string-array name="music_actions">
<item>Play</item>
<item>Pause</item>
<item>Stop</item>
<item>Shuffle</item>
</string-array>
<integer-array name="music_icons">
<item>@drawable/ic_play_arrow_black_24dp</item>
<item>@drawable/ic_pause_black_24dp</item>
<item>@drawable/ic_stop_black_24dp</item>
<item>@drawable/ic_shuffle_black_24dp</item>
</integer-array>
这是从资源中读取它们的代码(效果很好):
public class MainActivity extends AppCompatActivity {
private String[] mActions;
private int[] mIcons;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mActions = getResources().getStringArray(R.array.music_actions);
TypedArray ta = getResources().obtainTypedArray(R.array.music_icons);
mIcons = new int[ta.length()];
for (int i = 0; i < mIcons.length; i++)
mIcons[i] = ta.getResourceId(i, R.drawable.ic_menu_black_24dp);
ta.recycle();
我的问题是:
来自ListView监听器中的onItemClick
方法——如何在Fragment中调用相应的方法?
我只看到丑陋的方式来做到这一点,就像这里有一个硬编码的 switch
语句:
mRightDrawer.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mActions) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
view.setCompoundDrawablesWithIntrinsicBounds(mIcons[position], 0, 0, 0);
return view;
}
});
mRightDrawer.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mActiveFragment instanceof GameFragment) {
GameFragment fragment = (GameFragment) mActiveFragment;
switch (position) {
case 0:
fragment.playMusic(); // and then this calls myView.playMusic();
break;
case 1:
fragment.pauseMusic(); // calls myView.pauseMusic();
break;
}
}
mDrawerLayout.closeDrawer(mRightDrawer);
}
});
这很丑陋,因为我必须跟踪我当前活动的片段,然后使用包装器方法最终调用我的自定义视图上的方法。
如何改善这种情况,我是否应该在我的自定义视图中使用 BroadcastReceiver?
我希望,我可以在我的资源中定义 icon + title + action 然后使用它们。
更新:
目前我正在使用 BroadcastReceiver
,但想知道它是否是一个合适的解决方案(或者可能过于重量级?)。
在我的片段中我调用:
@Override
public void onResume() {
super.onResume();
myView.register();
}
@Override
public void onPause() {
super.onPause();
myView.unregister();
}
在我的自定义视图中,我有:
public class MyView extends View {
public final static String ACTION_PLAY = "de.afarber.myapp.play";
public final static String ACTION_PAUSE = "de.afarber.myapp.pause";
public final static String ACTION_STOP = "de.afarber.myapp.stop";
public final static String ACTION_SHUFFLE = "de.afarber.myapp.shuffle";
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_PLAY.equals(action))
playMusic();
else if (ACTION_PAUSE.equals(action))
pauseMusic();
else if (ACTION_STOP.equals(action))
stopMusic();
else if (ACTION_SHUFFLE.equals(action))
shuffleMusic();
}
};
public void register() {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_PLAY);
filter.addAction(ACTION_PAUSE);
filter.addAction(ACTION_STOP);
filter.addAction(ACTION_SHUFFLE);
getContext().registerReceiver(mMessageReceiver, filter);
}
我更愿意为 Fragments 提供某种 "context menu" - 定义 title + icon + action - 然后可以由 actions-ListView 在右抽屉...
public void unregister() {
getContext().unregisterReceiver(mMessageReceiver);
}
广播适用于我基于 DrawerLayout 的应用程序,但为了更好的安全性和效率,我已切换到 LocalBroadcastManager -
MainActivity.java(右动作抽屉菜单发送广播):
mRightDrawer.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String action = mActions[position];
Intent intent = new Intent(action);
//intent.putExtra("message", "data");
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
mDrawerLayout.closeDrawer(mRightDrawer);
}
});
PlanetFragment.java(活动片段接收应用程序内部广播):
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mManager = LocalBroadcastManager.getInstance(getActivity());
mFilter = new IntentFilter();
mFilter.addAction(ACTION_PLAY);
mFilter.addAction(ACTION_PAUSE);
mFilter.addAction(ACTION_STOP);
mFilter.addAction(ACTION_SHUFFLE);
...
}
@Override
public void onResume() {
super.onResume();
mManager.registerReceiver(mMessageReceiver, mFilter);
}
@Override
public void onPause() {
super.onPause();
mManager.unregisterReceiver(mMessageReceiver);
}
我已经准备好 a simple test project at GitHub 我的问题了。
它基于 the well-known Navigation Drawer Example by Google - 由右侧抽屉扩展,在 ListView 中执行与音乐相关的操作:
动作都定义在res/values/strings.xml:
<string-array name="music_actions">
<item>Play</item>
<item>Pause</item>
<item>Stop</item>
<item>Shuffle</item>
</string-array>
<integer-array name="music_icons">
<item>@drawable/ic_play_arrow_black_24dp</item>
<item>@drawable/ic_pause_black_24dp</item>
<item>@drawable/ic_stop_black_24dp</item>
<item>@drawable/ic_shuffle_black_24dp</item>
</integer-array>
这是从资源中读取它们的代码(效果很好):
public class MainActivity extends AppCompatActivity {
private String[] mActions;
private int[] mIcons;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mActions = getResources().getStringArray(R.array.music_actions);
TypedArray ta = getResources().obtainTypedArray(R.array.music_icons);
mIcons = new int[ta.length()];
for (int i = 0; i < mIcons.length; i++)
mIcons[i] = ta.getResourceId(i, R.drawable.ic_menu_black_24dp);
ta.recycle();
我的问题是:
来自ListView监听器中的onItemClick
方法——如何在Fragment中调用相应的方法?
我只看到丑陋的方式来做到这一点,就像这里有一个硬编码的 switch
语句:
mRightDrawer.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mActions) {
@Override
public View getView(int position, View convertView, ViewGroup parent) {
TextView view = (TextView) super.getView(position, convertView, parent);
view.setCompoundDrawablesWithIntrinsicBounds(mIcons[position], 0, 0, 0);
return view;
}
});
mRightDrawer.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mActiveFragment instanceof GameFragment) {
GameFragment fragment = (GameFragment) mActiveFragment;
switch (position) {
case 0:
fragment.playMusic(); // and then this calls myView.playMusic();
break;
case 1:
fragment.pauseMusic(); // calls myView.pauseMusic();
break;
}
}
mDrawerLayout.closeDrawer(mRightDrawer);
}
});
这很丑陋,因为我必须跟踪我当前活动的片段,然后使用包装器方法最终调用我的自定义视图上的方法。
如何改善这种情况,我是否应该在我的自定义视图中使用 BroadcastReceiver?
我希望,我可以在我的资源中定义 icon + title + action 然后使用它们。
更新:
目前我正在使用 BroadcastReceiver
,但想知道它是否是一个合适的解决方案(或者可能过于重量级?)。
在我的片段中我调用:
@Override
public void onResume() {
super.onResume();
myView.register();
}
@Override
public void onPause() {
super.onPause();
myView.unregister();
}
在我的自定义视图中,我有:
public class MyView extends View {
public final static String ACTION_PLAY = "de.afarber.myapp.play";
public final static String ACTION_PAUSE = "de.afarber.myapp.pause";
public final static String ACTION_STOP = "de.afarber.myapp.stop";
public final static String ACTION_SHUFFLE = "de.afarber.myapp.shuffle";
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (ACTION_PLAY.equals(action))
playMusic();
else if (ACTION_PAUSE.equals(action))
pauseMusic();
else if (ACTION_STOP.equals(action))
stopMusic();
else if (ACTION_SHUFFLE.equals(action))
shuffleMusic();
}
};
public void register() {
IntentFilter filter = new IntentFilter();
filter.addAction(ACTION_PLAY);
filter.addAction(ACTION_PAUSE);
filter.addAction(ACTION_STOP);
filter.addAction(ACTION_SHUFFLE);
getContext().registerReceiver(mMessageReceiver, filter);
}
我更愿意为 Fragments 提供某种 "context menu" - 定义 title + icon + action - 然后可以由 actions-ListView 在右抽屉...
public void unregister() {
getContext().unregisterReceiver(mMessageReceiver);
}
广播适用于我基于 DrawerLayout 的应用程序,但为了更好的安全性和效率,我已切换到 LocalBroadcastManager -
MainActivity.java(右动作抽屉菜单发送广播):
mRightDrawer.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String action = mActions[position];
Intent intent = new Intent(action);
//intent.putExtra("message", "data");
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
mDrawerLayout.closeDrawer(mRightDrawer);
}
});
PlanetFragment.java(活动片段接收应用程序内部广播):
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
mManager = LocalBroadcastManager.getInstance(getActivity());
mFilter = new IntentFilter();
mFilter.addAction(ACTION_PLAY);
mFilter.addAction(ACTION_PAUSE);
mFilter.addAction(ACTION_STOP);
mFilter.addAction(ACTION_SHUFFLE);
...
}
@Override
public void onResume() {
super.onResume();
mManager.registerReceiver(mMessageReceiver, mFilter);
}
@Override
public void onPause() {
super.onPause();
mManager.unregisterReceiver(mMessageReceiver);
}